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