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