]> Pileus Git - ~andy/gtk/blob - gtk/gtktextview.c
fix some shell typos
[~andy/gtk] / gtk / gtktextview.c
1 /* GTK - The GIMP Toolkit
2  * gtktextview.c Copyright (C) 2000 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include <string.h>
28
29 #include "gtkbindings.h"
30 #include "gtkdnd.h"
31 #include "gtkintl.h"
32 #include "gtkmain.h"
33 #include "gtkmenu.h"
34 #include "gtkmenuitem.h"
35 #include "gtkseparatormenuitem.h"
36 #include "gtksignal.h"
37 #include "gtktextdisplay.h"
38 #include "gtktextview.h"
39 #include "gtkimmulticontext.h"
40 #include "gdk/gdkkeysyms.h"
41 #include <string.h>
42
43 /* How scrolling, validation, exposes, etc. work.
44  *
45  * The expose_event handler has the invariant that the onscreen lines
46  * have been validated.
47  *
48  * There are two ways that onscreen lines can become invalid. The first
49  * is to change which lines are onscreen. This happens when the value
50  * of a scroll adjustment changes. So the code path begins in
51  * gtk_text_view_value_changed() and goes like this:
52  *   - gdk_window_scroll() to reflect the new adjustment value
53  *   - validate the lines that were moved onscreen
54  *   - gdk_window_process_updates() to handle the exposes immediately
55  *
56  * The second way is that you get the "invalidated" signal from the layout,
57  * indicating that lines have become invalid. This code path begins in
58  * invalidated_handler() and goes like this:
59  *   - install high-priority idle which does the rest of the steps
60  *   - if a scroll is pending from scroll_to_mark(), do the scroll,
61  *     jumping to the gtk_text_view_value_changed() code path
62  *   - otherwise, validate the onscreen lines
63  *   - DO NOT process updates
64  *
65  * In both cases, validating the onscreen lines can trigger a scroll
66  * due to maintaining the first_para on the top of the screen.
67  * If validation triggers a scroll, we jump to the top of the code path
68  * for value_changed, and bail out of the current code path.
69  *
70  * Also, in size_allocate, if we invalidate some lines from changing
71  * the layout width, we need to go ahead and run the high-priority idle,
72  * because GTK sends exposes right after doing the size allocates without
73  * returning to the main loop. This is also why the high-priority idle
74  * is at a higher priority than resizing.
75  *
76  */
77
78 #define FOCUS_EDGE_WIDTH 1
79
80 #define SCREEN_WIDTH(widget) text_window_get_width (GTK_TEXT_VIEW (widget)->text_window)
81 #define SCREEN_HEIGHT(widget) text_window_get_height (GTK_TEXT_VIEW (widget)->text_window)
82
83 /* #define DEBUG_VALIDATION_AND_SCROLLING */
84
85 #ifdef DEBUG_VALIDATION_AND_SCROLLING
86 #define DV(x) (x)
87 #else
88 #define DV(x)
89 #endif
90
91 struct _GtkTextPendingScroll
92 {
93   GtkTextMark   *mark;
94   gdouble        within_margin;
95   gboolean       use_align;
96   gdouble        xalign;
97   gdouble        yalign;
98 };
99   
100 enum
101 {
102   SET_SCROLL_ADJUSTMENTS,
103   POPULATE_POPUP,
104   MOVE_CURSOR,
105   SET_ANCHOR,
106   INSERT_AT_CURSOR,
107   DELETE_FROM_CURSOR,
108   CUT_CLIPBOARD,
109   COPY_CLIPBOARD,
110   PASTE_CLIPBOARD,
111   TOGGLE_OVERWRITE,
112   LAST_SIGNAL
113 };
114
115 enum
116 {
117   ARG_0,
118   ARG_HEIGHT_LINES,
119   ARG_WIDTH_COLUMNS,
120   ARG_PIXELS_ABOVE_LINES,
121   ARG_PIXELS_BELOW_LINES,
122   ARG_PIXELS_INSIDE_WRAP,
123   ARG_EDITABLE,
124   ARG_WRAP_MODE,
125   ARG_JUSTIFY,
126   ARG_LEFT_MARGIN,
127   ARG_RIGHT_MARGIN,
128   ARG_INDENT,
129   ARG_TABS,
130   LAST_ARG
131 };
132
133 static void gtk_text_view_init                 (GtkTextView      *text_view);
134 static void gtk_text_view_class_init           (GtkTextViewClass *klass);
135 static void gtk_text_view_destroy              (GtkObject        *object);
136 static void gtk_text_view_finalize             (GObject          *object);
137 static void gtk_text_view_set_arg              (GtkObject        *object,
138                                                 GtkArg           *arg,
139                                                 guint             arg_id);
140 static void gtk_text_view_get_arg              (GtkObject        *object,
141                                                 GtkArg           *arg,
142                                                 guint             arg_id);
143 static void gtk_text_view_size_request         (GtkWidget        *widget,
144                                                 GtkRequisition   *requisition);
145 static void gtk_text_view_size_allocate        (GtkWidget        *widget,
146                                                 GtkAllocation    *allocation);
147 static void gtk_text_view_realize              (GtkWidget        *widget);
148 static void gtk_text_view_unrealize            (GtkWidget        *widget);
149 static void gtk_text_view_style_set            (GtkWidget        *widget,
150                                                 GtkStyle         *previous_style);
151 static void gtk_text_view_direction_changed    (GtkWidget        *widget,
152                                                 GtkTextDirection  previous_direction);
153 static gint gtk_text_view_event                (GtkWidget        *widget,
154                                                 GdkEvent         *event);
155 static gint gtk_text_view_key_press_event      (GtkWidget        *widget,
156                                                 GdkEventKey      *event);
157 static gint gtk_text_view_key_release_event    (GtkWidget        *widget,
158                                                 GdkEventKey      *event);
159 static gint gtk_text_view_button_press_event   (GtkWidget        *widget,
160                                                 GdkEventButton   *event);
161 static gint gtk_text_view_button_release_event (GtkWidget        *widget,
162                                                 GdkEventButton   *event);
163 static gint gtk_text_view_focus_in_event       (GtkWidget        *widget,
164                                                 GdkEventFocus    *event);
165 static gint gtk_text_view_focus_out_event      (GtkWidget        *widget,
166                                                 GdkEventFocus    *event);
167 static gint gtk_text_view_motion_event         (GtkWidget        *widget,
168                                                 GdkEventMotion   *event);
169 static gint gtk_text_view_expose_event         (GtkWidget        *widget,
170                                                 GdkEventExpose   *expose);
171 static void gtk_text_view_draw_focus           (GtkWidget        *widget);
172
173 /* Source side drag signals */
174 static void gtk_text_view_drag_begin       (GtkWidget        *widget,
175                                             GdkDragContext   *context);
176 static void gtk_text_view_drag_end         (GtkWidget        *widget,
177                                             GdkDragContext   *context);
178 static void gtk_text_view_drag_data_get    (GtkWidget        *widget,
179                                             GdkDragContext   *context,
180                                             GtkSelectionData *selection_data,
181                                             guint             info,
182                                             guint             time);
183 static void gtk_text_view_drag_data_delete (GtkWidget        *widget,
184                                             GdkDragContext   *context);
185
186 /* Target side drag signals */
187 static void     gtk_text_view_drag_leave         (GtkWidget        *widget,
188                                                   GdkDragContext   *context,
189                                                   guint             time);
190 static gboolean gtk_text_view_drag_motion        (GtkWidget        *widget,
191                                                   GdkDragContext   *context,
192                                                   gint              x,
193                                                   gint              y,
194                                                   guint             time);
195 static gboolean gtk_text_view_drag_drop          (GtkWidget        *widget,
196                                                   GdkDragContext   *context,
197                                                   gint              x,
198                                                   gint              y,
199                                                   guint             time);
200 static void     gtk_text_view_drag_data_received (GtkWidget        *widget,
201                                                   GdkDragContext   *context,
202                                                   gint              x,
203                                                   gint              y,
204                                                   GtkSelectionData *selection_data,
205                                                   guint             info,
206                                                   guint             time);
207
208 static void gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
209                                                   GtkAdjustment *hadj,
210                                                   GtkAdjustment *vadj);
211 static void gtk_text_view_popup_menu             (GtkWidget     *widget);
212
213 static void gtk_text_view_move_cursor      (GtkTextView           *text_view,
214                                             GtkMovementStep        step,
215                                             gint                   count,
216                                             gboolean               extend_selection);
217 static void gtk_text_view_set_anchor       (GtkTextView           *text_view);
218 static void gtk_text_view_scroll_pages     (GtkTextView           *text_view,
219                                             gint                   count);
220 static void gtk_text_view_insert_at_cursor (GtkTextView           *text_view,
221                                             const gchar           *str);
222 static void gtk_text_view_delete_from_cursor (GtkTextView           *text_view,
223                                               GtkDeleteType          type,
224                                               gint                   count);
225 static void gtk_text_view_cut_clipboard    (GtkTextView           *text_view);
226 static void gtk_text_view_copy_clipboard   (GtkTextView           *text_view);
227 static void gtk_text_view_paste_clipboard  (GtkTextView           *text_view);
228 static void gtk_text_view_toggle_overwrite (GtkTextView           *text_view);
229 static void gtk_text_view_unselect         (GtkTextView           *text_view);
230
231 static void     gtk_text_view_validate_onscreen     (GtkTextView        *text_view);
232 static void     gtk_text_view_get_first_para_iter   (GtkTextView        *text_view,
233                                                      GtkTextIter        *iter);
234 static void     gtk_text_view_update_layout_width       (GtkTextView        *text_view);
235 static void     gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
236                                                          GtkTextAttributes *values,
237                                                          GtkStyle           *style);
238 static void     gtk_text_view_ensure_layout         (GtkTextView        *text_view);
239 static void     gtk_text_view_destroy_layout        (GtkTextView        *text_view);
240 static void     gtk_text_view_reset_im_context      (GtkTextView        *text_view);
241 static void     gtk_text_view_start_selection_drag  (GtkTextView        *text_view,
242                                                      const GtkTextIter  *iter,
243                                                      GdkEventButton     *event);
244 static gboolean gtk_text_view_end_selection_drag    (GtkTextView        *text_view,
245                                                      GdkEventButton     *event);
246 static void     gtk_text_view_start_selection_dnd   (GtkTextView        *text_view,
247                                                      const GtkTextIter  *iter,
248                                                      GdkEventMotion     *event);
249 static void     gtk_text_view_start_cursor_blink    (GtkTextView        *text_view,
250                                                      gboolean            with_delay);
251 static void     gtk_text_view_stop_cursor_blink     (GtkTextView        *text_view);
252
253 static void gtk_text_view_value_changed           (GtkAdjustment *adj,
254                                                    GtkTextView   *view);
255 static void gtk_text_view_commit_handler          (GtkIMContext  *context,
256                                                    const gchar   *str,
257                                                    GtkTextView   *text_view);
258 static void gtk_text_view_preedit_changed_handler (GtkIMContext  *context,
259                                                    GtkTextView   *text_view);
260
261 static void gtk_text_view_mark_set_handler       (GtkTextBuffer     *buffer,
262                                                   const GtkTextIter *location,
263                                                   GtkTextMark       *mark,
264                                                   gpointer           data);
265 static void gtk_text_view_get_virtual_cursor_pos (GtkTextView       *text_view,
266                                                   gint              *x,
267                                                   gint              *y);
268 static void gtk_text_view_set_virtual_cursor_pos (GtkTextView       *text_view,
269                                                   gint               x,
270                                                   gint               y);
271
272 static GtkAdjustment* get_hadjustment            (GtkTextView       *text_view);
273 static GtkAdjustment* get_vadjustment            (GtkTextView       *text_view);
274
275 static void gtk_text_view_do_popup               (GtkTextView       *text_view,
276                                                   GdkEventButton    *event);
277
278 static void gtk_text_view_queue_scroll           (GtkTextView   *text_view,
279                                                   GtkTextMark   *mark,
280                                                   gdouble        within_margin,
281                                                   gboolean       use_align,
282                                                   gdouble        xalign,
283                                                   gdouble        yalign);
284
285 static gboolean gtk_text_view_flush_scroll       (GtkTextView *text_view);
286
287 /* Container methods */
288 static void gtk_text_view_add    (GtkContainer *container,
289                                   GtkWidget    *child);
290 static void gtk_text_view_remove (GtkContainer *container,
291                                   GtkWidget    *child);
292 static void gtk_text_view_forall (GtkContainer *container,
293                                   gboolean      include_internals,
294                                   GtkCallback   callback,
295                                   gpointer      callback_data);
296
297 /* FIXME probably need the focus methods. */
298
299 /* Hack-around */
300 #define g_signal_handlers_disconnect_by_func(obj, func, data) g_signal_handlers_disconnect_matched (obj, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, func, data)
301
302 typedef struct _GtkTextViewChild GtkTextViewChild;
303
304 struct _GtkTextViewChild
305 {
306   GtkWidget *widget;
307
308   GtkTextChildAnchor *anchor;
309
310   gint from_top_of_line;
311   gint from_left_of_buffer;
312   
313   /* These are ignored if anchor != NULL */
314   GtkTextWindowType type;
315   gint x;
316   gint y;
317 };
318
319 static GtkTextViewChild* text_view_child_new_anchored (GtkWidget          *child,
320                                                        GtkTextChildAnchor *anchor,
321                                                        GtkTextLayout      *layout);
322 static GtkTextViewChild* text_view_child_new_window   (GtkWidget          *child,
323                                                        GtkTextWindowType   type,
324                                                        gint                x,
325                                                        gint                y);
326 static void              text_view_child_free         (GtkTextViewChild   *child);
327
328 static void              text_view_child_realize      (GtkTextView      *text_view,
329                                                        GtkTextViewChild *child);
330
331 struct _GtkTextWindow
332 {
333   GtkTextWindowType type;
334   GtkWidget *widget;
335   GdkWindow *window;
336   GdkWindow *bin_window;
337   GtkRequisition requisition;
338   GdkRectangle allocation;
339 };
340
341 static GtkTextWindow *text_window_new             (GtkTextWindowType  type,
342                                                    GtkWidget         *widget,
343                                                    gint               width_request,
344                                                    gint               height_request);
345 static void           text_window_free            (GtkTextWindow     *win);
346 static void           text_window_realize         (GtkTextWindow     *win,
347                                                    GdkWindow         *parent);
348 static void           text_window_unrealize       (GtkTextWindow     *win);
349 static void           text_window_size_allocate   (GtkTextWindow     *win,
350                                                    GdkRectangle      *rect);
351 static void           text_window_scroll          (GtkTextWindow     *win,
352                                                    gint               dx,
353                                                    gint               dy);
354 static void           text_window_invalidate_rect (GtkTextWindow     *win,
355                                                    GdkRectangle      *rect);
356
357 static gint           text_window_get_width       (GtkTextWindow     *win);
358 static gint           text_window_get_height      (GtkTextWindow     *win);
359 static void           text_window_get_allocation  (GtkTextWindow     *win,
360                                                    GdkRectangle      *rect);
361
362
363 enum
364 {
365   TARGET_STRING,
366   TARGET_TEXT,
367   TARGET_COMPOUND_TEXT,
368   TARGET_UTF8_STRING,
369   TARGET_TEXT_BUFFER_CONTENTS
370 };
371
372 static GtkTargetEntry target_table[] = {
373   { "GTK_TEXT_BUFFER_CONTENTS", GTK_TARGET_SAME_APP,
374     TARGET_TEXT_BUFFER_CONTENTS },
375   { "UTF8_STRING", 0, TARGET_UTF8_STRING },
376   { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
377   { "TEXT", 0, TARGET_TEXT },
378   { "text/plain", 0, TARGET_STRING },
379   { "STRING",     0, TARGET_STRING }
380 };
381
382 static GtkContainerClass *parent_class = NULL;
383 static guint signals[LAST_SIGNAL] = { 0 };
384
385 GtkType
386 gtk_text_view_get_type (void)
387 {
388   static GtkType our_type = 0;
389
390   if (our_type == 0)
391     {
392       static const GtkTypeInfo our_info =
393       {
394         "GtkTextView",
395         sizeof (GtkTextView),
396         sizeof (GtkTextViewClass),
397         (GtkClassInitFunc) gtk_text_view_class_init,
398         (GtkObjectInitFunc) gtk_text_view_init,
399         /* reserved_1 */ NULL,
400         /* reserved_2 */ NULL,
401         (GtkClassInitFunc) NULL
402       };
403
404       our_type = gtk_type_unique (GTK_TYPE_CONTAINER, &our_info);
405     }
406
407   return our_type;
408 }
409
410 static void
411 add_move_binding (GtkBindingSet  *binding_set,
412                   guint           keyval,
413                   guint           modmask,
414                   GtkMovementStep step,
415                   gint            count)
416 {
417   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
418
419   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
420                                 "move_cursor", 3,
421                                 GTK_TYPE_ENUM, step,
422                                 GTK_TYPE_INT, count,
423                                 GTK_TYPE_BOOL, FALSE);
424
425   /* Selection-extending version */
426   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
427                                 "move_cursor", 3,
428                                 GTK_TYPE_ENUM, step,
429                                 GTK_TYPE_INT, count,
430                                 GTK_TYPE_BOOL, TRUE);
431 }
432
433 static void
434 gtk_text_view_class_init (GtkTextViewClass *klass)
435 {
436   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
437   GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
438   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
439   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
440   GtkBindingSet *binding_set;
441
442   parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
443
444   /* Default handlers and virtual methods
445    */
446   object_class->set_arg = gtk_text_view_set_arg;
447   object_class->get_arg = gtk_text_view_get_arg;
448
449   object_class->destroy = gtk_text_view_destroy;
450   gobject_class->finalize = gtk_text_view_finalize;
451
452   widget_class->realize = gtk_text_view_realize;
453   widget_class->unrealize = gtk_text_view_unrealize;
454   widget_class->style_set = gtk_text_view_style_set;
455   widget_class->direction_changed = gtk_text_view_direction_changed;
456   widget_class->size_request = gtk_text_view_size_request;
457   widget_class->size_allocate = gtk_text_view_size_allocate;
458   widget_class->event = gtk_text_view_event;
459   widget_class->key_press_event = gtk_text_view_key_press_event;
460   widget_class->key_release_event = gtk_text_view_key_release_event;
461   widget_class->button_press_event = gtk_text_view_button_press_event;
462   widget_class->button_release_event = gtk_text_view_button_release_event;
463   widget_class->focus_in_event = gtk_text_view_focus_in_event;
464   widget_class->focus_out_event = gtk_text_view_focus_out_event;
465   widget_class->motion_notify_event = gtk_text_view_motion_event;
466   widget_class->expose_event = gtk_text_view_expose_event;
467
468   widget_class->drag_begin = gtk_text_view_drag_begin;
469   widget_class->drag_end = gtk_text_view_drag_end;
470   widget_class->drag_data_get = gtk_text_view_drag_data_get;
471   widget_class->drag_data_delete = gtk_text_view_drag_data_delete;
472
473   widget_class->drag_leave = gtk_text_view_drag_leave;
474   widget_class->drag_motion = gtk_text_view_drag_motion;
475   widget_class->drag_drop = gtk_text_view_drag_drop;
476   widget_class->drag_data_received = gtk_text_view_drag_data_received;
477
478   widget_class->popup_menu = gtk_text_view_popup_menu;
479   
480   container_class->add = gtk_text_view_add;
481   container_class->remove = gtk_text_view_remove;
482   container_class->forall = gtk_text_view_forall;
483
484   klass->move_cursor = gtk_text_view_move_cursor;
485   klass->set_anchor = gtk_text_view_set_anchor;
486   klass->insert_at_cursor = gtk_text_view_insert_at_cursor;
487   klass->delete_from_cursor = gtk_text_view_delete_from_cursor;
488   klass->cut_clipboard = gtk_text_view_cut_clipboard;
489   klass->copy_clipboard = gtk_text_view_copy_clipboard;
490   klass->paste_clipboard = gtk_text_view_paste_clipboard;
491   klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
492   klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments;
493
494   /*
495    * Arguments
496    */
497   gtk_object_add_arg_type ("GtkTextView::height_lines", GTK_TYPE_INT,
498                            GTK_ARG_READWRITE, ARG_HEIGHT_LINES);
499   gtk_object_add_arg_type ("GtkTextView::width_columns", GTK_TYPE_INT,
500                            GTK_ARG_READWRITE, ARG_WIDTH_COLUMNS);
501   gtk_object_add_arg_type ("GtkTextView::pixels_above_lines", GTK_TYPE_INT,
502                            GTK_ARG_READWRITE, ARG_PIXELS_ABOVE_LINES);
503   gtk_object_add_arg_type ("GtkTextView::pixels_below_lines", GTK_TYPE_INT,
504                            GTK_ARG_READWRITE, ARG_PIXELS_BELOW_LINES);
505   gtk_object_add_arg_type ("GtkTextView::pixels_inside_wrap", GTK_TYPE_INT,
506                            GTK_ARG_READWRITE, ARG_PIXELS_INSIDE_WRAP);
507   gtk_object_add_arg_type ("GtkTextView::editable", GTK_TYPE_BOOL,
508                            GTK_ARG_READWRITE, ARG_EDITABLE);
509
510   gtk_object_add_arg_type ("GtkTextView::wrap_mode", GTK_TYPE_WRAP_MODE,
511                            GTK_ARG_READWRITE, ARG_WRAP_MODE);
512   gtk_object_add_arg_type ("GtkTextView::justify", GTK_TYPE_JUSTIFICATION,
513                            GTK_ARG_READWRITE, ARG_JUSTIFY);
514   gtk_object_add_arg_type ("GtkTextView::left_margin", GTK_TYPE_INT,
515                            GTK_ARG_READWRITE, ARG_LEFT_MARGIN);
516   gtk_object_add_arg_type ("GtkTextView::right_margin", GTK_TYPE_INT,
517                            GTK_ARG_READWRITE, ARG_RIGHT_MARGIN);
518   gtk_object_add_arg_type ("GtkTextView::indent", GTK_TYPE_INT,
519                            GTK_ARG_READWRITE, ARG_INDENT);
520   gtk_object_add_arg_type ("GtkTextView::tabs", GTK_TYPE_PANGO_TAB_ARRAY,
521                            GTK_ARG_READWRITE, ARG_TABS);
522
523   /*
524    * Signals
525    */
526
527   signals[MOVE_CURSOR] =
528     gtk_signal_new ("move_cursor",
529                     GTK_RUN_LAST | GTK_RUN_ACTION,
530                     GTK_CLASS_TYPE (object_class),
531                     GTK_SIGNAL_OFFSET (GtkTextViewClass, move_cursor),
532                     gtk_marshal_VOID__ENUM_INT_BOOLEAN,
533                     GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL);
534
535   signals[SET_ANCHOR] =
536     gtk_signal_new ("set_anchor",
537                     GTK_RUN_LAST | GTK_RUN_ACTION,
538                     GTK_CLASS_TYPE (object_class),
539                     GTK_SIGNAL_OFFSET (GtkTextViewClass, set_anchor),
540                     gtk_marshal_VOID__VOID,
541                     GTK_TYPE_NONE, 0);
542
543   signals[INSERT_AT_CURSOR] =
544     gtk_signal_new ("insert_at_cursor",
545                     GTK_RUN_LAST | GTK_RUN_ACTION,
546                     GTK_CLASS_TYPE (object_class),
547                     GTK_SIGNAL_OFFSET (GtkTextViewClass, insert_at_cursor),
548                     gtk_marshal_VOID__STRING,
549                     GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
550
551   signals[DELETE_FROM_CURSOR] =
552     gtk_signal_new ("delete_from_cursor",
553                     GTK_RUN_LAST | GTK_RUN_ACTION,
554                     GTK_CLASS_TYPE (object_class),
555                     GTK_SIGNAL_OFFSET (GtkTextViewClass, delete_from_cursor),
556                     gtk_marshal_VOID__ENUM_INT,
557                     GTK_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, GTK_TYPE_INT);
558
559   signals[CUT_CLIPBOARD] =
560     gtk_signal_new ("cut_clipboard",
561                     GTK_RUN_LAST | GTK_RUN_ACTION,
562                     GTK_CLASS_TYPE (object_class),
563                     GTK_SIGNAL_OFFSET (GtkTextViewClass, cut_clipboard),
564                     gtk_marshal_VOID__VOID,
565                     GTK_TYPE_NONE, 0);
566
567   signals[COPY_CLIPBOARD] =
568     gtk_signal_new ("copy_clipboard",
569                     GTK_RUN_LAST | GTK_RUN_ACTION,
570                     GTK_CLASS_TYPE (object_class),
571                     GTK_SIGNAL_OFFSET (GtkTextViewClass, copy_clipboard),
572                     gtk_marshal_VOID__VOID,
573                     GTK_TYPE_NONE, 0);
574
575   signals[PASTE_CLIPBOARD] =
576     gtk_signal_new ("paste_clipboard",
577                     GTK_RUN_LAST | GTK_RUN_ACTION,
578                     GTK_CLASS_TYPE (object_class),
579                     GTK_SIGNAL_OFFSET (GtkTextViewClass, paste_clipboard),
580                     gtk_marshal_VOID__VOID,
581                     GTK_TYPE_NONE, 0);
582
583   signals[TOGGLE_OVERWRITE] =
584     gtk_signal_new ("toggle_overwrite",
585                     GTK_RUN_LAST | GTK_RUN_ACTION,
586                     GTK_CLASS_TYPE (object_class),
587                     GTK_SIGNAL_OFFSET (GtkTextViewClass, toggle_overwrite),
588                     gtk_marshal_VOID__VOID,
589                     GTK_TYPE_NONE, 0);
590
591   signals[SET_SCROLL_ADJUSTMENTS] =
592     gtk_signal_new ("set_scroll_adjustments",
593                     GTK_RUN_LAST,
594                     GTK_CLASS_TYPE (object_class),
595                     GTK_SIGNAL_OFFSET (GtkTextViewClass, set_scroll_adjustments),
596                     gtk_marshal_VOID__OBJECT_OBJECT,
597                     GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
598   widget_class->set_scroll_adjustments_signal = signals[SET_SCROLL_ADJUSTMENTS];
599
600   signals[POPULATE_POPUP] =
601     gtk_signal_new ("populate_popup",
602                     GTK_RUN_LAST,
603                     GTK_CLASS_TYPE (object_class),
604                     GTK_SIGNAL_OFFSET (GtkTextViewClass, populate_popup),
605                     gtk_marshal_VOID__OBJECT,
606                     GTK_TYPE_NONE, 1, GTK_TYPE_MENU);
607   
608   /*
609    * Key bindings
610    */
611
612   binding_set = gtk_binding_set_by_class (klass);
613
614   /* Moving the insertion point */
615   add_move_binding (binding_set, GDK_Right, 0,
616                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
617
618   add_move_binding (binding_set, GDK_Left, 0,
619                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
620
621   add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
622                     GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
623
624   add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
625                     GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
626
627   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
628                     GTK_MOVEMENT_WORDS, 1);
629
630   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
631                     GTK_MOVEMENT_WORDS, -1);
632
633   /* Eventually we want to move by display lines, not paragraphs */
634   add_move_binding (binding_set, GDK_Up, 0,
635                     GTK_MOVEMENT_DISPLAY_LINES, -1);
636
637   add_move_binding (binding_set, GDK_Down, 0,
638                     GTK_MOVEMENT_DISPLAY_LINES, 1);
639
640   add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK,
641                     GTK_MOVEMENT_DISPLAY_LINES, -1);
642
643   add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK,
644                     GTK_MOVEMENT_DISPLAY_LINES, 1);
645
646   add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
647                     GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
648
649   add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
650                     GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
651
652   add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
653                     GTK_MOVEMENT_WORDS, 1);
654
655   add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
656                     GTK_MOVEMENT_WORDS, -1);
657
658   add_move_binding (binding_set, GDK_Home, 0,
659                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
660
661   add_move_binding (binding_set, GDK_End, 0,
662                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
663
664   add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
665                     GTK_MOVEMENT_BUFFER_ENDS, -1);
666
667   add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
668                     GTK_MOVEMENT_BUFFER_ENDS, 1);
669
670   add_move_binding (binding_set, GDK_Page_Up, 0,
671                     GTK_MOVEMENT_PAGES, -1);
672
673   add_move_binding (binding_set, GDK_Page_Down, 0,
674                     GTK_MOVEMENT_PAGES, 1);
675
676
677   /* Setting the cut/paste/copy anchor */
678   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK,
679                                 "set_anchor", 0);
680
681   /* Deleting text */
682   gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
683                                 "delete_from_cursor", 2,
684                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
685                                 GTK_TYPE_INT, 1);
686
687   gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, 0,
688                                 "delete_from_cursor", 2,
689                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
690                                 GTK_TYPE_INT, 1);
691   
692   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK,
693                                 "delete_from_cursor", 2,
694                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
695                                 GTK_TYPE_INT, 1);
696
697   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
698                                 "delete_from_cursor", 2,
699                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
700                                 GTK_TYPE_INT, -1);
701
702   gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
703                                 "delete_from_cursor", 2,
704                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
705                                 GTK_TYPE_INT, 1);
706
707   gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, GDK_CONTROL_MASK,
708                                 "delete_from_cursor", 2,
709                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
710                                 GTK_TYPE_INT, 1);
711   
712   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_MOD1_MASK,
713                                 "delete_from_cursor", 2,
714                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
715                                 GTK_TYPE_INT, 1);
716
717   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
718                                 "delete_from_cursor", 2,
719                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
720                                 GTK_TYPE_INT, -1);
721
722   gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
723                                 "delete_from_cursor", 2,
724                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
725                                 GTK_TYPE_INT, 1);
726
727   gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
728                                 "delete_from_cursor", 2,
729                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPHS,
730                                 GTK_TYPE_INT, 1);
731
732   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
733                                 "delete_from_cursor", 2,
734                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
735                                 GTK_TYPE_INT, 1);
736   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
737                                 "insert_at_cursor", 1,
738                                 GTK_TYPE_STRING, " ");
739
740   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
741                                 "delete_from_cursor", 2,
742                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
743                                 GTK_TYPE_INT, 1);
744
745   /* Cut/copy/paste */
746
747   gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
748                                 "cut_clipboard", 0);
749
750   gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
751                                 "cut_clipboard", 0);
752
753   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
754                                 "copy_clipboard", 0);
755
756   gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
757                                 "paste_clipboard", 0);
758
759   gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
760                                 "paste_clipboard", 0);
761
762   /* Overwrite */
763   gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
764                                 "toggle_overwrite", 0);
765 }
766
767 void
768 gtk_text_view_init (GtkTextView *text_view)
769 {
770   GtkWidget *widget;
771
772   widget = GTK_WIDGET (text_view);
773
774   GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
775
776   /* Set up default style */
777   text_view->wrap_mode = GTK_WRAP_NONE;
778   text_view->pixels_above_lines = 0;
779   text_view->pixels_below_lines = 0;
780   text_view->pixels_inside_wrap = 0;
781   text_view->justify = GTK_JUSTIFY_LEFT;
782   text_view->left_margin = 0;
783   text_view->right_margin = 0;
784   text_view->indent = 0;
785   text_view->tabs = NULL;
786   text_view->editable = TRUE;
787
788   gtk_drag_dest_set (widget,
789                      GTK_DEST_DEFAULT_DROP,
790                      target_table, G_N_ELEMENTS (target_table),
791                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
792
793   text_view->virtual_cursor_x = -1;
794   text_view->virtual_cursor_y = -1;
795
796   /* This object is completely private. No external entity can gain a reference
797    * to it; so we create it here and destroy it in finalize ().
798    */
799   text_view->im_context = gtk_im_multicontext_new ();
800
801   gtk_signal_connect (GTK_OBJECT (text_view->im_context), "commit",
802                       GTK_SIGNAL_FUNC (gtk_text_view_commit_handler), text_view);
803
804   gtk_signal_connect (GTK_OBJECT (text_view->im_context), "preedit_changed",
805                       GTK_SIGNAL_FUNC (gtk_text_view_preedit_changed_handler), text_view);
806
807   text_view->cursor_visible = TRUE;
808
809   text_view->text_window = text_window_new (GTK_TEXT_WINDOW_TEXT,
810                                             widget, 200, 200);
811
812   text_view->drag_start_x = -1;
813   text_view->drag_start_y = -1;
814 }
815
816 /**
817  * gtk_text_view_new:
818  *
819  * Creates a new #GtkTextView. If you don't call gtk_text_view_set_buffer()
820  * before using the text view, an empty default buffer will be created
821  * for you. Get the buffer with gtk_text_view_get_buffer(). If you want
822  * to specify your own buffer, consider gtk_text_view_new_with_buffer().
823  *
824  * Return value: a new #GtkTextView
825  **/
826 GtkWidget*
827 gtk_text_view_new (void)
828 {
829   return GTK_WIDGET (gtk_type_new (gtk_text_view_get_type ()));
830 }
831
832 /**
833  * gtk_text_view_new_with_buffer:
834  * @buffer: a #GtkTextBuffer
835  *
836  * Creates a new #GtkTextView widget displaying the buffer
837  * @buffer. One buffer can be shared among many widgets.
838  * @buffer may be NULL to create a default buffer, in which case
839  * this function is equivalent to gtk_text_view_new(). The
840  * text view adds its own reference count to the buffer; it does not
841  * take over an existing reference.
842  *
843  * Return value: a new #GtkTextView.
844  **/
845 GtkWidget*
846 gtk_text_view_new_with_buffer (GtkTextBuffer *buffer)
847 {
848   GtkTextView *text_view;
849
850   text_view = (GtkTextView*)gtk_text_view_new ();
851
852   gtk_text_view_set_buffer (text_view, buffer);
853
854   return GTK_WIDGET (text_view);
855 }
856
857 /**
858  * gtk_text_view_set_buffer:
859  * @text_view: a #GtkTextView
860  * @buffer: a #GtkTextBuffer
861  *
862  * Sets @buffer as the buffer being displayed by @text_view. The previous
863  * buffer displayed by the text view is unreferenced, and a reference is
864  * added to @buffer. If you owned a reference to @buffer before passing it
865  * to this function, you must remove that reference yourself; #GtkTextView
866  * will not "adopt" it.
867  *
868  **/
869 void
870 gtk_text_view_set_buffer (GtkTextView   *text_view,
871                           GtkTextBuffer *buffer)
872 {
873   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
874   g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
875
876   if (text_view->buffer == buffer)
877     return;
878
879   if (text_view->buffer != NULL)
880     {
881       /* Destroy all anchored children */
882       GSList *tmp_list;
883       GSList *copy;
884
885       copy = g_slist_copy (text_view->children);
886       tmp_list = copy;
887       while (tmp_list != NULL)
888         {
889           GtkTextViewChild *vc = tmp_list->data;
890
891           if (vc->anchor)
892             {
893               gtk_widget_destroy (vc->widget);
894               /* vc may now be invalid! */
895             }
896
897           tmp_list = g_slist_next (tmp_list);
898         }
899
900       g_slist_free (copy);
901
902       g_signal_handlers_disconnect_by_func (G_OBJECT (text_view->buffer),
903                                             gtk_text_view_mark_set_handler, text_view);
904       g_object_unref (G_OBJECT (text_view->buffer));
905       text_view->dnd_mark = NULL;
906     }
907
908   text_view->buffer = buffer;
909
910   if (buffer != NULL)
911     {
912       GtkTextIter start;
913
914       g_object_ref (G_OBJECT (buffer));
915
916       if (text_view->layout)
917         gtk_text_layout_set_buffer (text_view->layout, buffer);
918
919       gtk_text_buffer_get_iter_at_offset (text_view->buffer, &start, 0);
920
921       text_view->dnd_mark = gtk_text_buffer_create_mark (text_view->buffer,
922                                                          "gtk_drag_target",
923                                                          &start, FALSE);
924
925       text_view->first_para_mark = gtk_text_buffer_create_mark (text_view->buffer,
926                                                                 NULL,
927                                                                 &start, TRUE);
928
929       text_view->first_para_pixels = 0;
930
931       g_signal_connect_data (G_OBJECT (text_view->buffer), "mark_set",
932                              gtk_text_view_mark_set_handler, text_view,
933                              NULL, FALSE, FALSE);
934     }
935
936   if (GTK_WIDGET_VISIBLE (text_view))
937     gtk_widget_queue_draw (GTK_WIDGET (text_view));
938 }
939
940 static GtkTextBuffer*
941 get_buffer (GtkTextView *text_view)
942 {
943   if (text_view->buffer == NULL)
944     {
945       GtkTextBuffer *b;
946       b = gtk_text_buffer_new (NULL);
947       gtk_text_view_set_buffer (text_view, b);
948       g_object_unref (G_OBJECT (b));
949     }
950
951   return text_view->buffer;
952 }
953
954 /**
955  * gtk_text_view_get_buffer:
956  * @text_view: a #GtkTextView
957  *
958  * Returns the #GtkTextBuffer being displayed by this text view.
959  * The reference count on the buffer is not incremented; the caller
960  * of this function won't own a new reference.
961  *
962  * Return value: a #GtkTextBuffer
963  **/
964 GtkTextBuffer*
965 gtk_text_view_get_buffer (GtkTextView *text_view)
966 {
967   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
968
969   return get_buffer (text_view);
970 }
971
972 /**
973  * gtk_text_view_get_iter_at_location:
974  * @text_view: a #GtkTextView
975  * @iter: a #GtkTextIter
976  * @x: x position, in buffer coordinates
977  * @y: y position, in buffer coordinates
978  *
979  * Retrieves the iterator at buffer coordinates @x and @y. Buffer
980  * coordinates are coordinates for the entire buffer, not just the
981  * currently-displayed portion.  If you have coordinates from an
982  * event, you have to convert those to buffer coordinates with
983  * gtk_text_view_window_to_buffer_coords().
984  *
985  **/
986 void
987 gtk_text_view_get_iter_at_location (GtkTextView *text_view,
988                                     GtkTextIter *iter,
989                                     gint         x,
990                                     gint         y)
991 {
992   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
993   g_return_if_fail (iter != NULL);
994   g_return_if_fail (text_view->layout != NULL);
995
996   gtk_text_layout_get_iter_at_pixel (text_view->layout,
997                                      iter,
998                                      x,
999                                      y);
1000 }
1001
1002 /**
1003  * gtk_text_view_get_iter_location:
1004  * @text_view: a #GtkTextView
1005  * @iter: a #GtkTextIter
1006  * @location: bounds of the character at @iter
1007  *
1008  * Gets a rectangle which roughly contains the character at @iter.
1009  * The rectangle position is in buffer coordinates; use
1010  * gtk_text_view_buffer_to_window_coords() to convert these
1011  * coordinates to coordinates for one of the windows in the text view.
1012  *
1013  **/
1014 void
1015 gtk_text_view_get_iter_location (GtkTextView       *text_view,
1016                                  const GtkTextIter *iter,
1017                                  GdkRectangle      *location)
1018 {
1019   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1020   g_return_if_fail (gtk_text_iter_get_buffer (iter) == get_buffer (text_view));
1021
1022   gtk_text_layout_get_iter_location (text_view->layout, iter, location);
1023 }
1024
1025 /**
1026  * gtk_text_view_get_line_yrange:
1027  * @text_view: a #GtkTextView
1028  * @iter: a #GtkTextIter
1029  * @y: return location for a y coordinate
1030  * @height: return location for a height
1031  *
1032  * Gets the y coordinate of the top of the line containing @iter,
1033  * and the height of the line. The coordinate is a buffer coordinate;
1034  * convert to window coordinates with gtk_text_view_buffer_to_window_coords().
1035  *
1036  **/
1037 void
1038 gtk_text_view_get_line_yrange (GtkTextView       *text_view,
1039                                const GtkTextIter *iter,
1040                                gint              *y,
1041                                gint              *height)
1042 {
1043   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1044   g_return_if_fail (gtk_text_iter_get_buffer (iter) == get_buffer (text_view));
1045
1046   gtk_text_layout_get_line_yrange (text_view->layout,
1047                                    iter,
1048                                    y,
1049                                    height);
1050 }
1051
1052 /**
1053  * gtk_text_view_get_line_at_y:
1054  * @text_view: a #GtkTextView
1055  * @target_iter: a #GtkTextIter
1056  * @y: a y coordinate
1057  * @line_top: return location for top coordinate of the line
1058  *
1059  * Gets the #GtkTextIter at the start of the line containing
1060  * the coordinate @y. @y is in buffer coordinates, convert from
1061  * window coordinates with gtk_text_view_window_to_buffer_coords().
1062  * If non-%NULL, @line_top will be filled with the coordinate of the top
1063  * edge of the line.
1064  **/
1065 void
1066 gtk_text_view_get_line_at_y (GtkTextView *text_view,
1067                              GtkTextIter *target_iter,
1068                              gint         y,
1069                              gint        *line_top)
1070 {
1071   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1072
1073   gtk_text_layout_get_line_at_y (text_view->layout,
1074                                  target_iter,
1075                                  y,
1076                                  line_top);
1077 }
1078
1079 static gboolean
1080 set_adjustment_clamped (GtkAdjustment *adj, gdouble val)
1081 {
1082   /* We don't really want to clamp to upper; we want to clamp to
1083      upper - page_size which is the highest value the scrollbar
1084      will let us reach. */
1085   if (val > (adj->upper - adj->page_size))
1086     val = adj->upper - adj->page_size;
1087
1088   if (val < adj->lower)
1089     val = adj->lower;
1090
1091   if (val != adj->value)
1092     {
1093       gtk_adjustment_set_value (adj, val);
1094       return TRUE;
1095     }
1096   else
1097     return FALSE;
1098 }
1099
1100 /**
1101  * gtk_text_view_scroll_to_iter:
1102  * @text_view: a #GtkTextView
1103  * @iter: a #GtkTextIter
1104  * @within_margin: margin as a [0.0,0.5) fraction of screen size
1105  * @use_align: whether to use alignment arguments (if %FALSE, just get the mark onscreen)
1106  * @xalign: horizontal alignment of mark within visible area.
1107  * @yalign: vertical alignment of mark within visible area
1108  *
1109  * Scrolls @text_view so that @iter is on the screen in the position
1110  * indicated by @xalign and @yalign. An alignment of 0.0 indicates
1111  * left or top, 1.0 indicates right or bottom, 0.5 means center. If @use_align
1112  * is %FALSE, the text scrolls the minimal distance to get the mark onscreen,
1113  * possibly not scrolling at all. The effective screen for purposes
1114  * of this function is reduced by a margin of size @within_margin.
1115  * NOTE: This function uses the currently-computed height of the
1116  * lines in the text buffer. Note that line heights are computed
1117  * in an idle handler; so this function may not have the desired effect
1118  * if it's called before the height computations. To avoid oddness,
1119  * consider using gtk_text_view_scroll_to_mark() which saves a point
1120  * to be scrolled to after line validation.
1121  *
1122  * Return value: %TRUE if scrolling occurred
1123  **/
1124 gboolean
1125 gtk_text_view_scroll_to_iter (GtkTextView   *text_view,
1126                               GtkTextIter   *iter,
1127                               gdouble        within_margin,
1128                               gboolean       use_align,
1129                               gdouble        xalign,
1130                               gdouble        yalign)
1131 {
1132   GdkRectangle rect;
1133   GdkRectangle screen;
1134   gint screen_bottom;
1135   gint screen_right;
1136   gint scroll_dest;
1137   GtkWidget *widget;
1138   gboolean retval = FALSE;
1139   gint scroll_inc;
1140   gint screen_xoffset, screen_yoffset;
1141   gint current_x_scroll, current_y_scroll;
1142   
1143   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1144   g_return_val_if_fail (iter != NULL, FALSE);
1145   g_return_val_if_fail (within_margin >= 0.0 && within_margin < 0.5, FALSE);
1146   g_return_val_if_fail (xalign >= 0.0 && xalign <= 1.0, FALSE);
1147   g_return_val_if_fail (yalign >= 0.0 && yalign <= 1.0, FALSE);
1148   
1149   widget = GTK_WIDGET (text_view);
1150
1151   DV(g_print(G_STRLOC"\n"));
1152   
1153   if (!GTK_WIDGET_MAPPED (widget))
1154     {
1155       g_warning ("%s: calling this function before mapping the GtkTextView doesn't make sense, maybe try gtk_text_view_scroll_to_mark() instead", G_STRLOC);
1156       return FALSE;
1157     }
1158   
1159   gtk_text_layout_get_iter_location (text_view->layout,
1160                                      iter,
1161                                      &rect);
1162
1163   current_x_scroll = text_view->xoffset;
1164   current_y_scroll = text_view->yoffset;
1165
1166   screen.x = current_x_scroll;
1167   screen.y = current_y_scroll;
1168   screen.width = SCREEN_WIDTH (widget);
1169   screen.height = SCREEN_HEIGHT (widget);
1170   
1171   screen_xoffset = screen.width * within_margin;
1172   screen_yoffset = screen.height * within_margin;
1173   
1174   screen.x += screen_xoffset;
1175   screen.y += screen_yoffset;
1176   screen.width -= screen_xoffset * 2;
1177   screen.height -= screen_yoffset * 2;
1178
1179   /* paranoia check */
1180   if (screen.width < 1)
1181     screen.width = 1;
1182   if (screen.height < 1)
1183     screen.height = 1;
1184   
1185   screen_right = screen.x + screen.width;
1186   screen_bottom = screen.y + screen.height;
1187   
1188   /* The alignment affects the point in the target character that we
1189    * choose to align. If we're doing right/bottom alignment, we align
1190    * the right/bottom edge of the character the mark is at; if we're
1191    * doing left/top we align the left/top edge of the character; if
1192    * we're doing center alignment we align the center of the
1193    * character.
1194    */
1195   
1196   /* Vertical scroll */
1197
1198   scroll_inc = 0;
1199   scroll_dest = current_y_scroll;
1200   
1201   if (use_align)
1202     {      
1203       scroll_dest = rect.y + (rect.height * yalign) - (screen.height * yalign);
1204       
1205       /* if scroll_dest < screen.y, we move a negative increment (up),
1206        * else a positive increment (down)
1207        */
1208       scroll_inc = scroll_dest - screen.y + screen_yoffset;
1209     }
1210   else
1211     {
1212       /* move minimum to get onscreen */
1213       if (rect.y < screen.y)
1214         {
1215           scroll_dest = rect.y;
1216           scroll_inc = scroll_dest - screen.y - screen_yoffset;
1217         }
1218       else if ((rect.y + rect.height) > screen_bottom)
1219         {
1220           scroll_dest = rect.y + rect.height;
1221           scroll_inc = scroll_dest - screen_bottom + screen_yoffset;
1222         }
1223     }  
1224   
1225   if (scroll_inc != 0)
1226     {
1227       retval = set_adjustment_clamped (get_vadjustment (text_view),
1228                                        current_y_scroll + scroll_inc);
1229     }
1230
1231   /* Horizontal scroll */
1232   
1233   scroll_inc = 0;
1234   scroll_dest = current_x_scroll;
1235   
1236   if (use_align)
1237     {      
1238       scroll_dest = rect.x + (rect.width * xalign) - (screen.width * xalign);
1239
1240       /* if scroll_dest < screen.y, we move a negative increment (left),
1241        * else a positive increment (right)
1242        */
1243       scroll_inc = scroll_dest - screen.x + screen_xoffset;
1244     }
1245   else
1246     {
1247       /* move minimum to get onscreen */
1248       if (rect.x < screen.x)
1249         {
1250           scroll_dest = rect.x;
1251           scroll_inc = scroll_dest - screen.x - screen_xoffset;
1252         }
1253       else if ((rect.x + rect.width) > screen_right)
1254         {
1255           scroll_dest = rect.x + rect.width;
1256           scroll_inc = scroll_dest - screen_right + screen_xoffset;
1257         }
1258     }
1259   
1260   if (scroll_inc != 0)
1261     {
1262       retval = set_adjustment_clamped (get_hadjustment (text_view),
1263                                        current_x_scroll + scroll_inc);
1264     }
1265
1266   if (retval)
1267     DV(g_print (">Actually scrolled ("G_STRLOC")\n"));
1268   else
1269     DV(g_print (">Didn't end up scrolling ("G_STRLOC")\n"));
1270   
1271   return retval;
1272 }
1273
1274 static void
1275 free_pending_scroll (GtkTextPendingScroll *scroll)
1276 {
1277   if (!gtk_text_mark_get_deleted (scroll->mark))
1278     gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (scroll->mark),
1279                                  scroll->mark);
1280   g_object_unref (G_OBJECT (scroll->mark));
1281   g_free (scroll);
1282 }
1283
1284 static void
1285 gtk_text_view_queue_scroll (GtkTextView   *text_view,
1286                             GtkTextMark   *mark,
1287                             gdouble        within_margin,
1288                             gboolean       use_align,
1289                             gdouble        xalign,
1290                             gdouble        yalign)
1291 {
1292   GtkTextIter iter;
1293   GtkTextPendingScroll *scroll;
1294
1295   DV(g_print(G_STRLOC"\n"));
1296   
1297   scroll = g_new (GtkTextPendingScroll, 1);
1298
1299   scroll->within_margin = within_margin;
1300   scroll->use_align = use_align;
1301   scroll->xalign = xalign;
1302   scroll->yalign = yalign;
1303   
1304   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, mark);
1305
1306   scroll->mark = gtk_text_buffer_create_mark (get_buffer (text_view),
1307                                               NULL,
1308                                               &iter,
1309                                               gtk_text_mark_get_left_gravity (mark));
1310
1311   g_object_ref (G_OBJECT (scroll->mark));
1312   
1313   if (text_view->pending_scroll)
1314     free_pending_scroll (text_view->pending_scroll);
1315
1316   text_view->pending_scroll = scroll;
1317 }
1318
1319 static gboolean
1320 gtk_text_view_flush_scroll (GtkTextView *text_view)
1321 {
1322   GtkTextIter iter, start, end;
1323   GtkTextPendingScroll *scroll;
1324   gint y0, y1, height;
1325   gboolean retval;
1326   
1327   DV(g_print(G_STRLOC"\n"));
1328   
1329   if (text_view->pending_scroll == NULL)
1330     return FALSE;
1331
1332   scroll = text_view->pending_scroll;
1333
1334   /* avoid recursion */
1335   text_view->pending_scroll = NULL;
1336   
1337   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, scroll->mark);
1338
1339   start = iter;
1340   end = iter;
1341
1342   /* Force-validate the region around the iterator, at least the lines
1343    * on either side, so that we can meaningfully get the iter location
1344    */
1345   gtk_text_iter_backward_line (&start);
1346   gtk_text_iter_forward_line (&end);
1347   
1348   gtk_text_layout_get_line_yrange (text_view->layout, &start, &y0, NULL);
1349   gtk_text_layout_get_line_yrange (text_view->layout, &end, &y1, &height);
1350
1351   DV(g_print (">Validating scroll destination ("G_STRLOC")\n"));
1352   gtk_text_layout_validate_yrange (text_view->layout, &start, y0, y1 + height);
1353
1354   DV(g_print (">Done validating scroll destination ("G_STRLOC")\n"));
1355   
1356   retval = gtk_text_view_scroll_to_iter (text_view,
1357                                          &iter,
1358                                          scroll->within_margin,
1359                                          scroll->use_align,
1360                                          scroll->xalign,
1361                                          scroll->yalign);
1362   
1363   free_pending_scroll (scroll);
1364
1365   return retval;
1366 }
1367
1368 static void
1369 gtk_text_view_set_adjustment_upper (GtkAdjustment *adj, gdouble upper)
1370 {  
1371   if (upper != adj->upper)
1372     {
1373       gdouble min = MAX (0., upper - adj->page_size);
1374       gboolean value_changed = FALSE;
1375
1376       adj->upper = upper;
1377
1378       if (adj->value > min)
1379         {
1380           adj->value = min;
1381           value_changed = TRUE;
1382         }
1383
1384       gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed");
1385       DV(g_print(">Changed adj upper to %d ("G_STRLOC")\n", upper));
1386       
1387       if (value_changed)
1388         {
1389           DV(g_print(">Changed adj value because upper descreased ("G_STRLOC")\n"));
1390           gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
1391         }
1392     }
1393 }
1394
1395 static void
1396 gtk_text_view_update_adjustments (GtkTextView *text_view)
1397 {
1398   gint width = 0, height = 0;
1399
1400   DV(g_print(">Updating adjustments ("G_STRLOC")\n"));
1401   
1402   gtk_text_layout_get_size (text_view->layout, &width, &height);
1403
1404   if (text_view->width != width || text_view->height != height)
1405     {
1406       text_view->width = width;
1407       text_view->height = height;
1408
1409       gtk_text_view_set_adjustment_upper (get_hadjustment (text_view),
1410                                           MAX (SCREEN_WIDTH (text_view), width));
1411       gtk_text_view_set_adjustment_upper (get_vadjustment (text_view),
1412                                           MAX (SCREEN_HEIGHT (text_view), height));
1413       
1414       /* hadj/vadj exist since we called get_hadjustment/get_vadjustment above */
1415
1416       /* Set up the step sizes; we'll say that a page is
1417          our allocation minus one step, and a step is
1418          1/10 of our allocation. */
1419       text_view->hadjustment->step_increment =
1420         SCREEN_WIDTH (text_view) / 10.0;
1421       text_view->hadjustment->page_increment =
1422         SCREEN_WIDTH (text_view) * 0.9;
1423       
1424       text_view->vadjustment->step_increment =
1425         SCREEN_HEIGHT (text_view) / 10.0;
1426       text_view->vadjustment->page_increment =
1427         SCREEN_HEIGHT (text_view) * 0.9;
1428
1429       gtk_signal_emit_by_name (GTK_OBJECT (get_hadjustment (text_view)), "changed");
1430       gtk_signal_emit_by_name (GTK_OBJECT (get_hadjustment (text_view)), "changed");
1431     }
1432 }
1433
1434 static void
1435 gtk_text_view_update_layout_width (GtkTextView *text_view)
1436 {
1437   DV(g_print(">Updating layout width ("G_STRLOC")\n"));
1438   
1439   gtk_text_view_ensure_layout (text_view);
1440
1441   gtk_text_layout_set_screen_width (text_view->layout,
1442                                     SCREEN_WIDTH (text_view));
1443 }
1444
1445 /**
1446  * gtk_text_view_scroll_to_mark:
1447  * @text_view: a #GtkTextView
1448  * @mark: a #GtkTextMark
1449  * @within_margin: margin as a [0.0,0.5) fraction of screen size
1450  * @use_align: whether to use alignment arguments (if %FALSE, just get the mark onscreen)
1451  * @xalign: horizontal alignment of mark within visible area.
1452  * @yalign: vertical alignment of mark within visible area
1453  *
1454  * Scrolls @text_view so that @mark is on the screen in the position
1455  * indicated by @xalign and @yalign. An alignment of 0.0 indicates
1456  * left or top, 1.0 indicates right or bottom, 0.5 means center. If @use_align
1457  * is %FALSE, the text scrolls the minimal distance to get the mark onscreen,
1458  * possibly not scrolling at all. The effective screen for purposes
1459  * of this function is reduced by a margin of size @within_margin.
1460  *
1461  **/
1462 void
1463 gtk_text_view_scroll_to_mark (GtkTextView *text_view,
1464                               GtkTextMark *mark,
1465                               gdouble      within_margin,
1466                               gboolean     use_align,
1467                               gdouble      xalign,
1468                               gdouble      yalign)
1469 {  
1470   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1471   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
1472   g_return_if_fail (within_margin >= 0.0 && within_margin < 0.5);
1473   g_return_if_fail (xalign >= 0.0 && xalign <= 1.0);
1474   g_return_if_fail (yalign >= 0.0 && yalign <= 1.0);
1475
1476   gtk_text_view_queue_scroll (text_view, mark,
1477                               within_margin,
1478                               use_align,
1479                               xalign,
1480                               yalign);
1481
1482   /* If no validation is pending, we need to go ahead and force an
1483    * immediate scroll.
1484    */
1485   if (text_view->layout &&
1486       gtk_text_layout_is_valid (text_view->layout))
1487     gtk_text_view_flush_scroll (text_view);
1488 }
1489
1490 void
1491 gtk_text_view_scroll_mark_onscreen (GtkTextView *text_view,
1492                                     GtkTextMark *mark)
1493 {
1494   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1495   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
1496
1497   gtk_text_view_scroll_to_mark (text_view, mark, 0.0, FALSE, 0.0, 0.0);
1498 }
1499
1500 static gboolean
1501 clamp_iter_onscreen (GtkTextView *text_view, GtkTextIter *iter)
1502 {
1503   GdkRectangle visible_rect;
1504   gtk_text_view_get_visible_rect (text_view, &visible_rect);
1505
1506   return gtk_text_layout_clamp_iter_to_vrange (text_view->layout, iter,
1507                                                visible_rect.y,
1508                                                visible_rect.y + visible_rect.height);
1509 }
1510
1511 /**
1512  * gtk_text_view_move_mark_onscreen:
1513  * @text_view: a #GtkTextView
1514  * @mark: a #GtkTextMark
1515  *
1516  * Moves a mark within the buffer so that it's
1517  * located within the currently-visible text area.
1518  *
1519  * Return value: %TRUE if the mark moved (wasn't already onscreen)
1520  **/
1521 gboolean
1522 gtk_text_view_move_mark_onscreen (GtkTextView *text_view,
1523                                   GtkTextMark *mark)
1524 {
1525   GtkTextIter iter;
1526
1527   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1528   g_return_val_if_fail (mark != NULL, FALSE);
1529
1530   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, mark);
1531
1532   if (clamp_iter_onscreen (text_view, &iter))
1533     {
1534       gtk_text_buffer_move_mark (get_buffer (text_view), mark, &iter);
1535       return TRUE;
1536     }
1537   else
1538     return FALSE;
1539 }
1540
1541 /**
1542  * gtk_text_view_get_visible_rect:
1543  * @text_view: a #GtkTextView
1544  * @visible_rect: rectangle to fill
1545  *
1546  * Fills @visible_rect with the currently-visible
1547  * region of the buffer, in buffer coordinates. Convert to window coordinates
1548  * with gtk_text_view_buffer_to_window_coords().
1549  **/
1550 void
1551 gtk_text_view_get_visible_rect (GtkTextView  *text_view,
1552                                 GdkRectangle *visible_rect)
1553 {
1554   GtkWidget *widget;
1555
1556   g_return_if_fail (text_view != NULL);
1557   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1558
1559   widget = GTK_WIDGET (text_view);
1560
1561   if (visible_rect)
1562     {
1563       visible_rect->x = text_view->xoffset;
1564       visible_rect->y = text_view->yoffset;
1565       visible_rect->width = SCREEN_WIDTH (widget);
1566       visible_rect->height = SCREEN_HEIGHT (widget);
1567
1568       DV(g_print(" visible rect: %d,%d %d x %d\n",
1569                  visible_rect->x,
1570                  visible_rect->y,
1571                  visible_rect->width,
1572                  visible_rect->height));
1573     }
1574 }
1575
1576 /**
1577  * gtk_text_view_set_wrap_mode:
1578  * @text_view: a #GtkTextView
1579  * @wrap_mode: a #GtkWrapMode
1580  *
1581  * Sets the line wrapping for the view.
1582  **/
1583 void
1584 gtk_text_view_set_wrap_mode (GtkTextView *text_view,
1585                              GtkWrapMode  wrap_mode)
1586 {
1587   g_return_if_fail (text_view != NULL);
1588   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1589
1590   if (text_view->wrap_mode != wrap_mode)
1591     {
1592       text_view->wrap_mode = wrap_mode;
1593
1594       if (text_view->layout)
1595         {
1596           text_view->layout->default_style->wrap_mode = wrap_mode;
1597           gtk_text_layout_default_style_changed (text_view->layout);
1598         }
1599     }
1600 }
1601
1602 /**
1603  * gtk_text_view_get_wrap_mode:
1604  * @text_view: a #GtkTextView
1605  *
1606  * Gets the line wrapping for the view.
1607  *
1608  * Return value: the line wrap setting
1609  **/
1610 GtkWrapMode
1611 gtk_text_view_get_wrap_mode (GtkTextView *text_view)
1612 {
1613   g_return_val_if_fail (text_view != NULL, GTK_WRAP_NONE);
1614   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_WRAP_NONE);
1615
1616   return text_view->wrap_mode;
1617 }
1618
1619 /**
1620  * gtk_text_view_set_editable:
1621  * @text_view: a #GtkTextView
1622  * @setting: whether it's editable
1623  *
1624  * Sets the default editability of the #GtkTextView. You can override
1625  * this default setting with tags in the buffer, using the "editable"
1626  * attribute of tags.
1627  **/
1628 void
1629 gtk_text_view_set_editable (GtkTextView *text_view,
1630                             gboolean     setting)
1631 {
1632   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1633
1634   if (text_view->editable != setting)
1635     {
1636       text_view->editable = setting;
1637
1638       if (text_view->layout)
1639         {
1640           text_view->layout->default_style->editable = text_view->editable;
1641           gtk_text_layout_default_style_changed (text_view->layout);
1642         }
1643     }
1644 }
1645
1646 /**
1647  * gtk_text_view_get_editable:
1648  * @text_view: a #GtkTextView
1649  *
1650  * Returns the default editability of the #GtkTextView. Tags in the
1651  * buffer may override this setting for some ranges of text.
1652  *
1653  * Return value: whether text is editable by default
1654  **/
1655 gboolean
1656 gtk_text_view_get_editable (GtkTextView *text_view)
1657 {
1658   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1659
1660   return text_view->editable;
1661 }
1662
1663 void
1664 gtk_text_view_set_pixels_above_lines (GtkTextView *text_view,
1665                                       gint         pixels_above_lines)
1666 {
1667   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1668
1669   if (text_view->pixels_above_lines != pixels_above_lines)
1670     {
1671       text_view->pixels_above_lines = pixels_above_lines;
1672
1673       if (text_view->layout)
1674         {
1675           text_view->layout->default_style->pixels_above_lines = pixels_above_lines;
1676           gtk_text_layout_default_style_changed (text_view->layout);
1677         }
1678     }
1679 }
1680
1681 gint
1682 gtk_text_view_get_pixels_above_lines (GtkTextView *text_view)
1683 {
1684   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
1685
1686   return text_view->pixels_above_lines;
1687 }
1688
1689 void
1690 gtk_text_view_set_pixels_below_lines (GtkTextView *text_view,
1691                                       gint         pixels_below_lines)
1692 {
1693   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1694
1695   if (text_view->pixels_below_lines != pixels_below_lines)
1696     {
1697       text_view->pixels_below_lines = pixels_below_lines;
1698
1699       if (text_view->layout)
1700         {
1701           text_view->layout->default_style->pixels_below_lines = pixels_below_lines;
1702           gtk_text_layout_default_style_changed (text_view->layout);
1703         }
1704     }
1705 }
1706
1707 gint
1708 gtk_text_view_get_pixels_below_lines (GtkTextView *text_view)
1709 {
1710   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
1711
1712   return text_view->pixels_below_lines;
1713 }
1714
1715 void
1716 gtk_text_view_set_pixels_inside_wrap (GtkTextView *text_view,
1717                                       gint         pixels_inside_wrap)
1718 {
1719   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1720
1721   if (text_view->pixels_inside_wrap != pixels_inside_wrap)
1722     {
1723       text_view->pixels_inside_wrap = pixels_inside_wrap;
1724
1725       if (text_view->layout)
1726         {
1727           text_view->layout->default_style->pixels_inside_wrap = pixels_inside_wrap;
1728           gtk_text_layout_default_style_changed (text_view->layout);
1729         }
1730     }
1731 }
1732
1733 gint
1734 gtk_text_view_get_pixels_inside_wrap (GtkTextView *text_view)
1735 {
1736   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
1737
1738   return text_view->pixels_inside_wrap;
1739 }
1740
1741 void
1742 gtk_text_view_set_justification (GtkTextView     *text_view,
1743                                  GtkJustification justify)
1744 {
1745   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1746
1747   if (text_view->justify != justify)
1748     {
1749       text_view->justify = justify;
1750
1751       if (text_view->layout)
1752         {
1753           text_view->layout->default_style->justification = justify;
1754           gtk_text_layout_default_style_changed (text_view->layout);
1755         }
1756     }
1757 }
1758
1759 GtkJustification
1760 gtk_text_view_get_justification (GtkTextView *text_view)
1761 {
1762   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_JUSTIFY_LEFT);
1763
1764   return text_view->justify;
1765 }
1766
1767 void
1768 gtk_text_view_set_left_margin (GtkTextView *text_view,
1769                                gint         left_margin)
1770 {
1771   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1772
1773   if (text_view->left_margin != left_margin)
1774     {
1775       text_view->left_margin = left_margin;
1776
1777       if (text_view->layout)
1778         {
1779           text_view->layout->default_style->left_margin = left_margin;
1780           gtk_text_layout_default_style_changed (text_view->layout);
1781         }
1782     }
1783 }
1784
1785 gint
1786 gtk_text_view_get_left_margin (GtkTextView *text_view)
1787 {
1788   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
1789
1790   return text_view->left_margin;
1791 }
1792
1793 void
1794 gtk_text_view_set_right_margin (GtkTextView *text_view,
1795                                 gint         right_margin)
1796 {
1797   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1798
1799   if (text_view->right_margin != right_margin)
1800     {
1801       text_view->right_margin = right_margin;
1802
1803       if (text_view->layout)
1804         {
1805           text_view->layout->default_style->right_margin = right_margin;
1806           gtk_text_layout_default_style_changed (text_view->layout);
1807         }
1808     }
1809 }
1810
1811 gint
1812 gtk_text_view_get_right_margin (GtkTextView *text_view)
1813 {
1814   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
1815
1816   return text_view->right_margin;
1817 }
1818
1819 void
1820 gtk_text_view_set_indent (GtkTextView *text_view,
1821                           gint         indent)
1822 {
1823   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1824
1825   if (text_view->indent != indent)
1826     {
1827       text_view->indent = indent;
1828
1829       if (text_view->layout)
1830         {
1831           text_view->layout->default_style->indent = indent;
1832           gtk_text_layout_default_style_changed (text_view->layout);
1833         }
1834     }
1835 }
1836
1837 gint
1838 gtk_text_view_get_indent (GtkTextView *text_view)
1839 {
1840   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
1841
1842   return text_view->indent;
1843 }
1844
1845 void
1846 gtk_text_view_set_tabs (GtkTextView   *text_view,
1847                         PangoTabArray *tabs)
1848 {
1849   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1850
1851   if (text_view->tabs)
1852     pango_tab_array_free (text_view->tabs);
1853
1854   text_view->tabs = tabs ? pango_tab_array_copy (tabs) : NULL;
1855
1856   if (text_view->layout)
1857     {
1858       /* some unkosher futzing in internal struct details... */
1859       if (text_view->layout->default_style->tabs)
1860         pango_tab_array_free (text_view->layout->default_style->tabs);
1861
1862       text_view->layout->default_style->tabs =
1863         text_view->tabs ? pango_tab_array_copy (text_view->tabs) : NULL;
1864
1865       gtk_text_layout_default_style_changed (text_view->layout);
1866     }
1867 }
1868
1869 PangoTabArray*
1870 gtk_text_view_get_tabs (GtkTextView *text_view)
1871 {
1872   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
1873
1874   return text_view->tabs ? pango_tab_array_copy (text_view->tabs) : NULL;
1875 }
1876
1877 /**
1878  * gtk_text_view_set_cursor_visible:
1879  * @text_view: a #GtkTextView
1880  * @setting: whether to show the insertion cursor
1881  *
1882  * Toggles whether the insertion point is displayed. A buffer with no editable
1883  * text probably shouldn't have a visible cursor, so you may want to turn
1884  * the cursor off.
1885  **/
1886 void
1887 gtk_text_view_set_cursor_visible    (GtkTextView   *text_view,
1888                                      gboolean       setting)
1889 {
1890   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1891
1892   setting = (setting != FALSE);
1893
1894   if (text_view->cursor_visible != setting)
1895     {
1896       text_view->cursor_visible = setting;
1897
1898       if (GTK_WIDGET_HAS_FOCUS (text_view))
1899         {
1900           if (text_view->layout)
1901             {
1902               gtk_text_layout_set_cursor_visible (text_view->layout, setting);
1903
1904               if (setting)
1905                 gtk_text_view_start_cursor_blink (text_view, FALSE);
1906               else
1907                 gtk_text_view_stop_cursor_blink (text_view);
1908             }
1909         }
1910     }
1911 }
1912
1913 /**
1914  * gtk_text_view_get_cursor_visible:
1915  * @text_view: a #GtkTextView
1916  *
1917  * Find out whether the cursor is being displayed.
1918  *
1919  * Return value: whether the insertion mark is visible
1920  **/
1921 gboolean
1922 gtk_text_view_get_cursor_visible    (GtkTextView   *text_view)
1923 {
1924   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1925
1926   return text_view->cursor_visible;
1927 }
1928
1929
1930 /**
1931  * gtk_text_view_place_cursor_onscreen:
1932  * @text_view: a #GtkTextView
1933  *
1934  * Moves the cursor to the currently visible region of the
1935  * buffer, it it isn't there already.
1936  *
1937  * Return value: TRUE if the cursor had to be moved.
1938  **/
1939 gboolean
1940 gtk_text_view_place_cursor_onscreen (GtkTextView *text_view)
1941 {
1942   GtkTextIter insert;
1943
1944   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1945
1946   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
1947                                     gtk_text_buffer_get_mark (get_buffer (text_view),
1948                                                               "insert"));
1949
1950   if (clamp_iter_onscreen (text_view, &insert))
1951     {
1952       gtk_text_buffer_place_cursor (get_buffer (text_view), &insert);
1953       return TRUE;
1954     }
1955   else
1956     return FALSE;
1957 }
1958
1959 static void
1960 gtk_text_view_destroy (GtkObject *object)
1961 {
1962   GtkTextView *text_view;
1963   GtkTextLayout *layout;
1964   
1965   text_view = GTK_TEXT_VIEW (object);
1966
1967   layout = text_view->layout;
1968   
1969   gtk_text_view_destroy_layout (text_view);
1970   gtk_text_view_set_buffer (text_view, NULL);
1971
1972   (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
1973 }
1974
1975 static void
1976 gtk_text_view_finalize (GObject *object)
1977 {
1978   GtkTextView *text_view;
1979
1980   text_view = GTK_TEXT_VIEW (object);
1981
1982   g_return_if_fail (text_view->buffer == NULL);
1983
1984   gtk_text_view_destroy_layout (text_view);
1985   gtk_text_view_set_buffer (text_view, NULL);
1986   
1987   if (text_view->pending_scroll)
1988     {
1989       free_pending_scroll (text_view->pending_scroll);
1990       text_view->pending_scroll = NULL;
1991     }
1992   
1993   if (text_view->hadjustment)
1994     g_object_unref (G_OBJECT (text_view->hadjustment));
1995   if (text_view->vadjustment)
1996     g_object_unref (G_OBJECT (text_view->vadjustment));
1997
1998   text_window_free (text_view->text_window);
1999
2000   if (text_view->left_window)
2001     text_window_free (text_view->left_window);
2002
2003   if (text_view->top_window)
2004     text_window_free (text_view->top_window);
2005
2006   if (text_view->right_window)
2007     text_window_free (text_view->right_window);
2008
2009   if (text_view->bottom_window)
2010     text_window_free (text_view->bottom_window);
2011
2012   g_object_unref (G_OBJECT (text_view->im_context));
2013
2014   (* G_OBJECT_CLASS (parent_class)->finalize) (object);
2015 }
2016
2017 static void
2018 gtk_text_view_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
2019 {
2020   GtkTextView *text_view;
2021
2022   text_view = GTK_TEXT_VIEW (object);
2023
2024   switch (arg_id)
2025     {
2026     case ARG_HEIGHT_LINES:
2027       g_warning ("FIXME");
2028       break;
2029
2030     case ARG_WIDTH_COLUMNS:
2031       g_warning ("FIXME");
2032       break;
2033
2034     case ARG_PIXELS_ABOVE_LINES:
2035       gtk_text_view_set_pixels_above_lines (text_view, GTK_VALUE_INT (*arg));
2036       break;
2037
2038     case ARG_PIXELS_BELOW_LINES:
2039       gtk_text_view_set_pixels_below_lines (text_view, GTK_VALUE_INT (*arg));
2040       break;
2041
2042     case ARG_PIXELS_INSIDE_WRAP:
2043       gtk_text_view_set_pixels_inside_wrap (text_view, GTK_VALUE_INT (*arg));
2044       break;
2045
2046     case ARG_EDITABLE:
2047       gtk_text_view_set_editable (text_view, GTK_VALUE_BOOL (*arg));
2048       break;
2049
2050     case ARG_WRAP_MODE:
2051       gtk_text_view_set_wrap_mode (text_view, GTK_VALUE_ENUM (*arg));
2052       break;
2053
2054     case ARG_JUSTIFY:
2055       gtk_text_view_set_justification (text_view, GTK_VALUE_ENUM (*arg));
2056       break;
2057
2058     case ARG_LEFT_MARGIN:
2059       gtk_text_view_set_left_margin (text_view, GTK_VALUE_INT (*arg));
2060       break;
2061
2062     case ARG_RIGHT_MARGIN:
2063       gtk_text_view_set_right_margin (text_view, GTK_VALUE_INT (*arg));
2064       break;
2065
2066     case ARG_INDENT:
2067       gtk_text_view_set_indent (text_view, GTK_VALUE_INT (*arg));
2068       break;
2069
2070     case ARG_TABS:
2071       gtk_text_view_set_tabs (text_view, GTK_VALUE_POINTER (*arg));
2072       break;
2073
2074     default:
2075       g_assert_not_reached ();
2076       break;
2077     }
2078 }
2079
2080 static void
2081 gtk_text_view_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
2082 {
2083   GtkTextView *text_view;
2084
2085   text_view = GTK_TEXT_VIEW (object);
2086
2087   switch (arg_id)
2088     {
2089     case ARG_HEIGHT_LINES:
2090       g_warning ("FIXME");
2091       break;
2092
2093     case ARG_WIDTH_COLUMNS:
2094       g_warning ("FIXME");
2095       break;
2096
2097     case ARG_PIXELS_ABOVE_LINES:
2098       GTK_VALUE_INT (*arg) = text_view->pixels_above_lines;
2099       break;
2100
2101     case ARG_PIXELS_BELOW_LINES:
2102       GTK_VALUE_INT (*arg) = text_view->pixels_below_lines;
2103       break;
2104
2105     case ARG_PIXELS_INSIDE_WRAP:
2106       GTK_VALUE_INT (*arg) = text_view->pixels_inside_wrap;
2107       break;
2108
2109     case ARG_EDITABLE:
2110       GTK_VALUE_BOOL (*arg) = text_view->editable;
2111       break;
2112
2113     case ARG_WRAP_MODE:
2114       GTK_VALUE_ENUM (*arg) = text_view->wrap_mode;
2115       break;
2116
2117     case ARG_JUSTIFY:
2118       GTK_VALUE_ENUM (*arg) = text_view->justify;
2119       break;
2120
2121     case ARG_LEFT_MARGIN:
2122       GTK_VALUE_INT (*arg) = text_view->left_margin;
2123       break;
2124
2125     case ARG_RIGHT_MARGIN:
2126       GTK_VALUE_INT (*arg) = text_view->right_margin;
2127       break;
2128
2129     case ARG_INDENT:
2130       GTK_VALUE_INT (*arg) = text_view->indent;
2131       break;
2132
2133     case ARG_TABS:
2134       GTK_VALUE_POINTER (*arg) = gtk_text_view_get_tabs (text_view);
2135       break;
2136
2137     default:
2138       arg->type = GTK_TYPE_INVALID;
2139       break;
2140     }
2141 }
2142
2143 static void
2144 gtk_text_view_size_request (GtkWidget      *widget,
2145                             GtkRequisition *requisition)
2146 {
2147   GtkTextView *text_view;
2148   GSList *tmp_list;
2149
2150   text_view = GTK_TEXT_VIEW (widget);
2151
2152   requisition->width = text_view->text_window->requisition.width + FOCUS_EDGE_WIDTH * 2;
2153   requisition->height = text_view->text_window->requisition.height + FOCUS_EDGE_WIDTH * 2;
2154
2155   if (text_view->left_window)
2156     requisition->width += text_view->left_window->requisition.width;
2157
2158   if (text_view->right_window)
2159     requisition->width += text_view->right_window->requisition.width;
2160
2161   if (text_view->top_window)
2162     requisition->height += text_view->top_window->requisition.height;
2163
2164   if (text_view->bottom_window)
2165     requisition->height += text_view->bottom_window->requisition.height;
2166
2167   tmp_list = text_view->children;
2168   while (tmp_list != NULL)
2169     {
2170       GtkTextViewChild *child = tmp_list->data;
2171
2172       if (child->anchor)
2173         {
2174           GtkRequisition child_req;
2175           GtkRequisition old_req;
2176
2177           old_req = child->widget->requisition;
2178
2179           gtk_widget_size_request (child->widget, &child_req);
2180
2181           if (text_view->layout &&
2182               (old_req.width != child_req.width ||
2183                old_req.height != child_req.height))
2184             gtk_text_child_anchor_queue_resize (child->anchor,
2185                                                 text_view->layout);
2186         }
2187       else
2188         {
2189
2190         }
2191
2192       tmp_list = g_slist_next (tmp_list);
2193     }
2194 }
2195
2196 static void
2197 gtk_text_view_update_child_allocation (GtkTextView      *text_view,
2198                                        GtkTextViewChild *vc)
2199 {
2200   gint buffer_y;
2201   GtkTextIter iter;
2202   GtkAllocation allocation;
2203   
2204   gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view),
2205                                             &iter,
2206                                             vc->anchor);
2207
2208   gtk_text_layout_get_line_yrange (text_view->layout, &iter,
2209                                    &buffer_y, NULL);
2210
2211   buffer_y += vc->from_top_of_line;
2212
2213   allocation.x = vc->from_left_of_buffer;
2214   allocation.y = buffer_y;
2215   allocation.width = vc->widget->requisition.width;
2216   allocation.height = vc->widget->requisition.height;
2217   
2218   gtk_widget_size_allocate (vc->widget, &allocation);
2219 }
2220
2221 static void
2222 gtk_text_view_child_allocated (GtkTextLayout *layout,
2223                                GtkWidget     *child,
2224                                gint           x,
2225                                gint           y,
2226                                gpointer       data)
2227 {
2228   GtkTextViewChild *vc = NULL;
2229   GtkTextView *text_view = data;
2230   
2231   /* x,y is the position of the child from the top of the line, and
2232    * from the left of the buffer. We have to translate that into text
2233    * window coordinates, then size_allocate the child.
2234    */
2235
2236   vc = g_object_get_data (G_OBJECT (child),
2237                             "gtk-text-view-child");
2238
2239   g_assert (vc != NULL);
2240
2241   g_print ("child allocated at %d,%d\n", x, y);
2242   
2243   vc->from_left_of_buffer = x;
2244   vc->from_top_of_line = y;
2245
2246   gtk_text_view_update_child_allocation (text_view, vc);
2247 }
2248
2249 static void
2250 gtk_text_view_validate_children (GtkTextView *text_view)
2251 {
2252   GSList *tmp_list;
2253
2254   DV(g_print(G_STRLOC"\n"));
2255   
2256   tmp_list = text_view->children;
2257   while (tmp_list != NULL)
2258     {
2259       GtkTextViewChild *child = tmp_list->data;
2260
2261       if (child->anchor)
2262         {
2263           /* We need to force-validate the regions containing
2264            * children.
2265            */
2266           GtkTextIter child_loc;
2267           gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view),
2268                                                     &child_loc,
2269                                                     child->anchor);
2270
2271           gtk_text_layout_validate_yrange (text_view->layout,
2272                                            &child_loc,
2273                                            0, 1);
2274         }
2275       else
2276         {
2277
2278         }
2279
2280       tmp_list = g_slist_next (tmp_list);
2281     }
2282 }
2283
2284 static void
2285 gtk_text_view_size_allocate (GtkWidget *widget,
2286                              GtkAllocation *allocation)
2287 {
2288   GtkTextView *text_view;
2289   GtkTextIter first_para;
2290   gint y;
2291   GtkAdjustment *vadj;
2292   gboolean yoffset_changed = FALSE;
2293   gint width, height;
2294   GdkRectangle text_rect;
2295   GdkRectangle left_rect;
2296   GdkRectangle right_rect;
2297   GdkRectangle top_rect;
2298   GdkRectangle bottom_rect;
2299
2300   text_view = GTK_TEXT_VIEW (widget);
2301
2302   DV(g_print(G_STRLOC"\n"));
2303   
2304   widget->allocation = *allocation;
2305
2306   if (GTK_WIDGET_REALIZED (widget))
2307     {
2308       gdk_window_move_resize (widget->window,
2309                               allocation->x, allocation->y,
2310                               allocation->width, allocation->height);
2311     }
2312
2313   /* distribute width/height among child windows. Ensure all
2314    * windows get at least a 1x1 allocation.
2315    */
2316
2317   width = allocation->width - FOCUS_EDGE_WIDTH * 2;
2318
2319   if (text_view->left_window)
2320     left_rect.width = text_view->left_window->requisition.width;
2321   else
2322     left_rect.width = 1;
2323
2324   width -= left_rect.width;
2325
2326   if (text_view->right_window)
2327     right_rect.width = text_view->right_window->requisition.width;
2328   else
2329     right_rect.width = 1;
2330
2331   width -= right_rect.width;
2332
2333   text_rect.width = MAX (1, width);
2334
2335   top_rect.width = text_rect.width;
2336   bottom_rect.width = text_rect.width;
2337
2338
2339   height = allocation->height - FOCUS_EDGE_WIDTH * 2;
2340
2341   if (text_view->top_window)
2342     top_rect.height = text_view->top_window->requisition.height;
2343   else
2344     top_rect.height = 1;
2345
2346   height -= top_rect.height;
2347
2348   if (text_view->bottom_window)
2349     bottom_rect.height = text_view->bottom_window->requisition.height;
2350   else
2351     bottom_rect.height = 1;
2352
2353   height -= bottom_rect.height;
2354
2355   text_rect.height = MAX (1, height);
2356
2357   left_rect.height = text_rect.height;
2358   right_rect.height = text_rect.height;
2359
2360   /* Origins */
2361   left_rect.x = FOCUS_EDGE_WIDTH;
2362   top_rect.y = FOCUS_EDGE_WIDTH;
2363
2364   text_rect.x = left_rect.x + left_rect.width;
2365   text_rect.y = top_rect.y + top_rect.height;
2366
2367   left_rect.y = text_rect.y;
2368   right_rect.y = text_rect.y;
2369
2370   top_rect.x = text_rect.x;
2371   bottom_rect.x = text_rect.x;
2372
2373   right_rect.x = text_rect.x + text_rect.width;
2374   bottom_rect.y = text_rect.y + text_rect.height;
2375
2376   text_window_size_allocate (text_view->text_window,
2377                              &text_rect);
2378
2379   if (text_view->left_window)
2380     text_window_size_allocate (text_view->left_window,
2381                                &left_rect);
2382
2383   if (text_view->right_window)
2384     text_window_size_allocate (text_view->right_window,
2385                                &right_rect);
2386
2387   if (text_view->top_window)
2388     text_window_size_allocate (text_view->top_window,
2389                                &top_rect);
2390
2391   if (text_view->bottom_window)
2392     text_window_size_allocate (text_view->bottom_window,
2393                                &bottom_rect);
2394
2395   gtk_text_view_update_layout_width (text_view);
2396   
2397   /* This is because validating children ends up size allocating them. */
2398   gtk_text_view_validate_children (text_view);
2399
2400   /* Now adjust the value of the adjustment to keep the cursor at the
2401    * same place in the buffer
2402    */
2403   gtk_text_view_get_first_para_iter (text_view, &first_para);
2404   gtk_text_layout_get_line_yrange (text_view->layout, &first_para, &y, NULL);
2405
2406   y += text_view->first_para_pixels;
2407
2408   /* Ensure h/v adj exist */
2409   get_hadjustment (text_view);
2410   get_vadjustment (text_view);
2411
2412   vadj = text_view->vadjustment;
2413   if (y > vadj->upper - vadj->page_size)
2414     y = MAX (0, vadj->upper - vadj->page_size);
2415
2416   if (y != text_view->yoffset)
2417     {
2418       vadj->value = text_view->yoffset = y;
2419       yoffset_changed = TRUE;
2420     }
2421
2422   text_view->hadjustment->page_size = SCREEN_WIDTH (text_view);
2423   text_view->hadjustment->page_increment = SCREEN_WIDTH (text_view) / 2;
2424   text_view->hadjustment->lower = 0;
2425   text_view->hadjustment->upper = MAX (SCREEN_WIDTH (text_view),
2426                                        text_view->width);
2427   gtk_signal_emit_by_name (GTK_OBJECT (text_view->hadjustment), "changed");
2428
2429   text_view->vadjustment->page_size = SCREEN_HEIGHT (text_view);
2430   text_view->vadjustment->page_increment = SCREEN_HEIGHT (text_view) / 2;
2431   text_view->vadjustment->lower = 0;
2432   text_view->vadjustment->upper = MAX (SCREEN_HEIGHT (text_view),
2433                                        text_view->height);
2434   gtk_signal_emit_by_name (GTK_OBJECT (text_view->vadjustment), "changed");
2435
2436   if (yoffset_changed)
2437     gtk_adjustment_value_changed (vadj);
2438
2439   if (text_view->first_validate_idle != 0)
2440     {
2441       /* The GTK resize loop processes all the pending exposes right
2442        * after doing the resize stuff, so the idle sizer won't have a
2443        * chance to run. So we do the work here. 
2444        */
2445
2446       g_source_remove (text_view->first_validate_idle);
2447       text_view->first_validate_idle = 0;
2448       
2449       if (!gtk_text_view_flush_scroll (text_view))
2450         gtk_text_view_validate_onscreen (text_view);
2451     }
2452 }
2453
2454 static void
2455 gtk_text_view_get_first_para_iter (GtkTextView *text_view,
2456                                    GtkTextIter *iter)
2457 {
2458   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), iter,
2459                                     text_view->first_para_mark);
2460 }
2461
2462 static void
2463 gtk_text_view_validate_onscreen (GtkTextView *text_view)
2464 {
2465   GtkWidget *widget = GTK_WIDGET (text_view);
2466   
2467   DV(g_print(">Validating onscreen ("G_STRLOC")\n"));
2468   
2469   if (SCREEN_HEIGHT (widget) > 0)
2470     {
2471       GtkTextIter first_para;
2472
2473       /* Be sure we've validated the stuff onscreen; if we
2474        * scrolled, these calls won't have any effect, because
2475        * they were called in the recursive validate_onscreen
2476        */
2477       gtk_text_view_get_first_para_iter (text_view, &first_para);
2478
2479       gtk_text_layout_validate_yrange (text_view->layout,
2480                                        &first_para,
2481                                        0,
2482                                        text_view->first_para_pixels +
2483                                        SCREEN_HEIGHT (widget));
2484     }
2485
2486   text_view->onscreen_validated = TRUE;
2487
2488   DV(g_print(">Done validating onscreen, onscreen_validated = TRUE ("G_STRLOC")\n"));
2489   
2490   /* This can have the odd side effect of triggering a scroll, which should
2491    * flip "onscreen_validated" back to FALSE, but should also get us
2492    * back into this function to turn it on again.
2493    */
2494   gtk_text_view_update_adjustments (text_view);
2495
2496   g_assert (text_view->onscreen_validated);
2497 }
2498
2499 static gboolean
2500 first_validate_callback (gpointer data)
2501 {
2502   GtkTextView *text_view = data;
2503
2504   /* Note that some of this code is duplicated at the end of size_allocate,
2505    * keep in sync with that.
2506    */
2507   
2508   DV(g_print(G_STRLOC"\n"));
2509
2510   /* Do this first, which means that if an "invalidate"
2511    * occurs during any of this process, a new first_validate_callback
2512    * will be installed, and we'll start again.
2513    */
2514   text_view->first_validate_idle = 0;
2515   
2516   /* be sure we have up-to-date screen size set on the
2517    * layout.
2518    */
2519   gtk_text_view_update_layout_width (text_view);
2520
2521   /* Bail out if we invalidated stuff; scrolling right away will just
2522    * confuse the issue.
2523    */
2524   if (text_view->first_validate_idle != 0)
2525     {
2526       DV(g_print(">Width change forced requeue ("G_STRLOC")\n"));
2527       return FALSE;
2528     }
2529   
2530   /* scroll to any marks, if that's pending. This can
2531    * jump us to the validation codepath used for scrolling
2532    * onscreen, if so we bail out.
2533    */
2534   if (!gtk_text_view_flush_scroll (text_view))
2535     gtk_text_view_validate_onscreen (text_view);
2536
2537   DV(g_print(">Leaving first validate idle ("G_STRLOC")\n"));
2538
2539   g_assert (text_view->onscreen_validated);
2540   
2541   return FALSE;
2542 }
2543
2544 static gboolean
2545 incremental_validate_callback (gpointer data)
2546 {
2547   GtkTextView *text_view = data;
2548
2549   DV(g_print(G_STRLOC"\n"));
2550   
2551   gtk_text_layout_validate (text_view->layout, 2000);
2552
2553   gtk_text_view_update_adjustments (text_view);
2554   
2555   if (gtk_text_layout_is_valid (text_view->layout))
2556     {
2557       text_view->incremental_validate_idle = 0;
2558       return FALSE;
2559     }
2560   else
2561     return TRUE;
2562 }
2563
2564 static void
2565 invalidated_handler (GtkTextLayout *layout,
2566                      gpointer       data)
2567 {
2568   GtkTextView *text_view;
2569
2570   text_view = GTK_TEXT_VIEW (data);
2571
2572   text_view->onscreen_validated = FALSE;
2573   
2574   DV(g_print(">Invalidate, onscreen_validated = FALSE ("G_STRLOC")\n"));
2575   
2576   if (!text_view->first_validate_idle)
2577     text_view->first_validate_idle = g_idle_add_full (GTK_PRIORITY_RESIZE - 1, first_validate_callback, text_view, NULL);
2578
2579   if (!text_view->incremental_validate_idle)
2580     text_view->incremental_validate_idle = g_idle_add_full (GTK_TEXT_VIEW_PRIORITY_VALIDATE, incremental_validate_callback, text_view, NULL);
2581 }
2582
2583 static void
2584 changed_handler (GtkTextLayout *layout,
2585                  gint           start_y,
2586                  gint           old_height,
2587                  gint           new_height,
2588                  gpointer       data)
2589 {
2590   GtkTextView *text_view;
2591   GtkWidget *widget;
2592   GdkRectangle visible_rect;
2593   GdkRectangle redraw_rect;
2594
2595   text_view = GTK_TEXT_VIEW (data);
2596   widget = GTK_WIDGET (data);
2597   
2598   DV(g_print(">Lines Validated ("G_STRLOC")\n"));
2599
2600   if (GTK_WIDGET_REALIZED (text_view))
2601     {      
2602       gtk_text_view_get_visible_rect (text_view, &visible_rect);
2603
2604       redraw_rect.x = visible_rect.x;
2605       redraw_rect.width = visible_rect.width;
2606       redraw_rect.y = start_y;
2607
2608       if (old_height == new_height)
2609         redraw_rect.height = old_height;
2610       else
2611         redraw_rect.height = MAX (0, visible_rect.y + visible_rect.height - start_y);
2612
2613       if (gdk_rectangle_intersect (&redraw_rect, &visible_rect, &redraw_rect))
2614         {
2615           /* text_window_invalidate_rect() takes buffer coordinates */
2616           text_window_invalidate_rect (text_view->text_window,
2617                                        &redraw_rect);
2618
2619           DV(g_print(" invalidated rect: %d,%d %d x %d\n",
2620                      redraw_rect.x,
2621                      redraw_rect.y,
2622                      redraw_rect.width,
2623                      redraw_rect.height));
2624           
2625           if (text_view->left_window)
2626             text_window_invalidate_rect (text_view->left_window,
2627                                          &redraw_rect);
2628           if (text_view->right_window)
2629             text_window_invalidate_rect (text_view->right_window,
2630                                          &redraw_rect);
2631           if (text_view->top_window)
2632             text_window_invalidate_rect (text_view->top_window,
2633                                          &redraw_rect);
2634           if (text_view->bottom_window)
2635             text_window_invalidate_rect (text_view->bottom_window,
2636                                          &redraw_rect);
2637         }
2638     }
2639   
2640   if (old_height != new_height)
2641     {
2642       gboolean yoffset_changed = FALSE;
2643       GSList *tmp_list;
2644       
2645       if (start_y + old_height <= text_view->yoffset - text_view->first_para_pixels)
2646         {
2647           text_view->yoffset += new_height - old_height;
2648           get_vadjustment (text_view)->value = text_view->yoffset;
2649           yoffset_changed = TRUE;
2650         }
2651
2652       if (yoffset_changed)
2653         gtk_adjustment_value_changed (get_vadjustment (text_view));
2654
2655       /* FIXME be smarter about which anchored widgets we update */
2656
2657       tmp_list = text_view->children;
2658       while (tmp_list != NULL)
2659         {
2660           GtkTextViewChild *child = tmp_list->data;
2661
2662           if (child->anchor)
2663             gtk_text_view_update_child_allocation (text_view, child);
2664
2665           tmp_list = g_slist_next (tmp_list);
2666         }
2667     }
2668 }
2669
2670 static void
2671 gtk_text_view_realize (GtkWidget *widget)
2672 {
2673   GtkTextView *text_view;
2674   GdkWindowAttr attributes;
2675   gint attributes_mask;
2676   
2677   text_view = GTK_TEXT_VIEW (widget);
2678   GTK_WIDGET_SET_FLAGS (text_view, GTK_REALIZED);
2679
2680   attributes.window_type = GDK_WINDOW_CHILD;
2681   attributes.x = widget->allocation.x;
2682   attributes.y = widget->allocation.y;
2683   attributes.width = widget->allocation.width;
2684   attributes.height = widget->allocation.height;
2685   attributes.wclass = GDK_INPUT_OUTPUT;
2686   attributes.visual = gtk_widget_get_visual (widget);
2687   attributes.colormap = gtk_widget_get_colormap (widget);
2688   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK;
2689
2690   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
2691
2692   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
2693                                    &attributes, attributes_mask);
2694   gdk_window_set_user_data (widget->window, widget);
2695
2696   /* must come before text_window_realize calls */
2697   widget->style = gtk_style_attach (widget->style, widget->window);
2698
2699   gdk_window_set_background (widget->window,
2700                              &widget->style->bg[GTK_WIDGET_STATE (widget)]);
2701
2702   text_window_realize (text_view->text_window, widget->window);
2703
2704   if (text_view->left_window)
2705     text_window_realize (text_view->left_window,
2706                          widget->window);
2707
2708   if (text_view->top_window)
2709     text_window_realize (text_view->top_window,
2710                          widget->window);
2711
2712   if (text_view->right_window)
2713     text_window_realize (text_view->right_window,
2714                          widget->window);
2715
2716   if (text_view->bottom_window)
2717     text_window_realize (text_view->bottom_window,
2718                          widget->window);
2719
2720   gtk_text_view_ensure_layout (text_view);
2721 }
2722
2723 static void
2724 gtk_text_view_unrealize (GtkWidget *widget)
2725 {
2726   GtkTextView *text_view;
2727   
2728   text_view = GTK_TEXT_VIEW (widget);
2729
2730   if (text_view->first_validate_idle)
2731     {
2732       g_source_remove (text_view->first_validate_idle);
2733       text_view->first_validate_idle = 0;
2734     }
2735
2736   if (text_view->incremental_validate_idle)
2737     {
2738       g_source_remove (text_view->incremental_validate_idle);
2739       text_view->incremental_validate_idle = 0;
2740     }
2741
2742   if (text_view->popup_menu)
2743     {
2744       gtk_widget_destroy (text_view->popup_menu);
2745       text_view->popup_menu = NULL;
2746     }
2747
2748   text_window_unrealize (text_view->text_window);
2749
2750   if (text_view->left_window)
2751     text_window_unrealize (text_view->left_window);
2752
2753   if (text_view->top_window)
2754     text_window_unrealize (text_view->top_window);
2755
2756   if (text_view->right_window)
2757     text_window_unrealize (text_view->right_window);
2758
2759   if (text_view->bottom_window)
2760     text_window_unrealize (text_view->bottom_window);
2761
2762   gtk_text_view_destroy_layout (text_view);
2763   
2764   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2765 }
2766
2767 static void
2768 gtk_text_view_style_set (GtkWidget *widget,
2769                          GtkStyle  *previous_style)
2770 {
2771   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
2772
2773   if (GTK_WIDGET_REALIZED (widget))
2774     {
2775       gdk_window_set_background (widget->window,
2776                                  &widget->style->bg[GTK_WIDGET_STATE (widget)]);
2777
2778       gdk_window_set_background (text_view->text_window->bin_window,
2779                                  &widget->style->base[GTK_WIDGET_STATE (widget)]);
2780
2781       if (text_view->left_window)
2782         gdk_window_set_background (text_view->left_window->bin_window,
2783                                    &widget->style->bg[GTK_WIDGET_STATE (widget)]);
2784       if (text_view->right_window)
2785         gdk_window_set_background (text_view->right_window->bin_window,
2786                                    &widget->style->bg[GTK_WIDGET_STATE (widget)]);
2787
2788       if (text_view->top_window)
2789         gdk_window_set_background (text_view->top_window->bin_window,
2790                                    &widget->style->bg[GTK_WIDGET_STATE (widget)]);
2791
2792       if (text_view->bottom_window)
2793         gdk_window_set_background (text_view->bottom_window->bin_window,
2794                                    &widget->style->bg[GTK_WIDGET_STATE (widget)]);
2795
2796       gtk_text_view_set_attributes_from_style (text_view,
2797                                                text_view->layout->default_style,
2798                                                widget->style);
2799       gtk_text_layout_default_style_changed (text_view->layout);
2800     }
2801 }
2802
2803 static void
2804 gtk_text_view_direction_changed (GtkWidget        *widget,
2805                                  GtkTextDirection  previous_direction)
2806 {
2807   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
2808
2809   if (text_view->layout)
2810     {
2811       text_view->layout->default_style->direction = gtk_widget_get_direction (widget);
2812       gtk_text_layout_default_style_changed (text_view->layout);
2813     }
2814 }
2815
2816 /*
2817  * Events
2818  */
2819
2820 static gboolean
2821 get_event_coordinates (GdkEvent *event, gint *x, gint *y)
2822 {
2823   if (event)
2824     switch (event->type)
2825       {
2826       case GDK_MOTION_NOTIFY:
2827         *x = event->motion.x;
2828         *y = event->motion.y;
2829         return TRUE;
2830         break;
2831
2832       case GDK_BUTTON_PRESS:
2833       case GDK_2BUTTON_PRESS:
2834       case GDK_3BUTTON_PRESS:
2835       case GDK_BUTTON_RELEASE:
2836         *x = event->button.x;
2837         *y = event->button.y;
2838         return TRUE;
2839         break;
2840
2841       case GDK_KEY_PRESS:
2842       case GDK_KEY_RELEASE:
2843       case GDK_ENTER_NOTIFY:
2844       case GDK_LEAVE_NOTIFY:
2845       case GDK_PROPERTY_NOTIFY:
2846       case GDK_SELECTION_CLEAR:
2847       case GDK_SELECTION_REQUEST:
2848       case GDK_SELECTION_NOTIFY:
2849       case GDK_PROXIMITY_IN:
2850       case GDK_PROXIMITY_OUT:
2851       case GDK_DRAG_ENTER:
2852       case GDK_DRAG_LEAVE:
2853       case GDK_DRAG_MOTION:
2854       case GDK_DRAG_STATUS:
2855       case GDK_DROP_START:
2856       case GDK_DROP_FINISHED:
2857       default:
2858         return FALSE;
2859         break;
2860       }
2861
2862   return FALSE;
2863 }
2864
2865 static gint
2866 emit_event_on_tags (GtkWidget   *widget,
2867                     GdkEvent    *event,
2868                     GtkTextIter *iter)
2869 {
2870   GSList *tags;
2871   GSList *tmp;
2872   gboolean retval = FALSE;
2873   GtkTextView *text_view;
2874
2875   text_view = GTK_TEXT_VIEW (widget);
2876
2877   tags = gtk_text_iter_get_tags (iter);
2878
2879   tmp = tags;
2880   while (tmp != NULL)
2881     {
2882       GtkTextTag *tag = tmp->data;
2883
2884       if (gtk_text_tag_event (tag, G_OBJECT (widget), event, iter))
2885         {
2886           retval = TRUE;
2887           break;
2888         }
2889
2890       tmp = g_slist_next (tmp);
2891     }
2892
2893   g_slist_free (tags);
2894
2895   return retval;
2896 }
2897
2898 static gint
2899 gtk_text_view_event (GtkWidget *widget, GdkEvent *event)
2900 {
2901   GtkTextView *text_view;
2902   gint x = 0, y = 0;
2903
2904   text_view = GTK_TEXT_VIEW (widget);
2905
2906   if (text_view->layout == NULL ||
2907       get_buffer (text_view) == NULL)
2908     return FALSE;
2909
2910   if (event->any.window != text_view->text_window->bin_window)
2911     return FALSE;
2912
2913   if (get_event_coordinates (event, &x, &y))
2914     {
2915       GtkTextIter iter;
2916
2917       x += text_view->xoffset;
2918       y += text_view->yoffset;
2919
2920       /* FIXME this is slow and we do it twice per event.
2921        * My favorite solution is to have GtkTextLayout cache
2922        * the last couple lookups.
2923        */
2924       gtk_text_layout_get_iter_at_pixel (text_view->layout,
2925                                          &iter,
2926                                          x, y);
2927
2928       return emit_event_on_tags (widget, event, &iter);
2929     }
2930   else if (event->type == GDK_KEY_PRESS ||
2931            event->type == GDK_KEY_RELEASE)
2932     {
2933       GtkTextMark *insert;
2934       GtkTextIter iter;
2935
2936       insert = gtk_text_buffer_get_mark (get_buffer (text_view),
2937                                          "insert");
2938
2939       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert);
2940
2941       return emit_event_on_tags (widget, event, &iter);
2942     }
2943   else
2944     return FALSE;
2945 }
2946
2947 static gint
2948 gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
2949 {
2950   gboolean retval = FALSE;
2951   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
2952
2953   if (text_view->layout == NULL ||
2954       get_buffer (text_view) == NULL)
2955     return FALSE;
2956
2957   if (gtk_im_context_filter_keypress (text_view->im_context, event))
2958     {
2959       text_view->need_im_reset = TRUE;
2960       retval = TRUE;
2961     }
2962   else if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
2963            GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
2964     retval = TRUE;
2965   else if (event->keyval == GDK_Return ||
2966            event->keyval == GDK_KP_Enter)
2967     {
2968       gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
2969                                                     text_view->editable);
2970       gtk_text_view_scroll_to_mark (text_view,
2971                                     gtk_text_buffer_get_mark (get_buffer (text_view),
2972                                                               "insert"),
2973                                     0.0, FALSE, 0.0, 0.0);
2974       retval = TRUE;
2975     }
2976   /* Pass through Tab as literal tab, unless Control is held down */
2977   else if (event->keyval == GDK_Tab && !(event->state & GDK_CONTROL_MASK))
2978     {
2979       gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\t", 1,
2980                                                     text_view->editable);
2981       gtk_text_view_scroll_mark_onscreen (text_view,
2982                                           gtk_text_buffer_get_mark (get_buffer (text_view),
2983                                                                     "insert"));
2984       retval = TRUE;
2985     }
2986   else
2987     retval = FALSE;
2988
2989   gtk_text_view_start_cursor_blink (text_view, TRUE);
2990
2991   return retval;
2992 }
2993
2994 static gint
2995 gtk_text_view_key_release_event (GtkWidget *widget, GdkEventKey *event)
2996 {
2997   return FALSE;
2998 }
2999
3000 static gint
3001 gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
3002 {
3003   GtkTextView *text_view;
3004
3005   text_view = GTK_TEXT_VIEW (widget);
3006
3007   gtk_widget_grab_focus (widget);
3008
3009   if (event->window != text_view->text_window->bin_window)
3010     {
3011       /* Remove selection if any. */
3012       gtk_text_view_unselect (text_view);
3013       return FALSE;
3014     }
3015
3016 #if 0
3017   /* debug hack */
3018   if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0)
3019     _gtk_text_buffer_spew (GTK_TEXT_VIEW (widget)->buffer);
3020   else if (event->button == 3)
3021     gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout);
3022 #endif
3023
3024   if (event->type == GDK_BUTTON_PRESS)
3025     {
3026       gtk_text_view_reset_im_context (text_view);
3027
3028       if (event->button == 1)
3029         {
3030           /* If we're in the selection, start a drag copy/move of the
3031            * selection; otherwise, start creating a new selection.
3032            */
3033           GtkTextIter iter;
3034           GtkTextIter start, end;
3035
3036           gtk_text_layout_get_iter_at_pixel (text_view->layout,
3037                                              &iter,
3038                                              event->x + text_view->xoffset,
3039                                              event->y + text_view->yoffset);
3040
3041           if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
3042                                                     &start, &end) &&
3043               gtk_text_iter_in_range (&iter, &start, &end))
3044             {
3045               text_view->drag_start_x = event->x;
3046               text_view->drag_start_y = event->y;
3047             }
3048           else
3049             {
3050               gtk_text_view_start_selection_drag (text_view, &iter, event);
3051             }
3052
3053           return TRUE;
3054         }
3055       else if (event->button == 2)
3056         {
3057           GtkTextIter iter;
3058
3059           gtk_text_layout_get_iter_at_pixel (text_view->layout,
3060                                              &iter,
3061                                              event->x + text_view->xoffset,
3062                                              event->y + text_view->yoffset);
3063
3064           gtk_text_buffer_paste_primary (get_buffer (text_view),
3065                                          &iter,
3066                                          text_view->editable);
3067           return TRUE;
3068         }
3069       else if (event->button == 3)
3070         {
3071           gtk_text_view_do_popup (text_view, event);
3072         }
3073     }
3074   else if ((event->type == GDK_2BUTTON_PRESS ||
3075             event->type == GDK_3BUTTON_PRESS) &&
3076            event->button == 1)
3077     {
3078       GtkTextIter start, end;
3079
3080       /* End the selection drag, otherwise we'd clear the new
3081        * word/line selection on button release
3082        */
3083       gtk_text_view_end_selection_drag (text_view, event);
3084
3085       gtk_text_layout_get_iter_at_pixel (text_view->layout,
3086                                          &start,
3087                                          event->x + text_view->xoffset,
3088                                          event->y + text_view->yoffset); 
3089
3090       end = start;
3091       
3092       if (event->type == GDK_2BUTTON_PRESS)
3093         {
3094           if (gtk_text_iter_inside_word (&start))
3095             {
3096               if (!gtk_text_iter_starts_word (&start))
3097                 gtk_text_iter_backward_word_start (&start);
3098               
3099               if (!gtk_text_iter_ends_word (&end))
3100                 gtk_text_iter_forward_word_end (&end);
3101             }
3102         }
3103       else if (event->type == GDK_3BUTTON_PRESS)
3104         {
3105           if (gtk_text_view_starts_display_line (text_view, &start))
3106             {
3107               /* If on a display line boundary, we assume the user
3108                * clicked off the end of a line and we therefore select
3109                * the line before the boundary.
3110                */
3111               gtk_text_view_backward_display_line_start (text_view, &start);
3112             }
3113           else
3114             {
3115               /* start isn't on the start of a line, so we move it to the
3116                * start, and move end to the end unless it's already there.
3117                */
3118               gtk_text_view_backward_display_line_start (text_view, &start);
3119
3120               if (!gtk_text_view_starts_display_line (text_view, &end))
3121                 gtk_text_view_forward_display_line_end (text_view, &end);
3122             }
3123         }
3124
3125       gtk_text_buffer_move_mark (get_buffer (text_view),
3126                                  gtk_text_buffer_get_selection_bound (get_buffer (text_view)),
3127                                  &start);
3128       gtk_text_buffer_move_mark (get_buffer (text_view),
3129                                  gtk_text_buffer_get_insert (get_buffer (text_view)),
3130                                  &end);
3131
3132       text_view->just_selected_element = TRUE;
3133       
3134       return TRUE;
3135     }
3136   
3137   return FALSE;
3138 }
3139
3140 static gint
3141 gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
3142 {
3143   GtkTextView *text_view;
3144
3145   text_view = GTK_TEXT_VIEW (widget);
3146
3147   if (event->window != text_view->text_window->bin_window)
3148     return FALSE;
3149
3150   if (event->button == 1)
3151     {
3152       if (text_view->drag_start_x >= 0)
3153         {
3154           text_view->drag_start_x = -1;
3155           text_view->drag_start_y = -1;
3156         }
3157
3158       if (gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget), event))
3159         return TRUE;
3160       else if (text_view->just_selected_element)
3161         {
3162           text_view->just_selected_element = FALSE;
3163           return FALSE;
3164         }
3165       else
3166         {
3167           GtkTextIter iter;
3168
3169           /* Unselect everything; probably we were dragging, or clicked
3170            * without dragging to remove selection.
3171            */
3172           gtk_text_layout_get_iter_at_pixel (text_view->layout,
3173                                              &iter,
3174                                              event->x + text_view->xoffset,
3175                                              event->y + text_view->yoffset);
3176
3177           gtk_text_buffer_place_cursor (get_buffer (text_view), &iter);
3178   
3179           return FALSE;
3180         }
3181     }
3182
3183   return FALSE;
3184 }
3185
3186 static gint
3187 gtk_text_view_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
3188 {
3189   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
3190
3191   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
3192   gtk_widget_queue_draw (widget);
3193
3194   if (text_view->cursor_visible && text_view->layout)
3195     {
3196       gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);
3197       gtk_text_view_start_cursor_blink (text_view, FALSE);
3198     }
3199
3200   text_view->need_im_reset = TRUE;
3201   gtk_im_context_focus_in (GTK_TEXT_VIEW (widget)->im_context);
3202
3203   return FALSE;
3204 }
3205
3206 static gint
3207 gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
3208 {
3209   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
3210
3211   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
3212   gtk_widget_queue_draw (widget);
3213
3214   if (text_view->cursor_visible && text_view->layout)
3215     {
3216       gtk_text_layout_set_cursor_visible (text_view->layout, FALSE);
3217       gtk_text_view_stop_cursor_blink (text_view);
3218     }
3219
3220   text_view->need_im_reset = TRUE;
3221   gtk_im_context_focus_out (GTK_TEXT_VIEW (widget)->im_context);
3222
3223   return FALSE;
3224 }
3225
3226 static gint
3227 gtk_text_view_motion_event (GtkWidget *widget, GdkEventMotion *event)
3228 {
3229   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
3230
3231   if (event->window == text_view->text_window->bin_window &&
3232       text_view->drag_start_x >= 0)
3233     {
3234       gint x, y;
3235
3236       gdk_window_get_pointer (text_view->text_window->bin_window,
3237                               &x, &y, NULL);
3238
3239       if (gtk_drag_check_threshold (widget,
3240                                     text_view->drag_start_x, 
3241                                     text_view->drag_start_y,
3242                                     x, y))
3243         {
3244           GtkTextIter iter;
3245           gint buffer_x, buffer_y;
3246
3247           gtk_text_view_window_to_buffer_coords (text_view,
3248                                                  GTK_TEXT_WINDOW_TEXT,
3249                                                  text_view->drag_start_x,
3250                                                  text_view->drag_start_y,
3251                                                  &buffer_x,
3252                                                  &buffer_y);
3253
3254           gtk_text_layout_get_iter_at_pixel (text_view->layout,
3255                                              &iter,
3256                                              buffer_x, buffer_y);
3257
3258           gtk_text_view_start_selection_dnd (text_view, &iter, event);
3259           return TRUE;
3260         }
3261     }
3262
3263   return FALSE;
3264 }
3265
3266 static void
3267 gtk_text_view_paint (GtkWidget *widget, GdkRectangle *area)
3268 {
3269   GtkTextView *text_view;
3270
3271   text_view = GTK_TEXT_VIEW (widget);
3272
3273   g_return_if_fail (text_view->layout != NULL);
3274   g_return_if_fail (text_view->xoffset >= 0);
3275   g_return_if_fail (text_view->yoffset >= 0);
3276   
3277   if (!text_view->onscreen_validated)
3278     {
3279       G_BREAKPOINT ();
3280       g_warning (G_STRLOC ": somehow some text lines were modified or scrolling occurred since the last validation of lines on the screen");
3281     }
3282   
3283 #if 0
3284   printf ("painting %d,%d  %d x %d\n",
3285           area->x, area->y,
3286           area->width, area->height);
3287 #endif
3288   
3289   gtk_text_layout_draw (text_view->layout,
3290                         widget,
3291                         text_view->text_window->bin_window,
3292                         text_view->xoffset,
3293                         text_view->yoffset,
3294                         area->x, area->y,
3295                         area->width, area->height);
3296 }
3297
3298 static gint
3299 gtk_text_view_expose_event (GtkWidget *widget, GdkEventExpose *event)
3300 {
3301 #if 0
3302   {
3303     GdkWindow *win = event->window;
3304     GdkColor color = { 0, 0, 0, 65535 };
3305     GdkGC *gc = gdk_gc_new (win);
3306     gdk_gc_set_rgb_fg_color (gc, &color);
3307     gdk_draw_rectangle (win,
3308                         gc, TRUE,
3309                         event->area.x, event->area.y,
3310                         event->area.width, event->area.height);
3311     gdk_gc_unref (gc);
3312   }
3313 #endif
3314   
3315   if (event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (widget),
3316                                                  GTK_TEXT_WINDOW_TEXT))
3317     {
3318       DV(g_print (">Exposed ("G_STRLOC")\n"));
3319       gtk_text_view_paint (widget, &event->area);
3320     }
3321
3322   if (event->window == widget->window)
3323     gtk_text_view_draw_focus (widget);
3324
3325   return TRUE;
3326 }
3327
3328 static void
3329 gtk_text_view_draw_focus (GtkWidget *widget)
3330 {
3331   if (GTK_WIDGET_DRAWABLE (widget))
3332     {
3333       if (GTK_WIDGET_HAS_FOCUS (widget))
3334         {
3335           gtk_paint_focus (widget->style, widget->window,
3336                            NULL, widget, "textview",
3337                            0, 0,
3338                            widget->allocation.width - 1,
3339                            widget->allocation.height - 1);
3340         }
3341       else
3342         {
3343           gdk_window_clear (widget->window);
3344         }
3345     }
3346 }
3347
3348 /*
3349  * Container
3350  */
3351
3352 static void
3353 gtk_text_view_add (GtkContainer *container,
3354                    GtkWidget    *child)
3355 {
3356   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
3357   g_return_if_fail (GTK_IS_WIDGET (child));
3358
3359   /* This is pretty random. */
3360   gtk_text_view_add_child_in_window (GTK_TEXT_VIEW (container),
3361                                      child,
3362                                      GTK_TEXT_WINDOW_WIDGET,
3363                                      0, 0);
3364 }
3365
3366 static void
3367 gtk_text_view_remove (GtkContainer *container,
3368                       GtkWidget    *child)
3369 {
3370   GSList *iter;
3371   GtkTextView *text_view;
3372   GtkTextViewChild *vc;
3373
3374   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
3375   g_return_if_fail (GTK_IS_WIDGET (child));
3376   g_return_if_fail (child->parent == (GtkWidget*) container);
3377
3378   text_view = GTK_TEXT_VIEW (container);
3379
3380   vc = NULL;
3381   iter = text_view->children;
3382
3383   while (iter != NULL)
3384     {
3385       vc = iter->data;
3386
3387       if (vc->widget == child)
3388         break;
3389
3390       iter = g_slist_next (iter);
3391     }
3392
3393   g_assert (iter != NULL); /* be sure we had the child in the list */
3394
3395   text_view->children = g_slist_remove (text_view->children, vc);
3396
3397   gtk_widget_unparent (vc->widget);
3398
3399   text_view_child_free (vc);
3400 }
3401
3402 static void
3403 gtk_text_view_forall (GtkContainer *container,
3404                       gboolean      include_internals,
3405                       GtkCallback   callback,
3406                       gpointer      callback_data)
3407 {
3408   GSList *iter;
3409   GtkTextView *text_view;
3410
3411   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
3412   g_return_if_fail (callback != NULL);
3413
3414   text_view = GTK_TEXT_VIEW (container);
3415
3416   iter = text_view->children;
3417
3418   while (iter != NULL)
3419     {
3420       GtkTextViewChild *vc = iter->data;
3421
3422       (* callback) (vc->widget, callback_data);
3423
3424       iter = g_slist_next (iter);
3425     }
3426 }
3427
3428 /* Note that CURSOR_ON_TIME is effectively added to PREBLINK_TIME
3429  * because blinking starts with the cursor turned on.
3430  */
3431 #define PREBLINK_TIME 300
3432 #define CURSOR_ON_TIME 800
3433 #define CURSOR_OFF_TIME 400
3434
3435 /*
3436  * preblink!
3437  */
3438
3439 static gint
3440 preblink_cb (gpointer data)
3441 {
3442   GtkTextView *text_view = GTK_TEXT_VIEW (data);
3443
3444   text_view->preblink_timeout = 0;
3445   gtk_text_view_start_cursor_blink (text_view, FALSE);
3446
3447   /* Remove ourselves */
3448   return FALSE;
3449 }
3450
3451 /*
3452  * Blink!
3453  */
3454
3455 static gint
3456 blink_cb (gpointer data)
3457 {
3458   GtkTextView *text_view = GTK_TEXT_VIEW (data);
3459   gboolean visible;
3460   
3461   g_assert (text_view->layout);
3462   g_assert (GTK_WIDGET_HAS_FOCUS (text_view));
3463   g_assert (text_view->cursor_visible);
3464
3465   visible = gtk_text_layout_get_cursor_visible (text_view->layout);
3466
3467   if (visible)
3468     text_view->blink_timeout = gtk_timeout_add (CURSOR_OFF_TIME,
3469                                                 blink_cb,
3470                                                 text_view);
3471   else
3472     text_view->blink_timeout = gtk_timeout_add (CURSOR_ON_TIME,
3473                                                 blink_cb,
3474                                                 text_view);
3475   
3476   gtk_text_layout_set_cursor_visible (text_view->layout,
3477                                       !visible);
3478
3479   /* Remove ourselves */
3480   return FALSE;
3481 }
3482
3483 static void
3484 gtk_text_view_start_cursor_blink(GtkTextView *text_view,
3485                                  gboolean     with_delay)
3486 {
3487 #ifdef DEBUG_VALIDATION_AND_SCROLLING
3488   return;
3489 #endif
3490
3491   if (text_view->layout == NULL)
3492     return;
3493   
3494   if (!text_view->cursor_visible)
3495     return;
3496
3497   if (!GTK_WIDGET_HAS_FOCUS (text_view))
3498     return;
3499   
3500   if (text_view->preblink_timeout != 0)
3501     {
3502       gtk_timeout_remove (text_view->preblink_timeout);
3503       text_view->preblink_timeout = 0;
3504     }
3505   
3506   if (with_delay)
3507     {
3508       if (text_view->blink_timeout != 0)
3509         {
3510           gtk_timeout_remove (text_view->blink_timeout);
3511           text_view->blink_timeout = 0;
3512         }
3513       
3514       gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);
3515       
3516       text_view->preblink_timeout = gtk_timeout_add (PREBLINK_TIME,
3517                                                      preblink_cb,
3518                                                      text_view);
3519     }
3520   else
3521     {
3522       if (text_view->blink_timeout == 0)
3523         {
3524           gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);
3525           
3526           text_view->blink_timeout = gtk_timeout_add (CURSOR_ON_TIME,
3527                                                       blink_cb,
3528                                                       text_view);
3529         }
3530     }
3531 }
3532
3533 static void
3534 gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
3535 {
3536   if (text_view->preblink_timeout)
3537     {
3538       gtk_timeout_remove (text_view->preblink_timeout);
3539       text_view->preblink_timeout = 0;
3540     }
3541
3542   if (text_view->blink_timeout)  
3543     { 
3544       gtk_timeout_remove (text_view->blink_timeout);
3545       text_view->blink_timeout = 0;
3546     }
3547 }
3548
3549 /*
3550  * Key binding handlers
3551  */
3552
3553 static void
3554 gtk_text_view_move_iter_by_lines (GtkTextView *text_view,
3555                                   GtkTextIter *newplace,
3556                                   gint         count)
3557 {
3558   while (count < 0)
3559     {
3560       gtk_text_layout_move_iter_to_previous_line (text_view->layout, newplace);
3561       count++;
3562     }
3563
3564   while (count > 0)
3565     {
3566       gtk_text_layout_move_iter_to_next_line (text_view->layout, newplace);
3567       count--;
3568     }
3569 }
3570
3571 static void
3572 gtk_text_view_move_cursor (GtkTextView     *text_view,
3573                            GtkMovementStep  step,
3574                            gint             count,
3575                            gboolean         extend_selection)
3576 {
3577   GtkTextIter insert;
3578   GtkTextIter newplace;
3579
3580   gint cursor_x_pos = 0;
3581
3582   gtk_text_view_reset_im_context (text_view);
3583
3584   if (step == GTK_MOVEMENT_PAGES)
3585     {
3586       gtk_text_view_scroll_pages (text_view, count);
3587       gtk_text_view_start_cursor_blink (text_view, TRUE);
3588       return;
3589     }
3590
3591   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
3592                                     gtk_text_buffer_get_mark (get_buffer (text_view),
3593                                                               "insert"));
3594   newplace = insert;
3595
3596   if (step == GTK_MOVEMENT_DISPLAY_LINES)
3597     gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, NULL);
3598
3599   switch (step)
3600     {
3601     case GTK_MOVEMENT_LOGICAL_POSITIONS:
3602       gtk_text_iter_forward_cursor_positions (&newplace, count);
3603       break;
3604
3605     case GTK_MOVEMENT_VISUAL_POSITIONS:
3606       gtk_text_layout_move_iter_visually (text_view->layout,
3607                                           &newplace, count);
3608       break;
3609
3610     case GTK_MOVEMENT_WORDS:
3611       if (count < 0)
3612         gtk_text_iter_backward_word_starts (&newplace, -count);
3613       else if (count > 0)
3614         gtk_text_iter_forward_word_ends (&newplace, count);
3615       break;
3616
3617     case GTK_MOVEMENT_DISPLAY_LINES:
3618       gtk_text_view_move_iter_by_lines (text_view, &newplace, count);
3619       gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
3620       break;
3621
3622     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
3623       if (count > 1)
3624         gtk_text_view_move_iter_by_lines (text_view, &newplace, --count);
3625       else if (count < -1)
3626         gtk_text_view_move_iter_by_lines (text_view, &newplace, ++count);
3627
3628       if (count != 0)
3629         gtk_text_layout_move_iter_to_line_end (text_view->layout, &newplace, count);
3630       break;
3631
3632     case GTK_MOVEMENT_PARAGRAPHS:
3633       /* This should almost certainly instead be doing the parallel thing to WORD */
3634       /*       gtk_text_iter_down_lines (&newplace, count); */
3635       /* FIXME */
3636       break;
3637
3638     case GTK_MOVEMENT_PARAGRAPH_ENDS:
3639       if (count > 0)
3640         gtk_text_iter_forward_to_line_end (&newplace);
3641       else if (count < 0)
3642         gtk_text_iter_set_line_offset (&newplace, 0);
3643       break;
3644
3645     case GTK_MOVEMENT_BUFFER_ENDS:
3646       if (count > 0)
3647         gtk_text_buffer_get_end_iter (get_buffer (text_view), &newplace);
3648       else if (count < 0)
3649         gtk_text_buffer_get_iter_at_offset (get_buffer (text_view), &newplace, 0);
3650       break;
3651
3652     default:
3653       break;
3654     }
3655
3656   if (!gtk_text_iter_equal (&insert, &newplace))
3657     {
3658       if (extend_selection)
3659         gtk_text_buffer_move_mark (get_buffer (text_view),
3660                                    gtk_text_buffer_get_mark (get_buffer (text_view),
3661                                                              "insert"),
3662                                    &newplace);
3663       else
3664         gtk_text_buffer_place_cursor (get_buffer (text_view), &newplace);
3665
3666       gtk_text_view_scroll_mark_onscreen (text_view,
3667                                           gtk_text_buffer_get_mark (get_buffer (text_view),
3668                                                                     "insert"));
3669
3670       if (step == GTK_MOVEMENT_DISPLAY_LINES)
3671         {
3672           gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
3673         }
3674     }
3675
3676   gtk_text_view_start_cursor_blink (text_view, TRUE);
3677 }
3678
3679 static void
3680 gtk_text_view_set_anchor (GtkTextView *text_view)
3681 {
3682   GtkTextIter insert;
3683
3684   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
3685                                     gtk_text_buffer_get_mark (get_buffer (text_view),
3686                                                               "insert"));
3687
3688   gtk_text_buffer_create_mark (get_buffer (text_view), "anchor", &insert, TRUE);
3689 }
3690
3691 static void
3692 gtk_text_view_scroll_pages (GtkTextView *text_view,
3693                             gint         count)
3694 {
3695   gdouble newval;
3696   GtkAdjustment *adj;
3697   gint cursor_x_pos, cursor_y_pos;
3698   GtkTextIter new_insert;
3699   GtkTextIter anchor;
3700   gint y0, y1;
3701
3702   g_return_if_fail (text_view->vadjustment != NULL);
3703
3704   adj = text_view->vadjustment;
3705
3706   /* Validate the region that will be brought into view by the cursor motion
3707    */
3708   if (count < 0)
3709     {
3710       gtk_text_view_get_first_para_iter (text_view, &anchor);
3711       y0 = adj->page_size;
3712       y1 = adj->page_size + count * adj->page_increment;
3713     }
3714   else
3715     {
3716       gtk_text_view_get_first_para_iter (text_view, &anchor);
3717       y0 = count * adj->page_increment + adj->page_size;
3718       y1 = 0;
3719     }
3720
3721   gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1);
3722
3723   gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
3724
3725   newval = adj->value;
3726
3727   newval += count * adj->page_increment;
3728
3729   cursor_y_pos += newval - adj->value;
3730   set_adjustment_clamped (adj, newval);
3731
3732   gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos);
3733   clamp_iter_onscreen (text_view, &new_insert);
3734   gtk_text_buffer_place_cursor (get_buffer (text_view), &new_insert);
3735
3736   gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
3737
3738   /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
3739    * only guarantees 1 pixel onscreen.
3740    */
3741   gtk_text_view_scroll_mark_onscreen (text_view,
3742                                       gtk_text_buffer_get_mark (get_buffer (text_view),
3743                                                                 "insert"));
3744 }
3745
3746 static gboolean
3747 whitespace (gunichar ch, gpointer user_data)
3748 {
3749   return (ch == ' ' || ch == '\t');
3750 }
3751
3752 static gboolean
3753 not_whitespace (gunichar ch, gpointer user_data)
3754 {
3755   return !whitespace (ch, user_data);
3756 }
3757
3758 static gboolean
3759 find_whitepace_region (const GtkTextIter *center,
3760                        GtkTextIter *start, GtkTextIter *end)
3761 {
3762   *start = *center;
3763   *end = *center;
3764
3765   if (gtk_text_iter_backward_find_char (start, not_whitespace, NULL, NULL))
3766     gtk_text_iter_forward_char (start); /* we want the first whitespace... */
3767   if (whitespace (gtk_text_iter_get_char (end), NULL))
3768     gtk_text_iter_forward_find_char (end, not_whitespace, NULL, NULL);
3769
3770   return !gtk_text_iter_equal (start, end);
3771 }
3772
3773 static void
3774 gtk_text_view_insert_at_cursor (GtkTextView *text_view,
3775                                 const gchar *str)
3776 {
3777   gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
3778                                                 text_view->editable);
3779 }
3780
3781 static void
3782 gtk_text_view_delete_from_cursor (GtkTextView   *text_view,
3783                                   GtkDeleteType  type,
3784                                   gint           count)
3785 {
3786   GtkTextIter insert;
3787   GtkTextIter start;
3788   GtkTextIter end;
3789   gboolean leave_one = FALSE;
3790
3791   gtk_text_view_reset_im_context (text_view);
3792
3793   if (type == GTK_DELETE_CHARS)
3794     {
3795       /* Char delete deletes the selection, if one exists */
3796       if (gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
3797                                             text_view->editable))
3798         return;
3799     }
3800
3801   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
3802                                     &insert,
3803                                     gtk_text_buffer_get_mark (get_buffer (text_view),
3804                                                               "insert"));
3805
3806   start = insert;
3807   end = insert;
3808
3809   switch (type)
3810     {
3811     case GTK_DELETE_CHARS:
3812       gtk_text_iter_forward_cursor_positions (&end, count);
3813       break;
3814
3815     case GTK_DELETE_WORD_ENDS:
3816       if (count > 0)
3817         gtk_text_iter_forward_word_ends (&end, count);
3818       else if (count < 0)
3819         gtk_text_iter_backward_word_starts (&start, 0 - count);
3820       break;
3821
3822     case GTK_DELETE_WORDS:
3823       break;
3824
3825     case GTK_DELETE_DISPLAY_LINE_ENDS:
3826       break;
3827
3828     case GTK_DELETE_DISPLAY_LINES:
3829       break;
3830
3831     case GTK_DELETE_PARAGRAPH_ENDS:
3832       /* If we're already at a newline, we need to
3833        * simply delete that newline, instead of
3834        * moving to the next one.
3835        */
3836       if (gtk_text_iter_ends_line (&end))
3837         {
3838           gtk_text_iter_forward_line (&end);
3839           --count;
3840         }
3841
3842       while (count > 0)
3843         {
3844           if (!gtk_text_iter_forward_to_line_end (&end))
3845             break;
3846
3847           --count;
3848         }
3849
3850       /* FIXME figure out what a negative count means
3851          and support that */
3852       break;
3853
3854     case GTK_DELETE_PARAGRAPHS:
3855       if (count > 0)
3856         {
3857           gtk_text_iter_set_line_offset (&start, 0);
3858           gtk_text_iter_forward_to_line_end (&end);
3859
3860           /* Do the lines beyond the first. */
3861           while (count > 1)
3862             {
3863               gtk_text_iter_forward_to_line_end (&end);
3864
3865               --count;
3866             }
3867         }
3868
3869       /* FIXME negative count? */
3870
3871       break;
3872
3873     case GTK_DELETE_WHITESPACE:
3874       {
3875         find_whitepace_region (&insert, &start, &end);
3876       }
3877       break;
3878
3879     default:
3880       break;
3881     }
3882
3883   if (!gtk_text_iter_equal (&start, &end))
3884     {
3885       gtk_text_buffer_begin_user_action (get_buffer (text_view));
3886
3887       if (gtk_text_buffer_delete_interactive (get_buffer (text_view), &start, &end,
3888                                               text_view->editable))
3889         {
3890           if (leave_one)
3891             gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view),
3892                                                           " ", 1,
3893                                                           text_view->editable);
3894         }
3895
3896       gtk_text_buffer_end_user_action (get_buffer (text_view));
3897       
3898       gtk_text_view_scroll_mark_onscreen (text_view,
3899                                           gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
3900     }
3901 }
3902
3903 static void
3904 gtk_text_view_cut_clipboard (GtkTextView *text_view)
3905 {
3906   gtk_text_buffer_cut_clipboard (get_buffer (text_view), text_view->editable);
3907   gtk_text_view_scroll_mark_onscreen (text_view,
3908                                       gtk_text_buffer_get_mark (get_buffer (text_view),
3909                                                                 "insert"));
3910 }
3911
3912 static void
3913 gtk_text_view_copy_clipboard (GtkTextView *text_view)
3914 {
3915   gtk_text_buffer_copy_clipboard (get_buffer (text_view));
3916   gtk_text_view_scroll_mark_onscreen (text_view,
3917                                       gtk_text_buffer_get_mark (get_buffer (text_view),
3918                                                                 "insert"));
3919 }
3920
3921 static void
3922 gtk_text_view_paste_clipboard (GtkTextView *text_view)
3923 {
3924   gtk_text_buffer_paste_clipboard (get_buffer (text_view), text_view->editable);
3925   gtk_text_view_scroll_mark_onscreen (text_view,
3926                                       gtk_text_buffer_get_mark (get_buffer (text_view),
3927                                                                 "insert"));
3928 }
3929
3930 static void
3931 gtk_text_view_toggle_overwrite (GtkTextView *text_view)
3932 {
3933   text_view->overwrite_mode = !text_view->overwrite_mode;
3934 }
3935
3936 /*
3937  * Selections
3938  */
3939
3940 static void
3941 gtk_text_view_unselect (GtkTextView *text_view)
3942 {
3943   GtkTextIter insert;
3944
3945   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
3946                                     &insert,
3947                                     gtk_text_buffer_get_mark (get_buffer (text_view),
3948                                                               "insert"));
3949
3950   gtk_text_buffer_move_mark (get_buffer (text_view),
3951                              gtk_text_buffer_get_mark (get_buffer (text_view),
3952                                                        "selection_bound"),
3953                              &insert);
3954 }
3955
3956 static void
3957 move_mark_to_pointer_and_scroll (GtkTextView *text_view,
3958                                  const gchar *mark_name)
3959 {
3960   gint x, y;
3961   GdkModifierType state;
3962   GtkTextIter newplace;
3963
3964   gdk_window_get_pointer (text_view->text_window->bin_window,
3965                           &x, &y, &state);
3966   
3967   gtk_text_layout_get_iter_at_pixel (text_view->layout,
3968                                      &newplace,
3969                                      x + text_view->xoffset,
3970                                      y + text_view->yoffset);
3971
3972   {
3973     GtkTextMark *mark =
3974       gtk_text_buffer_get_mark (get_buffer (text_view), mark_name);
3975
3976     gtk_text_buffer_move_mark (get_buffer (text_view),
3977                                mark,
3978                                &newplace);
3979
3980     gtk_text_view_scroll_mark_onscreen (text_view, mark);
3981   }
3982 }
3983
3984 static gint
3985 selection_scan_timeout (gpointer data)
3986 {
3987   GtkTextView *text_view;
3988
3989   text_view = GTK_TEXT_VIEW (data);
3990
3991   move_mark_to_pointer_and_scroll (text_view, "insert");
3992
3993   return TRUE; /* remain installed. */
3994 }
3995
3996 #define DND_SCROLL_MARGIN 0.20
3997
3998 static gint
3999 drag_scan_timeout (gpointer data)
4000 {
4001   GtkTextView *text_view;
4002   gint x, y;
4003   GdkModifierType state;
4004   GtkTextIter newplace;
4005   
4006   text_view = GTK_TEXT_VIEW (data);
4007
4008   gdk_window_get_pointer (text_view->text_window->bin_window,
4009                           &x, &y, &state);
4010   
4011   gtk_text_layout_get_iter_at_pixel (text_view->layout,
4012                                      &newplace,
4013                                      x + text_view->xoffset,
4014                                      y + text_view->yoffset);
4015   
4016   gtk_text_buffer_move_mark (get_buffer (text_view),
4017                              text_view->dnd_mark,
4018                              &newplace);
4019   
4020   gtk_text_view_scroll_to_mark (text_view,
4021                                 text_view->dnd_mark,
4022                                 DND_SCROLL_MARGIN, FALSE, 0.0, 0.0);
4023
4024   return TRUE;
4025 }
4026
4027 static gint
4028 selection_motion_event_handler (GtkTextView *text_view, GdkEventMotion *event, gpointer data)
4029 {
4030   move_mark_to_pointer_and_scroll (text_view, "insert");
4031
4032   /* If we had to scroll offscreen, insert a timeout to do so
4033    * again. Note that in the timeout, even if the mouse doesn't
4034    * move, due to this scroll xoffset/yoffset will have changed
4035    * and we'll need to scroll again.
4036    */
4037   if (text_view->scroll_timeout != 0) /* reset on every motion event */
4038     gtk_timeout_remove (text_view->scroll_timeout);
4039   
4040   text_view->scroll_timeout =
4041     gtk_timeout_add (50, selection_scan_timeout, text_view);
4042
4043   return TRUE;
4044 }
4045
4046 static void
4047 gtk_text_view_start_selection_drag (GtkTextView       *text_view,
4048                                     const GtkTextIter *iter,
4049                                     GdkEventButton    *button)
4050 {
4051   GtkTextIter newplace;
4052
4053   g_return_if_fail (text_view->selection_drag_handler == 0);
4054
4055   gtk_grab_add (GTK_WIDGET (text_view));
4056
4057   newplace = *iter;
4058
4059   gtk_text_buffer_place_cursor (get_buffer (text_view), &newplace);
4060
4061   text_view->selection_drag_handler = gtk_signal_connect (GTK_OBJECT (text_view),
4062                                                           "motion_notify_event",
4063                                                           GTK_SIGNAL_FUNC (selection_motion_event_handler),
4064                                                           NULL);
4065 }
4066
4067 /* returns whether we were really dragging */
4068 static gboolean
4069 gtk_text_view_end_selection_drag (GtkTextView *text_view, GdkEventButton *event)
4070 {
4071   if (text_view->selection_drag_handler == 0)
4072     return FALSE;
4073
4074   gtk_signal_disconnect (GTK_OBJECT (text_view), text_view->selection_drag_handler);
4075   text_view->selection_drag_handler = 0;
4076
4077   if (text_view->scroll_timeout != 0)
4078     {
4079       gtk_timeout_remove (text_view->scroll_timeout);
4080       text_view->scroll_timeout = 0;
4081     }
4082
4083   /* one last update to current position */
4084   move_mark_to_pointer_and_scroll (text_view, "insert");
4085
4086   gtk_grab_remove (GTK_WIDGET (text_view));
4087
4088   return TRUE;
4089 }
4090
4091 /*
4092  * Layout utils
4093  */
4094
4095 static void
4096 gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
4097                                          GtkTextAttributes *values,
4098                                          GtkStyle           *style)
4099 {
4100   values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
4101   values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];
4102
4103   if (values->font.family_name)
4104     g_free (values->font.family_name);
4105
4106   values->font = *style->font_desc;
4107   values->font.family_name = g_strdup (style->font_desc->family_name);
4108 }
4109
4110 static void
4111 gtk_text_view_ensure_layout (GtkTextView *text_view)
4112 {
4113   GtkWidget *widget;
4114
4115   widget = GTK_WIDGET (text_view);
4116
4117   if (text_view->layout == NULL)
4118     {
4119       GtkTextAttributes *style;
4120       PangoContext *ltr_context, *rtl_context;
4121       GSList *tmp_list;
4122
4123       DV(g_print(G_STRLOC"\n"));
4124       
4125       text_view->layout = gtk_text_layout_new ();
4126
4127       g_signal_connect_data (G_OBJECT (text_view->layout),
4128                              "invalidated",
4129                              invalidated_handler,
4130                              text_view,
4131                              NULL, FALSE, FALSE);
4132
4133       g_signal_connect_data (G_OBJECT (text_view->layout),
4134                              "changed",
4135                              changed_handler,
4136                              text_view,
4137                              NULL, FALSE, FALSE);
4138
4139       g_signal_connect_data (G_OBJECT (text_view->layout),
4140                              "allocate_child",
4141                              gtk_text_view_child_allocated,
4142                              text_view,
4143                              NULL, FALSE, FALSE);
4144       
4145       if (get_buffer (text_view))
4146         gtk_text_layout_set_buffer (text_view->layout, get_buffer (text_view));
4147
4148       if ((GTK_WIDGET_HAS_FOCUS (text_view) && text_view->cursor_visible))
4149         gtk_text_view_start_cursor_blink (text_view, FALSE);
4150       else
4151         gtk_text_layout_set_cursor_visible (text_view->layout, FALSE);
4152
4153       ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
4154       pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
4155       rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
4156       pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
4157
4158       gtk_text_layout_set_contexts (text_view->layout, ltr_context, rtl_context);
4159
4160       g_object_unref (G_OBJECT (ltr_context));
4161       g_object_unref (G_OBJECT (rtl_context));
4162
4163       style = gtk_text_attributes_new ();
4164
4165       gtk_widget_ensure_style (widget);
4166       gtk_text_view_set_attributes_from_style (text_view,
4167                                                style, widget->style);
4168
4169       style->pixels_above_lines = text_view->pixels_above_lines;
4170       style->pixels_below_lines = text_view->pixels_below_lines;
4171       style->pixels_inside_wrap = text_view->pixels_inside_wrap;
4172       style->left_margin = text_view->left_margin;
4173       style->right_margin = text_view->right_margin;
4174       style->indent = text_view->indent;
4175       style->tabs = text_view->tabs ? pango_tab_array_copy (text_view->tabs) : NULL;
4176
4177       style->wrap_mode = text_view->wrap_mode;
4178       style->justification = text_view->justify;
4179       style->direction = gtk_widget_get_direction (GTK_WIDGET (text_view));
4180
4181       gtk_text_layout_set_default_style (text_view->layout, style);
4182
4183       gtk_text_attributes_unref (style);
4184
4185       /* Set layout for all anchored children */
4186
4187       tmp_list = text_view->children;
4188       while (tmp_list != NULL)
4189         {
4190           GtkTextViewChild *vc = tmp_list->data;
4191
4192           if (vc->anchor)
4193             {
4194               gtk_text_anchored_child_set_layout (vc->widget,
4195                                                   text_view->layout);
4196               /* vc may now be invalid! */
4197             }
4198
4199           tmp_list = g_slist_next (tmp_list);
4200         }
4201     }
4202 }
4203
4204 static void
4205 gtk_text_view_destroy_layout (GtkTextView *text_view)
4206 {
4207   if (text_view->layout)
4208     {
4209       GSList *tmp_list;
4210
4211       if (text_view->incremental_validate_idle)
4212         {
4213           g_source_remove (text_view->incremental_validate_idle);
4214           text_view->incremental_validate_idle = 0;
4215         }
4216
4217       /* Remove layout from all anchored children */
4218       
4219       tmp_list = text_view->children;
4220       while (tmp_list != NULL)
4221         {
4222           GtkTextViewChild *vc = tmp_list->data;
4223
4224           if (vc->anchor)
4225             {
4226               gtk_text_anchored_child_set_layout (vc->widget, NULL);
4227               /* vc may now be invalid! */
4228             }
4229
4230           tmp_list = g_slist_next (tmp_list);
4231         }
4232       
4233       gtk_text_view_stop_cursor_blink (text_view);
4234       gtk_text_view_end_selection_drag (text_view, NULL);
4235
4236       g_signal_handlers_disconnect_by_func (G_OBJECT (text_view->layout),
4237                                             invalidated_handler, text_view);
4238       g_signal_handlers_disconnect_by_func (G_OBJECT (text_view->layout),
4239                                             changed_handler, text_view);
4240       g_object_unref (G_OBJECT (text_view->layout));
4241       text_view->layout = NULL;
4242     }
4243 }
4244
4245 static void
4246 gtk_text_view_reset_im_context (GtkTextView *text_view)
4247 {
4248   if (text_view->need_im_reset)
4249     {
4250       text_view->need_im_reset = FALSE;
4251       gtk_im_context_reset (text_view->im_context);
4252     }
4253 }
4254
4255 /*
4256  * DND feature
4257  */
4258
4259 static void
4260 gtk_text_view_start_selection_dnd (GtkTextView       *text_view,
4261                                    const GtkTextIter *iter,
4262                                    GdkEventMotion    *event)
4263 {
4264   GdkDragContext *context;
4265   GtkTargetList *target_list;
4266
4267   text_view->drag_start_x = -1;
4268   text_view->drag_start_y = -1;
4269
4270   target_list = gtk_target_list_new (target_table,
4271                                      G_N_ELEMENTS (target_table));
4272
4273   context = gtk_drag_begin (GTK_WIDGET (text_view), target_list,
4274                             GDK_ACTION_COPY | GDK_ACTION_MOVE,
4275                             1, (GdkEvent*)event);
4276
4277   gtk_target_list_unref (target_list);
4278
4279   gtk_drag_set_icon_default (context);
4280 }
4281
4282 static void
4283 gtk_text_view_drag_begin (GtkWidget        *widget,
4284                           GdkDragContext   *context)
4285 {
4286   /* do nothing */
4287 }
4288
4289 static void
4290 gtk_text_view_drag_end (GtkWidget        *widget,
4291                         GdkDragContext   *context)
4292 {
4293   GtkTextView *text_view;
4294
4295   text_view = GTK_TEXT_VIEW (widget);
4296 }
4297
4298 static void
4299 gtk_text_view_drag_data_get (GtkWidget        *widget,
4300                              GdkDragContext   *context,
4301                              GtkSelectionData *selection_data,
4302                              guint             info,
4303                              guint             time)
4304 {
4305   GtkTextView *text_view;
4306
4307   text_view = GTK_TEXT_VIEW (widget);
4308
4309   if (selection_data->target == gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE))
4310     {
4311       GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
4312
4313       gtk_selection_data_set (selection_data,
4314                               gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE),
4315                               8, /* bytes */
4316                               (void*)&buffer,
4317                               sizeof (buffer));
4318     }
4319   else
4320     {
4321       gchar *str;
4322       GtkTextIter start;
4323       GtkTextIter end;
4324
4325       str = NULL;
4326
4327       if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
4328                                                 &start, &end))
4329         {
4330           /* Extract the selected text */
4331           str = gtk_text_iter_get_visible_text (&start, &end);
4332         }
4333
4334       if (str)
4335         {
4336           gtk_selection_data_set_text (selection_data, str);
4337           g_free (str);
4338         }
4339     }
4340 }
4341
4342 static void
4343 gtk_text_view_drag_data_delete (GtkWidget        *widget,
4344                                 GdkDragContext   *context)
4345 {
4346   GtkTextView *text_view;
4347
4348   text_view = GTK_TEXT_VIEW (widget);
4349
4350   gtk_text_buffer_delete_selection (GTK_TEXT_VIEW (widget)->buffer,
4351                                     TRUE, GTK_TEXT_VIEW (widget)->editable);
4352 }
4353
4354 static void
4355 gtk_text_view_drag_leave (GtkWidget        *widget,
4356                           GdkDragContext   *context,
4357                           guint             time)
4358 {
4359   GtkTextView *text_view;
4360
4361   text_view = GTK_TEXT_VIEW (widget);
4362
4363   gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
4364   
4365   if (text_view->scroll_timeout != 0)
4366     gtk_timeout_remove (text_view->scroll_timeout);
4367
4368   text_view->scroll_timeout = 0;
4369 }
4370
4371 static gboolean
4372 gtk_text_view_drag_motion (GtkWidget        *widget,
4373                            GdkDragContext   *context,
4374                            gint              x,
4375                            gint              y,
4376                            guint             time)
4377 {
4378   GtkTextIter newplace;
4379   GtkTextView *text_view;
4380   GtkTextIter start;
4381   GtkTextIter end;
4382   GdkRectangle target_rect;
4383   gint bx, by;
4384   GdkDragAction suggested_action = 0;
4385   
4386   text_view = GTK_TEXT_VIEW (widget);
4387
4388   target_rect = text_view->text_window->allocation;
4389   
4390   if (x < target_rect.x ||
4391       y < target_rect.y ||
4392       x > (target_rect.x + target_rect.width) ||
4393       y > (target_rect.y + target_rect.height))
4394     return FALSE; /* outside the text window, allow parent widgets to handle event */
4395
4396   gtk_text_view_window_to_buffer_coords (text_view,
4397                                          GTK_TEXT_WINDOW_WIDGET,
4398                                          x, y,
4399                                          &bx, &by);
4400
4401   gtk_text_layout_get_iter_at_pixel (text_view->layout,
4402                                      &newplace,
4403                                      bx, by);  
4404
4405   if (gtk_drag_dest_find_target (widget, context,
4406                                  gtk_drag_dest_get_target_list (widget)) == GDK_NONE)
4407     {
4408       /* can't accept any of the offered targets */
4409     }                                 
4410   else if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
4411                                             &start, &end) &&
4412            gtk_text_iter_in_range (&newplace, &start, &end))
4413     {
4414       /* We're inside the selection. */
4415     }
4416   else
4417     {      
4418       if (gtk_text_iter_editable (&newplace, text_view->editable))
4419         {
4420           GtkWidget *source_widget;
4421           
4422           suggested_action = context->suggested_action;
4423           
4424           source_widget = gtk_drag_get_source_widget (context);
4425           
4426           if (source_widget == widget)
4427             {
4428               /* Default to MOVE, unless the user has
4429                * pressed ctrl or alt to affect available actions
4430                */
4431               if ((context->actions & GDK_ACTION_MOVE) != 0)
4432                 suggested_action = GDK_ACTION_MOVE;
4433             }
4434         }
4435       else
4436         {
4437           /* Can't drop here. */
4438         }
4439     }
4440
4441   if (suggested_action != 0)
4442     {
4443       gtk_text_mark_set_visible (text_view->dnd_mark,
4444                                  text_view->cursor_visible);
4445       
4446       gdk_drag_status (context, suggested_action, time);
4447     }
4448   else
4449     {
4450       gdk_drag_status (context, 0, time);
4451       gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
4452     }
4453       
4454   gtk_text_buffer_move_mark (get_buffer (text_view),
4455                              text_view->dnd_mark,
4456                              &newplace);
4457
4458   gtk_text_view_scroll_to_mark (text_view,
4459                                 text_view->dnd_mark,
4460                                 DND_SCROLL_MARGIN, FALSE, 0.0, 0.0);
4461   
4462   if (text_view->scroll_timeout != 0) /* reset on every motion event */
4463     gtk_timeout_remove (text_view->scroll_timeout);
4464       
4465   text_view->scroll_timeout =
4466     gtk_timeout_add (50, drag_scan_timeout, text_view);
4467
4468   /* TRUE return means don't propagate the drag motion to parent
4469    * widgets that may also be drop sites.
4470    */
4471   return TRUE;
4472 }
4473
4474 static gboolean
4475 gtk_text_view_drag_drop (GtkWidget        *widget,
4476                          GdkDragContext   *context,
4477                          gint              x,
4478                          gint              y,
4479                          guint             time)
4480 {
4481   GtkTextView *text_view;
4482   
4483   text_view = GTK_TEXT_VIEW (widget);
4484   
4485   if (text_view->scroll_timeout != 0)
4486     gtk_timeout_remove (text_view->scroll_timeout);
4487
4488   text_view->scroll_timeout = 0;
4489
4490   gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
4491   
4492   return TRUE;
4493 }
4494
4495 static void
4496 insert_text_data (GtkTextView      *text_view,
4497                   GtkTextIter      *drop_point,
4498                   GtkSelectionData *selection_data)
4499 {
4500   gchar *str;
4501
4502   str = gtk_selection_data_get_text (selection_data);
4503
4504   if (str)
4505     {
4506       gtk_text_buffer_insert_interactive (get_buffer (text_view),
4507                                           drop_point, str, -1,
4508                                           text_view->editable);
4509       g_free (str);
4510     }
4511 }
4512
4513 static void
4514 gtk_text_view_drag_data_received (GtkWidget        *widget,
4515                                   GdkDragContext   *context,
4516                                   gint              x,
4517                                   gint              y,
4518                                   GtkSelectionData *selection_data,
4519                                   guint             info,
4520                                   guint             time)
4521 {
4522   GtkTextIter drop_point;
4523   GtkTextView *text_view;
4524   GtkTextMark *drag_target_mark;
4525
4526   text_view = GTK_TEXT_VIEW (widget);
4527
4528   drag_target_mark = gtk_text_buffer_get_mark (get_buffer (text_view),
4529                                                "gtk_drag_target");
4530
4531   if (drag_target_mark == NULL)
4532     return;
4533
4534   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
4535                                     &drop_point,
4536                                     drag_target_mark);
4537
4538   if (selection_data->target == gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE))
4539     {
4540       GtkTextBuffer *src_buffer = NULL;
4541       GtkTextIter start, end;
4542       gboolean copy_tags = TRUE;
4543
4544       if (selection_data->length != sizeof (src_buffer))
4545         return;
4546
4547       memcpy (&src_buffer, selection_data->data, sizeof (src_buffer));
4548
4549       if (src_buffer == NULL)
4550         return;
4551
4552       g_return_if_fail (GTK_IS_TEXT_BUFFER (src_buffer));
4553
4554       if (gtk_text_buffer_get_tag_table (src_buffer) !=
4555           gtk_text_buffer_get_tag_table (get_buffer (text_view)))
4556         copy_tags = FALSE;
4557
4558       if (gtk_text_buffer_get_selection_bounds (src_buffer,
4559                                                 &start,
4560                                                 &end))
4561         {
4562           if (copy_tags)
4563             gtk_text_buffer_insert_range_interactive (get_buffer (text_view),
4564                                                       &drop_point,
4565                                                       &start,
4566                                                       &end,
4567                                                       text_view->editable);
4568           else
4569             {
4570               gchar *str;
4571
4572               str = gtk_text_iter_get_visible_text (&start, &end);
4573               gtk_text_buffer_insert_interactive (get_buffer (text_view),
4574                                                   &drop_point, str, -1,
4575                                                   text_view->editable);
4576               g_free (str);
4577             }
4578         }
4579     }
4580   else
4581     insert_text_data (text_view, &drop_point, selection_data);
4582 }
4583
4584 static GtkAdjustment*
4585 get_hadjustment (GtkTextView *text_view)
4586 {
4587   if (text_view->hadjustment == NULL)
4588     gtk_text_view_set_scroll_adjustments (text_view,
4589                                           NULL, /* forces creation */
4590                                           text_view->vadjustment);
4591
4592   return text_view->hadjustment;
4593 }
4594
4595 static GtkAdjustment*
4596 get_vadjustment (GtkTextView *text_view)
4597 {
4598   if (text_view->vadjustment == NULL)
4599     gtk_text_view_set_scroll_adjustments (text_view,
4600                                           text_view->hadjustment,
4601                                           NULL); /* forces creation */
4602   return text_view->vadjustment;
4603 }
4604
4605
4606 static void
4607 gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
4608                                       GtkAdjustment *hadj,
4609                                       GtkAdjustment *vadj)
4610 {
4611   gboolean need_adjust = FALSE;
4612
4613   g_return_if_fail (text_view != NULL);
4614   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
4615
4616   if (hadj)
4617     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
4618   else
4619     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
4620   if (vadj)
4621     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
4622   else
4623     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
4624
4625   if (text_view->hadjustment && (text_view->hadjustment != hadj))
4626     {
4627       gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->hadjustment), text_view);
4628       g_object_unref (G_OBJECT (text_view->hadjustment));
4629     }
4630
4631   if (text_view->vadjustment && (text_view->vadjustment != vadj))
4632     {
4633       gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->vadjustment), text_view);
4634       g_object_unref (G_OBJECT (text_view->vadjustment));
4635     }
4636
4637   if (text_view->hadjustment != hadj)
4638     {
4639       text_view->hadjustment = hadj;
4640       g_object_ref (G_OBJECT (text_view->hadjustment));
4641       gtk_object_sink (GTK_OBJECT (text_view->hadjustment));
4642
4643       gtk_signal_connect (GTK_OBJECT (text_view->hadjustment), "value_changed",
4644                           (GtkSignalFunc) gtk_text_view_value_changed,
4645                           text_view);
4646       need_adjust = TRUE;
4647     }
4648
4649   if (text_view->vadjustment != vadj)
4650     {
4651       text_view->vadjustment = vadj;
4652       g_object_ref (G_OBJECT (text_view->vadjustment));
4653       gtk_object_sink (GTK_OBJECT (text_view->vadjustment));
4654
4655       gtk_signal_connect (GTK_OBJECT (text_view->vadjustment), "value_changed",
4656                           (GtkSignalFunc) gtk_text_view_value_changed,
4657                           text_view);
4658       need_adjust = TRUE;
4659     }
4660
4661   if (need_adjust)
4662     gtk_text_view_value_changed (NULL, text_view);
4663 }
4664
4665 static void
4666 gtk_text_view_value_changed (GtkAdjustment *adj,
4667                              GtkTextView   *text_view)
4668 {
4669   GtkTextIter iter;
4670   gint line_top;
4671   gint dx = 0;
4672   gint dy = 0;
4673
4674   text_view->onscreen_validated = FALSE;
4675
4676   DV(g_print(">Scroll offset changed, onscreen_validated = FALSE ("G_STRLOC")\n"));
4677   
4678   if (adj == text_view->hadjustment)
4679     {
4680       dx = text_view->xoffset - (gint)adj->value;
4681       text_view->xoffset = adj->value;
4682     }
4683   else if (adj == text_view->vadjustment)
4684     {
4685       dy = text_view->yoffset - (gint)adj->value;
4686       text_view->yoffset = adj->value;
4687
4688       if (text_view->layout)
4689         {
4690           gtk_text_layout_get_line_at_y (text_view->layout, &iter, adj->value, &line_top);
4691
4692           gtk_text_buffer_move_mark (get_buffer (text_view), text_view->first_para_mark, &iter);
4693
4694           text_view->first_para_pixels = adj->value - line_top;
4695         }
4696     }
4697   
4698   if (GTK_WIDGET_REALIZED (text_view) && (dx != 0 || dy != 0))
4699     {
4700       if (dy != 0)
4701         {
4702           if (text_view->left_window)
4703             text_window_scroll (text_view->left_window, 0, dy);
4704           if (text_view->right_window)
4705             text_window_scroll (text_view->right_window, 0, dy);
4706         }
4707
4708       if (dx != 0)
4709         {
4710           if (text_view->top_window)
4711             text_window_scroll (text_view->top_window, dx, 0);
4712           if (text_view->bottom_window)
4713             text_window_scroll (text_view->bottom_window, dx, 0);
4714         }
4715
4716       /* It looks nicer to scroll the main area last, because
4717        * it takes a while, and making the side areas update
4718        * afterward emphasizes the slowness of scrolling the
4719        * main area.
4720        */
4721       text_window_scroll (text_view->text_window, dx, dy);
4722     }
4723
4724   /* This could result in invalidation, which would install the
4725    * first_validate_idle, which would validate onscreen;
4726    * but we're going to go ahead and validate here, so
4727    * first_validate_idle shouldn't have anything to do.
4728    */
4729   gtk_text_view_update_layout_width (text_view);
4730   
4731   /* note that validation of onscreen could invoke this function
4732    * recursively, by scrolling to maintain first_para, or in response
4733    * to updating the layout width, however there is no problem with
4734    * that, or shouldn't be.
4735    */
4736   gtk_text_view_validate_onscreen (text_view);
4737
4738   /* process exposes */
4739   if (GTK_WIDGET_REALIZED (text_view))
4740     {
4741       if (text_view->left_window)
4742         gdk_window_process_updates (text_view->left_window->bin_window, TRUE);
4743
4744       if (text_view->right_window)
4745         gdk_window_process_updates (text_view->right_window->bin_window, TRUE);
4746
4747       if (text_view->top_window)
4748         gdk_window_process_updates (text_view->top_window->bin_window, TRUE);
4749       
4750       if (text_view->bottom_window)
4751         gdk_window_process_updates (text_view->bottom_window->bin_window, TRUE);
4752   
4753       gdk_window_process_updates (text_view->text_window->bin_window, TRUE);
4754     }
4755
4756   /* If this got installed, get rid of it, it's just a waste of time. */
4757   if (text_view->first_validate_idle != 0)
4758     {
4759       g_source_remove (text_view->first_validate_idle);
4760       text_view->first_validate_idle = 0;
4761     }
4762   
4763   DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n"));
4764 }
4765
4766 static void
4767 gtk_text_view_commit_handler (GtkIMContext  *context,
4768                               const gchar   *str,
4769                               GtkTextView   *text_view)
4770 {
4771   gtk_text_buffer_begin_user_action (get_buffer (text_view));
4772
4773   gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
4774                                     text_view->editable);
4775
4776   if (!strcmp (str, "\n"))
4777     {
4778       gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
4779                                                     text_view->editable);
4780     }
4781   else
4782     {
4783       if (text_view->overwrite_mode)
4784         gtk_text_view_delete_from_cursor (text_view, GTK_DELETE_CHARS, 1);
4785       gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
4786                                                     text_view->editable);
4787     }
4788
4789   gtk_text_buffer_end_user_action (get_buffer (text_view));
4790   
4791   gtk_text_view_scroll_mark_onscreen (text_view,
4792                                       gtk_text_buffer_get_mark (get_buffer (text_view),
4793                                                                 "insert"));
4794 }
4795
4796 static void
4797 gtk_text_view_preedit_changed_handler (GtkIMContext *context,
4798                                        GtkTextView  *text_view)
4799 {
4800   gchar *str;
4801   PangoAttrList *attrs;
4802   gint cursor_pos;
4803
4804   gtk_im_context_get_preedit_string (context, &str, &attrs, &cursor_pos);
4805   gtk_text_layout_set_preedit_string (text_view->layout, str, attrs, cursor_pos);
4806
4807   pango_attr_list_unref (attrs);
4808   g_free (str);
4809 }
4810
4811 static void
4812 gtk_text_view_mark_set_handler (GtkTextBuffer     *buffer,
4813                                 const GtkTextIter *location,
4814                                 GtkTextMark       *mark,
4815                                 gpointer           data)
4816 {
4817   GtkTextView *text_view = GTK_TEXT_VIEW (data);
4818   gboolean need_reset = FALSE;
4819
4820   if (mark == gtk_text_buffer_get_insert (buffer))
4821     {
4822       text_view->virtual_cursor_x = -1;
4823       text_view->virtual_cursor_y = -1;
4824       need_reset = TRUE;
4825     }
4826   else if (mark == gtk_text_buffer_get_selection_bound (buffer))
4827     {
4828       need_reset = TRUE;
4829     }
4830
4831   if (need_reset)
4832     gtk_text_view_reset_im_context (text_view);
4833 }
4834
4835 static void
4836 gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
4837                                       gint        *x,
4838                                       gint        *y)
4839 {
4840   GdkRectangle strong_pos;
4841   GtkTextIter insert;
4842
4843   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
4844                                     gtk_text_buffer_get_mark (get_buffer (text_view),
4845                                                               "insert"));
4846
4847   if ((x && text_view->virtual_cursor_x == -1) ||
4848       (y && text_view->virtual_cursor_y == -1))
4849     gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
4850
4851   if (x)
4852     {
4853       if (text_view->virtual_cursor_x != -1)
4854         *x = text_view->virtual_cursor_x;
4855       else
4856         *x = strong_pos.x;
4857     }
4858
4859   if (y)
4860     {
4861       if (text_view->virtual_cursor_x != -1)
4862         *y = text_view->virtual_cursor_y;
4863       else
4864         *y = strong_pos.y + strong_pos.height / 2;
4865     }
4866 }
4867
4868 static void
4869 gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
4870                                       gint         x,
4871                                       gint         y)
4872 {
4873   GdkRectangle strong_pos;
4874   GtkTextIter insert;
4875
4876   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
4877                                     gtk_text_buffer_get_mark (get_buffer (text_view),
4878                                                               "insert"));
4879
4880   if (x == -1 || y == -1)
4881     gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
4882
4883   text_view->virtual_cursor_x = (x == -1) ? strong_pos.x : x;
4884   text_view->virtual_cursor_y = (y == -1) ? strong_pos.y + strong_pos.height / 2 : y;
4885 }
4886
4887 /* Quick hack of a popup menu
4888  */
4889 static void
4890 activate_cb (GtkWidget   *menuitem,
4891              GtkTextView *text_view)
4892 {
4893   const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
4894   gtk_signal_emit_by_name (GTK_OBJECT (text_view), signal);
4895 }
4896
4897 static void
4898 append_action_signal (GtkTextView  *text_view,
4899                       GtkWidget    *menu,
4900                       const gchar  *label,
4901                       const gchar  *signal,
4902                       gboolean      sensitive)
4903 {
4904   GtkWidget *menuitem = gtk_menu_item_new_with_label (label);
4905
4906   g_object_set_data (G_OBJECT (menuitem), "gtk-signal", (char *)signal);
4907   gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
4908                       GTK_SIGNAL_FUNC (activate_cb), text_view);
4909
4910   gtk_widget_set_sensitive (menuitem, sensitive);
4911   
4912   gtk_widget_show (menuitem);
4913   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
4914 }
4915
4916 static void
4917 popup_menu_detach (GtkWidget *attach_widget,
4918                    GtkMenu   *menu)
4919 {
4920   GTK_TEXT_VIEW (attach_widget)->popup_menu = NULL;
4921 }
4922
4923 static void
4924 popup_position_func (GtkMenu   *menu,
4925                      gint      *x,
4926                      gint      *y,
4927                      gboolean  *push_in,
4928                      gpointer   user_data)
4929 {
4930   GtkTextView *text_view;
4931   GtkWidget *widget;
4932   GdkRectangle cursor_rect;
4933   GdkRectangle onscreen_rect;
4934   gint root_x, root_y;
4935   GtkTextIter iter;
4936   GtkRequisition req;      
4937   
4938   text_view = GTK_TEXT_VIEW (user_data);
4939   widget = GTK_WIDGET (text_view);
4940   
4941   g_return_if_fail (GTK_WIDGET_REALIZED (text_view));
4942
4943   gdk_window_get_origin (widget->window, &root_x, &root_y);
4944
4945   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
4946                                     &iter,
4947                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
4948
4949   gtk_text_view_get_iter_location (text_view,
4950                                    &iter,
4951                                    &cursor_rect);
4952
4953   gtk_text_view_get_visible_rect (text_view, &onscreen_rect);
4954   
4955   gtk_widget_size_request (text_view->popup_menu, &req);
4956
4957   /* can't use rectangle_intersect since cursor rect can have 0 width */
4958   if (cursor_rect.x >= onscreen_rect.x &&
4959       cursor_rect.x < onscreen_rect.x + onscreen_rect.width &&
4960       cursor_rect.y >= onscreen_rect.y &&
4961       cursor_rect.y < onscreen_rect.y + onscreen_rect.height)
4962     {    
4963       gtk_text_view_buffer_to_window_coords (text_view,
4964                                              GTK_TEXT_WINDOW_WIDGET,
4965                                              cursor_rect.x, cursor_rect.y,
4966                                              &cursor_rect.x, &cursor_rect.y);
4967
4968       *x = root_x + cursor_rect.x + cursor_rect.width;
4969       *y = root_y + cursor_rect.y + cursor_rect.height;
4970     }
4971   else
4972     {
4973       /* Just center the menu, since cursor is offscreen. */      
4974       *x = root_x + (widget->allocation.width / 2 - req.width / 2);
4975       *y = root_y + (widget->allocation.height / 2 - req.height / 2);      
4976     }
4977
4978   /* Ensure sanity */
4979   *x = CLAMP (*x, root_x, (root_x + widget->allocation.width));
4980   *y = CLAMP (*y, root_y, (root_y + widget->allocation.height));
4981
4982   *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
4983   *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
4984 }
4985
4986 static void
4987 gtk_text_view_do_popup (GtkTextView    *text_view,
4988                         GdkEventButton *event)
4989 {
4990   GtkWidget *menuitem;
4991   GtkWidget *submenu;
4992   gboolean have_selection;
4993   gboolean can_insert;
4994   GtkTextIter iter;
4995   
4996   if (text_view->popup_menu)
4997     gtk_widget_destroy (text_view->popup_menu);
4998   
4999   text_view->popup_menu = gtk_menu_new ();
5000
5001   gtk_menu_attach_to_widget (GTK_MENU (text_view->popup_menu),
5002                              GTK_WIDGET (text_view),
5003                              popup_menu_detach);
5004
5005   have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
5006                                                          NULL, NULL);
5007
5008   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
5009                                     &iter,
5010                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
5011   
5012   can_insert = gtk_text_iter_editable (&iter, text_view->editable);
5013   
5014   append_action_signal (text_view, text_view->popup_menu, _("Cut"), "cut_clipboard",
5015                         have_selection);
5016   append_action_signal (text_view, text_view->popup_menu, _("Copy"), "copy_clipboard",
5017                         have_selection);
5018
5019   append_action_signal (text_view, text_view->popup_menu, _("Paste"), "paste_clipboard",
5020                         can_insert);
5021
5022   menuitem = gtk_separator_menu_item_new ();
5023   gtk_widget_show (menuitem);
5024   gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
5025       
5026   menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
5027   gtk_widget_show (menuitem);
5028   submenu = gtk_menu_new ();
5029   gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
5030   gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
5031
5032   gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (text_view->im_context),
5033                                         GTK_MENU_SHELL (submenu));
5034
5035   gtk_signal_emit (GTK_OBJECT (text_view),
5036                    signals[POPULATE_POPUP],
5037                    text_view->popup_menu);
5038   
5039   if (event)
5040     gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
5041                     NULL, NULL,
5042                     event->button, event->time);
5043   else
5044     gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
5045                     popup_position_func, text_view,
5046                     0, gtk_get_current_event_time ());
5047 }
5048
5049 static void
5050 gtk_text_view_popup_menu (GtkWidget *widget)
5051 {
5052   gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL);  
5053 }
5054
5055 /* Child GdkWindows */
5056
5057
5058 static GtkTextWindow*
5059 text_window_new (GtkTextWindowType  type,
5060                  GtkWidget         *widget,
5061                  gint               width_request,
5062                  gint               height_request)
5063 {
5064   GtkTextWindow *win;
5065
5066   win = g_new (GtkTextWindow, 1);
5067
5068   win->type = type;
5069   win->widget = widget;
5070   win->window = NULL;
5071   win->bin_window = NULL;
5072   win->requisition.width = width_request;
5073   win->requisition.height = height_request;
5074   win->allocation.width = width_request;
5075   win->allocation.height = height_request;
5076   win->allocation.x = 0;
5077   win->allocation.y = 0;
5078
5079   return win;
5080 }
5081
5082 static void
5083 text_window_free (GtkTextWindow *win)
5084 {
5085   if (win->window)
5086     text_window_unrealize (win);
5087
5088   g_free (win);
5089 }
5090
5091 static void
5092 text_window_realize (GtkTextWindow *win,
5093                      GdkWindow     *parent)
5094 {
5095   GdkWindowAttr attributes;
5096   gint attributes_mask;
5097   GdkCursor *cursor;
5098
5099   attributes.window_type = GDK_WINDOW_CHILD;
5100   attributes.x = win->allocation.x;
5101   attributes.y = win->allocation.y;
5102   attributes.width = win->allocation.width;
5103   attributes.height = win->allocation.height;
5104   attributes.wclass = GDK_INPUT_OUTPUT;
5105   attributes.visual = gtk_widget_get_visual (win->widget);
5106   attributes.colormap = gtk_widget_get_colormap (win->widget);
5107   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
5108
5109   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
5110
5111   win->window = gdk_window_new (parent,
5112                                 &attributes,
5113                                 attributes_mask);
5114
5115   gdk_window_show (win->window);
5116   gdk_window_set_user_data (win->window, win->widget);
5117
5118   attributes.x = 0;
5119   attributes.y = 0;
5120   attributes.width = win->allocation.width;
5121   attributes.height = win->allocation.height;
5122   attributes.event_mask = (GDK_EXPOSURE_MASK            |
5123                            GDK_SCROLL_MASK              |
5124                            GDK_KEY_PRESS_MASK           |
5125                            GDK_BUTTON_PRESS_MASK        |
5126                            GDK_BUTTON_RELEASE_MASK      |
5127                            GDK_POINTER_MOTION_MASK      |
5128                            GDK_POINTER_MOTION_HINT_MASK |
5129                            gtk_widget_get_events (win->widget));
5130
5131   win->bin_window = gdk_window_new (win->window,
5132                                     &attributes,
5133                                     attributes_mask);
5134
5135   gdk_window_show (win->bin_window);
5136   gdk_window_set_user_data (win->bin_window, win->widget);
5137
5138   if (win->type == GTK_TEXT_WINDOW_TEXT)
5139     {
5140       /* I-beam cursor */
5141       cursor = gdk_cursor_new (GDK_XTERM);
5142       gdk_window_set_cursor (win->bin_window, cursor);
5143       gdk_cursor_destroy (cursor);
5144
5145       gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->im_context,
5146                                         win->window);
5147
5148
5149       gdk_window_set_background (win->bin_window,
5150                                  &win->widget->style->base[GTK_WIDGET_STATE (win->widget)]);
5151     }
5152   else
5153     {
5154       gdk_window_set_background (win->bin_window,
5155                                  &win->widget->style->bg[GTK_WIDGET_STATE (win->widget)]);
5156     }
5157
5158   g_object_set_qdata (G_OBJECT (win->window),
5159                       g_quark_from_static_string ("gtk-text-view-text-window"),
5160                       win);
5161
5162   g_object_set_qdata (G_OBJECT (win->bin_window),
5163                       g_quark_from_static_string ("gtk-text-view-text-window"),
5164                       win);
5165 }
5166
5167 static void
5168 text_window_unrealize (GtkTextWindow *win)
5169 {
5170   if (win->type == GTK_TEXT_WINDOW_TEXT)
5171     {
5172       gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->im_context,
5173                                         NULL);
5174     }
5175
5176   gdk_window_set_user_data (win->window, NULL);
5177   gdk_window_set_user_data (win->bin_window, NULL);
5178   gdk_window_destroy (win->bin_window);
5179   gdk_window_destroy (win->window);
5180   win->window = NULL;
5181   win->bin_window = NULL;
5182 }
5183
5184 static void
5185 text_window_size_allocate (GtkTextWindow *win,
5186                            GdkRectangle  *rect)
5187 {
5188   win->allocation = *rect;
5189
5190   if (win->window)
5191     {
5192       gdk_window_move_resize (win->window,
5193                               rect->x, rect->y,
5194                               rect->width, rect->height);
5195
5196       gdk_window_resize (win->bin_window,
5197                          rect->width, rect->height);
5198     }
5199 }
5200
5201 static void
5202 text_window_scroll        (GtkTextWindow *win,
5203                            gint           dx,
5204                            gint           dy)
5205 {
5206   if (dx != 0 || dy != 0)
5207     {
5208       gdk_window_scroll (win->bin_window, dx, dy);
5209     }
5210 }
5211
5212 void
5213 text_window_invalidate_rect (GtkTextWindow *win,
5214                              GdkRectangle  *rect)
5215 {
5216   GdkRectangle window_rect;
5217
5218   gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (win->widget),
5219                                          win->type,
5220                                          rect->x,
5221                                          rect->y,
5222                                          &window_rect.x,
5223                                          &window_rect.y);
5224
5225   window_rect.width = rect->width;
5226   window_rect.height = rect->height;
5227   
5228   /* Adjust the rect as appropriate */
5229   
5230   switch (win->type)
5231     {
5232     case GTK_TEXT_WINDOW_TEXT:
5233       break;
5234
5235     case GTK_TEXT_WINDOW_LEFT:
5236     case GTK_TEXT_WINDOW_RIGHT:
5237       window_rect.x = 0;
5238       window_rect.width = win->allocation.width;
5239       break;
5240
5241     case GTK_TEXT_WINDOW_TOP:
5242     case GTK_TEXT_WINDOW_BOTTOM:
5243       window_rect.y = 0;
5244       window_rect.height = win->allocation.height;
5245       break;
5246
5247     default:
5248       g_warning ("%s: bug!", G_STRLOC);
5249       return;
5250       break;
5251     }
5252           
5253   gdk_window_invalidate_rect (win->bin_window, &window_rect, FALSE);
5254
5255 #if 0
5256   {
5257     GdkColor color = { 0, 65535, 0, 0 };
5258     GdkGC *gc = gdk_gc_new (win->bin_window);
5259     gdk_gc_set_rgb_fg_color (gc, &color);
5260     gdk_draw_rectangle (win->bin_window,
5261                         gc, TRUE, window_rect.x, window_rect.y,
5262                         window_rect.width, window_rect.height);
5263     gdk_gc_unref (gc);
5264   }
5265 #endif
5266 }
5267
5268 static gint
5269 text_window_get_width (GtkTextWindow *win)
5270 {
5271   return win->allocation.width;
5272 }
5273
5274 static gint
5275 text_window_get_height (GtkTextWindow *win)
5276 {
5277   return win->allocation.height;
5278 }
5279
5280 static void
5281 text_window_get_allocation (GtkTextWindow *win,
5282                             GdkRectangle  *rect)
5283 {
5284   *rect = win->allocation;
5285 }
5286
5287 /* Windows */
5288
5289
5290 /**
5291  * gtk_text_view_get_window:
5292  * @text_view: a #GtkTextView
5293  * @win: window to get
5294  *
5295  * Retrieves the #GdkWindow corresponding to an area of the text view;
5296  * possible windows include the overall widget window, child windows
5297  * on the left, right, top, bottom, and the window that displays the
5298  * text buffer. Windows are %NULL and nonexistent if their width or
5299  * height is 0, and are nonexistent before the widget has been
5300  * realized.
5301  *
5302  * Return value: a #GdkWindow, or %NULL
5303  **/
5304 GdkWindow*
5305 gtk_text_view_get_window (GtkTextView *text_view,
5306                           GtkTextWindowType win)
5307 {
5308   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
5309
5310   switch (win)
5311     {
5312     case GTK_TEXT_WINDOW_WIDGET:
5313       return GTK_WIDGET (text_view)->window;
5314       break;
5315
5316     case GTK_TEXT_WINDOW_TEXT:
5317       return text_view->text_window->bin_window;
5318       break;
5319
5320     case GTK_TEXT_WINDOW_LEFT:
5321       if (text_view->left_window)
5322         return text_view->left_window->bin_window;
5323       else
5324         return NULL;
5325       break;
5326
5327     case GTK_TEXT_WINDOW_RIGHT:
5328       if (text_view->right_window)
5329         return text_view->right_window->bin_window;
5330       else
5331         return NULL;
5332       break;
5333
5334     case GTK_TEXT_WINDOW_TOP:
5335       if (text_view->top_window)
5336         return text_view->top_window->bin_window;
5337       else
5338         return NULL;
5339       break;
5340
5341     case GTK_TEXT_WINDOW_BOTTOM:
5342       if (text_view->bottom_window)
5343         return text_view->bottom_window->bin_window;
5344       else
5345         return NULL;
5346       break;
5347
5348     default:
5349       g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC);
5350       return NULL;
5351       break;
5352     }
5353 }
5354
5355 /**
5356  * gtk_text_view_get_window_type:
5357  * @text_view: a #GtkTextView
5358  * @window: a window type
5359  *
5360  * Usually used to find out which window an event corresponds to.
5361  * If you connect to an event signal on @text_view, this function
5362  * should be called on <literal>event-&gt;window</literal> to
5363  * see which window it was.
5364  *
5365  * Return value: the window type.
5366  **/
5367 GtkTextWindowType
5368 gtk_text_view_get_window_type (GtkTextView *text_view,
5369                                GdkWindow   *window)
5370 {
5371   GtkTextWindow *win;
5372
5373   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
5374   g_return_val_if_fail (GDK_IS_WINDOW (text_view), 0);
5375
5376   if (window == GTK_WIDGET (text_view)->window)
5377     return GTK_TEXT_WINDOW_WIDGET;
5378
5379   win = g_object_get_qdata (G_OBJECT (window),
5380                             g_quark_try_string ("gtk-text-view-text-window"));
5381
5382   if (win)
5383     return win->type;
5384   else
5385     {
5386       return GTK_TEXT_WINDOW_PRIVATE;
5387     }
5388 }
5389
5390 static void
5391 buffer_to_widget (GtkTextView      *text_view,
5392                   gint              buffer_x,
5393                   gint              buffer_y,
5394                   gint             *window_x,
5395                   gint             *window_y)
5396 {
5397   if (window_x)
5398     {
5399       *window_x = buffer_x - text_view->xoffset + FOCUS_EDGE_WIDTH;
5400       if (text_view->left_window)
5401         *window_x += text_view->left_window->allocation.width;
5402     }
5403
5404   if (window_y)
5405     {
5406       *window_y = buffer_y - text_view->yoffset + FOCUS_EDGE_WIDTH;
5407       if (text_view->top_window)
5408         *window_y += text_view->top_window->allocation.height;
5409     }
5410 }
5411
5412 static void
5413 widget_to_text_window (GtkTextWindow *win,
5414                        gint           widget_x,
5415                        gint           widget_y,
5416                        gint          *window_x,
5417                        gint          *window_y)
5418 {
5419   if (window_x)
5420     *window_x = widget_x - win->allocation.x;
5421
5422   if (window_y)
5423     *window_y = widget_y - win->allocation.y;
5424 }
5425
5426 static void
5427 buffer_to_text_window (GtkTextView   *text_view,
5428                        GtkTextWindow *win,
5429                        gint           buffer_x,
5430                        gint           buffer_y,
5431                        gint          *window_x,
5432                        gint          *window_y)
5433 {
5434   if (win == NULL)
5435     {
5436       g_warning ("Attempt to convert text buffer coordinates to coordinates "
5437                  "for a nonexistent or private child window of GtkTextView");
5438       return;
5439     }
5440
5441   buffer_to_widget (text_view,
5442                     buffer_x, buffer_y,
5443                     window_x, window_y);
5444
5445   widget_to_text_window (win,
5446                          window_x ? *window_x : 0,
5447                          window_y ? *window_y : 0,
5448                          window_x,
5449                          window_y);
5450 }
5451
5452 /**
5453  * gtk_text_view_buffer_to_window_coords:
5454  * @text_view: a #GtkTextView
5455  * @win: a #GtkTextWindowType
5456  * @buffer_x: buffer x coordinate
5457  * @buffer_y: buffer y coordinate
5458  * @window_x: window x coordinate return location
5459  * @window_y: window y coordinate return location
5460  *
5461  * Converts coordinate (@buffer_x, @buffer_y) to coordinates for the window
5462  * @win, and stores the result in (@window_x, @window_y).
5463  **/
5464 void
5465 gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
5466                                        GtkTextWindowType win,
5467                                        gint              buffer_x,
5468                                        gint              buffer_y,
5469                                        gint             *window_x,
5470                                        gint             *window_y)
5471 {
5472   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
5473
5474   switch (win)
5475     {
5476     case GTK_TEXT_WINDOW_WIDGET:
5477       buffer_to_widget (text_view,
5478                         buffer_x, buffer_y,
5479                         window_x, window_y);
5480       break;
5481
5482     case GTK_TEXT_WINDOW_TEXT:
5483       if (window_x)
5484         *window_x = buffer_x - text_view->xoffset;
5485       if (window_y)
5486         *window_y = buffer_y - text_view->yoffset;
5487       break;
5488
5489     case GTK_TEXT_WINDOW_LEFT:
5490       buffer_to_text_window (text_view,
5491                              text_view->left_window,
5492                              buffer_x, buffer_y,
5493                              window_x, window_y);
5494       break;
5495
5496     case GTK_TEXT_WINDOW_RIGHT:
5497       buffer_to_text_window (text_view,
5498                              text_view->right_window,
5499                              buffer_x, buffer_y,
5500                              window_x, window_y);
5501       break;
5502
5503     case GTK_TEXT_WINDOW_TOP:
5504       buffer_to_text_window (text_view,
5505                              text_view->top_window,
5506                              buffer_x, buffer_y,
5507                              window_x, window_y);
5508       break;
5509
5510     case GTK_TEXT_WINDOW_BOTTOM:
5511       buffer_to_text_window (text_view,
5512                              text_view->bottom_window,
5513                              buffer_x, buffer_y,
5514                              window_x, window_y);
5515       break;
5516
5517     case GTK_TEXT_WINDOW_PRIVATE:
5518       g_warning ("%s: can't get coords for private windows", G_STRLOC);
5519       break;
5520
5521     default:
5522       g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC);
5523       break;
5524     }
5525 }
5526
5527 static void
5528 widget_to_buffer (GtkTextView *text_view,
5529                   gint         widget_x,
5530                   gint         widget_y,
5531                   gint        *buffer_x,
5532                   gint        *buffer_y)
5533 {
5534   if (buffer_x)
5535     {
5536       *buffer_x = widget_x - FOCUS_EDGE_WIDTH + text_view->xoffset;
5537       if (text_view->left_window)
5538         *buffer_x -= text_view->left_window->allocation.width;
5539     }
5540
5541   if (buffer_y)
5542     {
5543       *buffer_y = widget_y - FOCUS_EDGE_WIDTH + text_view->yoffset;
5544       if (text_view->top_window)
5545         *buffer_y -= text_view->top_window->allocation.height;
5546     }
5547 }
5548
5549 static void
5550 text_window_to_widget (GtkTextWindow *win,
5551                        gint           window_x,
5552                        gint           window_y,
5553                        gint          *widget_x,
5554                        gint          *widget_y)
5555 {
5556   if (widget_x)
5557     *widget_x = window_x + win->allocation.x;
5558
5559   if (widget_y)
5560     *widget_y = window_y + win->allocation.y;
5561 }
5562
5563 static void
5564 text_window_to_buffer (GtkTextView   *text_view,
5565                        GtkTextWindow *win,
5566                        gint           window_x,
5567                        gint           window_y,
5568                        gint          *buffer_x,
5569                        gint          *buffer_y)
5570 {
5571   if (win == NULL)
5572     {
5573       g_warning ("Attempt to convert GtkTextView buffer coordinates into "
5574                  "coordinates for a nonexistent child window.");
5575       return;
5576     }
5577
5578   text_window_to_widget (win,
5579                          window_x,
5580                          window_y,
5581                          buffer_x,
5582                          buffer_y);
5583
5584   widget_to_buffer (text_view,
5585                     buffer_x ? *buffer_x : 0,
5586                     buffer_y ? *buffer_y : 0,
5587                     buffer_x,
5588                     buffer_y);
5589 }
5590
5591 /**
5592  * gtk_text_view_window_to_buffer_coords:
5593  * @text_view: a #GtkTextView
5594  * @win: a #GtkTextWindowType
5595  * @window_x: window x coordinate
5596  * @window_y: window y coordinate
5597  * @buffer_x: buffer x coordinate return location
5598  * @buffer_y: buffer y coordinate return location
5599  *
5600  * Converts coordinates on the window identified by @win to buffer
5601  * coordinates, storing the result in (@buffer_x,@buffer_y).
5602  **/
5603 void
5604 gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
5605                                        GtkTextWindowType win,
5606                                        gint              window_x,
5607                                        gint              window_y,
5608                                        gint             *buffer_x,
5609                                        gint             *buffer_y)
5610 {
5611   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
5612
5613   switch (win)
5614     {
5615     case GTK_TEXT_WINDOW_WIDGET:
5616       widget_to_buffer (text_view,
5617                         window_x, window_y,
5618                         buffer_x, buffer_y);
5619       break;
5620
5621     case GTK_TEXT_WINDOW_TEXT:
5622       if (buffer_x)
5623         *buffer_x = window_x + text_view->xoffset;
5624       if (buffer_y)
5625         *buffer_y = window_y + text_view->yoffset;
5626       break;
5627
5628     case GTK_TEXT_WINDOW_LEFT:
5629       text_window_to_buffer (text_view,
5630                              text_view->left_window,
5631                              window_x, window_y,
5632                              buffer_x, buffer_y);
5633       break;
5634
5635     case GTK_TEXT_WINDOW_RIGHT:
5636       text_window_to_buffer (text_view,
5637                              text_view->right_window,
5638                              window_x, window_y,
5639                              buffer_x, buffer_y);
5640       break;
5641
5642     case GTK_TEXT_WINDOW_TOP:
5643       text_window_to_buffer (text_view,
5644                              text_view->top_window,
5645                              window_x, window_y,
5646                              buffer_x, buffer_y);
5647       break;
5648
5649     case GTK_TEXT_WINDOW_BOTTOM:
5650       text_window_to_buffer (text_view,
5651                              text_view->bottom_window,
5652                              window_x, window_y,
5653                              buffer_x, buffer_y);
5654       break;
5655
5656     case GTK_TEXT_WINDOW_PRIVATE:
5657       g_warning ("%s: can't get coords for private windows", G_STRLOC);
5658       break;
5659
5660     default:
5661       g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC);
5662       break;
5663     }
5664 }
5665
5666 static void
5667 set_window_width (GtkTextView      *text_view,
5668                   gint              width,
5669                   GtkTextWindowType type,
5670                   GtkTextWindow   **winp)
5671 {
5672   if (width == 0)
5673     {
5674       if (*winp)
5675         {
5676           text_window_free (*winp);
5677           *winp = NULL;
5678           gtk_widget_queue_resize (GTK_WIDGET (text_view));
5679         }
5680     }
5681   else
5682     {
5683       if (*winp == NULL)
5684         {
5685           *winp = text_window_new (type,
5686                                    GTK_WIDGET (text_view),
5687                                    width, 0);
5688           /* if the widget is already realized we need to realize the child manually */
5689           if (GTK_WIDGET_REALIZED (text_view))
5690             text_window_realize (*winp,
5691                          GTK_WIDGET(text_view)->window);
5692         }
5693       else
5694         {
5695           if ((*winp)->requisition.width == width)
5696             return;
5697         }
5698
5699       gtk_widget_queue_resize (GTK_WIDGET (text_view));
5700     }
5701 }
5702
5703
5704 static void
5705 set_window_height (GtkTextView      *text_view,
5706                    gint              height,
5707                    GtkTextWindowType type,
5708                    GtkTextWindow   **winp)
5709 {
5710   if (height == 0)
5711     {
5712       if (*winp)
5713         {
5714           text_window_free (*winp);
5715           *winp = NULL;
5716           gtk_widget_queue_resize (GTK_WIDGET (text_view));
5717         }
5718     }
5719   else
5720     {
5721       if (*winp == NULL)
5722         {
5723           *winp = text_window_new (type,
5724                                    GTK_WIDGET (text_view),
5725                                    0, height);
5726
5727           /* if the widget is already realized we need to realize the child manually */
5728           if (GTK_WIDGET_REALIZED (text_view))
5729             text_window_realize (*winp,
5730                          GTK_WIDGET(text_view)->window);
5731         }
5732       else
5733         {
5734           if ((*winp)->requisition.height == height)
5735             return;
5736         }
5737
5738       gtk_widget_queue_resize (GTK_WIDGET (text_view));
5739     }
5740 }
5741
5742 /**
5743  * gtk_text_view_set_border_window_size:
5744  * @text_view: a #GtkTextView
5745  * @type: window to affect
5746  * @size: width or height of the window
5747  *
5748  * Sets the width of %GTK_TEXT_WINDOW_LEFT or %GTK_TEXT_WINDOW_RIGHT,
5749  * or the height of %GTK_TEXT_WINDOW_TOP or %GTK_TEXT_WINDOW_BOTTOM.
5750  * Automatically destroys the corresponding window if the size is set to 0,
5751  * and creates the window if the size is set to non-zero.
5752  **/
5753 void
5754 gtk_text_view_set_border_window_size (GtkTextView      *text_view,
5755                                       GtkTextWindowType type,
5756                                       gint              size)
5757
5758 {
5759   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
5760   g_return_if_fail (size >= 0);
5761   g_return_if_fail (type != GTK_TEXT_WINDOW_WIDGET);
5762   g_return_if_fail (type != GTK_TEXT_WINDOW_TEXT);
5763
5764   switch (type)
5765     {
5766     case GTK_TEXT_WINDOW_LEFT:
5767       set_window_width (text_view, size, GTK_TEXT_WINDOW_LEFT,
5768                         &text_view->left_window);
5769       break;
5770
5771     case GTK_TEXT_WINDOW_RIGHT:
5772       set_window_width (text_view, size, GTK_TEXT_WINDOW_RIGHT,
5773                         &text_view->right_window);
5774       break;
5775
5776     case GTK_TEXT_WINDOW_TOP:
5777       set_window_height (text_view, size, GTK_TEXT_WINDOW_TOP,
5778                          &text_view->top_window);
5779       break;
5780
5781     case GTK_TEXT_WINDOW_BOTTOM:
5782       set_window_height (text_view, size, GTK_TEXT_WINDOW_BOTTOM,
5783                          &text_view->bottom_window);
5784       break;
5785
5786     default:
5787       g_warning ("Can't set size of center or widget or private GtkTextWindowType in %s", G_STRLOC);
5788       break;
5789     }
5790 }
5791
5792 /**
5793  * gtk_text_view_set_text_window_size:
5794  * @text_view: a #GtkTextView
5795  * @width: a width in pixels
5796  * @height: a height in pixels
5797  *
5798  * Sets the size request for the main text window (%GTK_TEXT_WINDOW_TEXT).
5799  * If the widget gets more space than it requested, the main text window
5800  * will be larger than this.
5801  *
5802  **/
5803 void
5804 gtk_text_view_set_text_window_size (GtkTextView *text_view,
5805                                     gint         width,
5806                                     gint         height)
5807 {
5808   GtkTextWindow *win;
5809
5810   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
5811   g_return_if_fail (width > 0);
5812   g_return_if_fail (height > 0);
5813
5814   win = text_view->text_window;
5815
5816   if (win->requisition.width == width &&
5817       win->requisition.height == height)
5818     return;
5819
5820   win->requisition.width = width;
5821   win->requisition.height = height;
5822
5823   gtk_widget_queue_resize (GTK_WIDGET (text_view));
5824 }
5825
5826 /*
5827  * Child widgets
5828  */
5829
5830 static GtkTextViewChild*
5831 text_view_child_new_anchored (GtkWidget          *child,
5832                               GtkTextChildAnchor *anchor,
5833                               GtkTextLayout      *layout)
5834 {
5835   GtkTextViewChild *vc;
5836
5837   vc = g_new (GtkTextViewChild, 1);
5838
5839   vc->widget = child;
5840   vc->anchor = anchor;
5841
5842   vc->from_top_of_line = 0;
5843   vc->from_left_of_buffer = 0;
5844   
5845   g_object_ref (G_OBJECT (vc->widget));
5846   g_object_ref (G_OBJECT (vc->anchor));
5847
5848   g_object_set_data (G_OBJECT (child),
5849                      "gtk-text-view-child",
5850                      vc);
5851
5852   gtk_text_child_anchor_register_child (anchor, child, layout);
5853   
5854   return vc;
5855 }
5856
5857 static GtkTextViewChild*
5858 text_view_child_new_window (GtkWidget          *child,
5859                             GtkTextWindowType   type,
5860                             gint                x,
5861                             gint                y)
5862 {
5863   GtkTextViewChild *vc;
5864
5865   vc = g_new (GtkTextViewChild, 1);
5866
5867   vc->widget = child;
5868   vc->anchor = NULL;
5869
5870   vc->from_top_of_line = 0;
5871   vc->from_left_of_buffer = 0;
5872   
5873   g_object_ref (G_OBJECT (vc->widget));
5874
5875   vc->type = type;
5876   vc->x = x;
5877   vc->y = y;
5878
5879   return vc;
5880 }
5881
5882 static void
5883 text_view_child_free (GtkTextViewChild *child)
5884 {
5885   g_object_set_data (G_OBJECT (child->widget),
5886                      "gtk-text-view-child", NULL);
5887
5888   if (child->anchor)
5889     {
5890       gtk_text_child_anchor_unregister_child (child->anchor,
5891                                               child->widget);
5892       g_object_unref (G_OBJECT (child->anchor));
5893     }
5894
5895   g_object_unref (G_OBJECT (child->widget));
5896
5897   g_free (child);
5898 }
5899
5900 static void
5901 text_view_child_realize (GtkTextView      *text_view,
5902                          GtkTextViewChild *vc)
5903 {
5904   if (vc->anchor)
5905     gtk_widget_set_parent_window (vc->widget,
5906                                   text_view->text_window->bin_window);
5907   else
5908     {
5909       GdkWindow *window;
5910       window = gtk_text_view_get_window (text_view,
5911                                          vc->type);
5912       gtk_widget_set_parent_window (vc->widget, window);
5913     }
5914
5915   gtk_widget_realize (vc->widget);
5916 }
5917
5918 static void
5919 add_child (GtkTextView      *text_view,
5920            GtkTextViewChild *vc)
5921 {
5922   text_view->children = g_slist_prepend (text_view->children,
5923                                          vc);
5924
5925   gtk_widget_set_parent (vc->widget, GTK_WIDGET (text_view));
5926
5927   if (GTK_WIDGET_REALIZED (text_view))
5928     text_view_child_realize (text_view, vc);
5929
5930   if (GTK_WIDGET_VISIBLE (text_view) && GTK_WIDGET_VISIBLE (vc->widget))
5931     {
5932       if (GTK_WIDGET_MAPPED (text_view))
5933         gtk_widget_map (vc->widget);
5934
5935       gtk_widget_queue_resize (vc->widget);
5936     }
5937 }
5938
5939 void
5940 gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
5941                                    GtkWidget            *child,
5942                                    GtkTextChildAnchor   *anchor)
5943 {
5944   GtkTextViewChild *vc;
5945
5946   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
5947   g_return_if_fail (GTK_IS_WIDGET (child));
5948   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
5949   g_return_if_fail (child->parent == NULL);
5950
5951   gtk_text_view_ensure_layout (text_view);
5952
5953   vc = text_view_child_new_anchored (child, anchor,
5954                                      text_view->layout);
5955
5956   add_child (text_view, vc);
5957 }
5958
5959 void
5960 gtk_text_view_add_child_in_window (GtkTextView          *text_view,
5961                                    GtkWidget            *child,
5962                                    GtkTextWindowType     which_window,
5963                                    gint                  xpos,
5964                                    gint                  ypos)
5965 {
5966   GtkTextViewChild *vc;
5967
5968   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
5969   g_return_if_fail (GTK_IS_WIDGET (child));
5970   g_return_if_fail (xpos >= 0);
5971   g_return_if_fail (ypos >= 0);
5972   g_return_if_fail (child->parent == NULL);
5973
5974   vc = text_view_child_new_window (child, which_window,
5975                                    xpos, ypos);
5976
5977   add_child (text_view, vc);
5978 }
5979
5980 void
5981 gtk_text_view_move_child          (GtkTextView          *text_view,
5982                                    GtkWidget            *child,
5983                                    gint                  xpos,
5984                                    gint                  ypos)
5985 {
5986   GtkTextViewChild *vc;
5987
5988   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
5989   g_return_if_fail (GTK_IS_WIDGET (child));
5990   g_return_if_fail (xpos >= 0);
5991   g_return_if_fail (ypos >= 0);
5992   g_return_if_fail (child->parent == (GtkWidget*) text_view);
5993
5994   vc = g_object_get_data (G_OBJECT (child),
5995                           "gtk-text-view-child");
5996
5997   g_assert (vc != NULL);
5998
5999   vc->x = xpos;
6000   vc->y = ypos;
6001
6002   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (text_view))
6003     gtk_widget_queue_resize (child);
6004 }
6005
6006
6007
6008 /* Iterator operations */
6009
6010 gboolean
6011 gtk_text_view_forward_display_line (GtkTextView *text_view,
6012                                     GtkTextIter *iter)
6013 {
6014   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
6015   g_return_val_if_fail (iter != NULL, FALSE);
6016
6017   gtk_text_view_ensure_layout (text_view);
6018
6019   return gtk_text_layout_move_iter_to_next_line (text_view->layout, iter);
6020 }
6021
6022 gboolean
6023 gtk_text_view_backward_display_line (GtkTextView *text_view,
6024                                      GtkTextIter *iter)
6025 {
6026   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
6027   g_return_val_if_fail (iter != NULL, FALSE);
6028
6029   gtk_text_view_ensure_layout (text_view);
6030
6031   return gtk_text_layout_move_iter_to_previous_line (text_view->layout, iter);
6032 }
6033
6034 gboolean
6035 gtk_text_view_forward_display_line_end (GtkTextView *text_view,
6036                                         GtkTextIter *iter)
6037 {
6038   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
6039   g_return_val_if_fail (iter != NULL, FALSE);
6040
6041   gtk_text_view_ensure_layout (text_view);
6042
6043   return gtk_text_layout_move_iter_to_line_end (text_view->layout, iter, 1);
6044 }
6045
6046 gboolean
6047 gtk_text_view_backward_display_line_start (GtkTextView *text_view,
6048                                            GtkTextIter *iter)
6049 {
6050   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
6051   g_return_val_if_fail (iter != NULL, FALSE);
6052
6053   gtk_text_view_ensure_layout (text_view);
6054
6055   return gtk_text_layout_move_iter_to_line_end (text_view->layout, iter, -1);
6056 }
6057
6058 gboolean
6059 gtk_text_view_starts_display_line (GtkTextView       *text_view,
6060                                    const GtkTextIter *iter)
6061 {
6062   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
6063   g_return_val_if_fail (iter != NULL, FALSE);
6064
6065   gtk_text_view_ensure_layout (text_view);
6066
6067   return gtk_text_layout_iter_starts_line (text_view->layout, iter);
6068 }
6069
6070 gboolean
6071 gtk_text_view_move_visually (GtkTextView *text_view,
6072                              GtkTextIter *iter,
6073                              gint         count)
6074 {
6075   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
6076   g_return_val_if_fail (iter != NULL, FALSE);
6077
6078   gtk_text_view_ensure_layout (text_view);
6079
6080   return gtk_text_layout_move_iter_visually (text_view->layout, iter, count);
6081 }
6082
6083