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