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