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