]> Pileus Git - ~andy/gtk/blob - gtk/gtktextview.c
remove unused variable (gtk_text_view_get_window): add warning about how
[~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   
4708   g_return_if_fail (text_view->hadjustment != NULL);
4709
4710   adj = text_view->hadjustment;
4711
4712   /* Validate the line that we're moving within.
4713    */
4714   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
4715                                     &new_insert,
4716                                     gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
4717   gtk_text_layout_get_line_yrange (text_view->layout, &new_insert, &y, &height);
4718   gtk_text_layout_validate_yrange (text_view->layout, &new_insert, y, y + height);
4719   /* FIXME do we need to update the adjustment ranges here? */
4720   
4721   if (count < 0 && adj->value <= (adj->lower + 1e-12))
4722     {
4723       /* already at far left, just be sure we are at offset 0 */
4724       gtk_text_iter_set_line_offset (&new_insert, 0);
4725       gtk_text_buffer_place_cursor (get_buffer (text_view), &new_insert);
4726     }
4727   else if (count > 0 && adj->value >= (adj->upper - adj->page_size - 1e-12))
4728     {
4729       /* already at far right, just be sure we are at the end */
4730       gtk_text_iter_forward_to_line_end (&new_insert);
4731       gtk_text_buffer_place_cursor (get_buffer (text_view), &new_insert);
4732     }
4733   else
4734     {
4735       gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
4736
4737       newval = adj->value;
4738       oldval = adj->value;
4739   
4740       newval += count * adj->page_increment;
4741
4742       set_adjustment_clamped (adj, newval);
4743       cursor_x_pos += adj->value - oldval;
4744
4745       gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos);
4746       clamp_iter_onscreen (text_view, &new_insert);
4747       gtk_text_buffer_place_cursor (get_buffer (text_view), &new_insert);
4748
4749       gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
4750     }
4751
4752   /*  FIXME for lines shorter than the overall widget width, this results in a
4753    *  "bounce" effect as we scroll to the right of the widget, then scroll
4754    *  back to get the end of the line onscreen.
4755    *      http://bugzilla.gnome.org/show_bug.cgi?id=68963
4756    */
4757   
4758   /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
4759    * only guarantees 1 pixel onscreen.
4760    */
4761   DV(g_print (G_STRLOC": scrolling onscreen\n"));
4762   gtk_text_view_scroll_mark_onscreen (text_view,
4763                                       gtk_text_buffer_get_mark (get_buffer (text_view),
4764                                                                 "insert"));
4765 }
4766
4767 static gboolean
4768 whitespace (gunichar ch, gpointer user_data)
4769 {
4770   return (ch == ' ' || ch == '\t');
4771 }
4772
4773 static gboolean
4774 not_whitespace (gunichar ch, gpointer user_data)
4775 {
4776   return !whitespace (ch, user_data);
4777 }
4778
4779 static gboolean
4780 find_whitepace_region (const GtkTextIter *center,
4781                        GtkTextIter *start, GtkTextIter *end)
4782 {
4783   *start = *center;
4784   *end = *center;
4785
4786   if (gtk_text_iter_backward_find_char (start, not_whitespace, NULL, NULL))
4787     gtk_text_iter_forward_char (start); /* we want the first whitespace... */
4788   if (whitespace (gtk_text_iter_get_char (end), NULL))
4789     gtk_text_iter_forward_find_char (end, not_whitespace, NULL, NULL);
4790
4791   return !gtk_text_iter_equal (start, end);
4792 }
4793
4794 static void
4795 gtk_text_view_insert_at_cursor (GtkTextView *text_view,
4796                                 const gchar *str)
4797 {
4798   gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
4799                                                 text_view->editable);
4800 }
4801
4802 static void
4803 gtk_text_view_delete_from_cursor (GtkTextView   *text_view,
4804                                   GtkDeleteType  type,
4805                                   gint           count)
4806 {
4807   GtkTextIter insert;
4808   GtkTextIter start;
4809   GtkTextIter end;
4810   gboolean leave_one = FALSE;
4811
4812   gtk_text_view_reset_im_context (text_view);
4813
4814   if (type == GTK_DELETE_CHARS)
4815     {
4816       /* Char delete deletes the selection, if one exists */
4817       if (gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
4818                                             text_view->editable))
4819         return;
4820     }
4821
4822   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
4823                                     &insert,
4824                                     gtk_text_buffer_get_mark (get_buffer (text_view),
4825                                                               "insert"));
4826
4827   start = insert;
4828   end = insert;
4829
4830   switch (type)
4831     {
4832     case GTK_DELETE_CHARS:
4833       gtk_text_iter_forward_cursor_positions (&end, count);
4834       break;
4835
4836     case GTK_DELETE_WORD_ENDS:
4837       if (count > 0)
4838         gtk_text_iter_forward_word_ends (&end, count);
4839       else if (count < 0)
4840         gtk_text_iter_backward_word_starts (&start, 0 - count);
4841       break;
4842
4843     case GTK_DELETE_WORDS:
4844       break;
4845
4846     case GTK_DELETE_DISPLAY_LINE_ENDS:
4847       break;
4848
4849     case GTK_DELETE_DISPLAY_LINES:
4850       break;
4851
4852     case GTK_DELETE_PARAGRAPH_ENDS:
4853       /* If we're already at a newline, we need to
4854        * simply delete that newline, instead of
4855        * moving to the next one.
4856        */
4857       if (gtk_text_iter_ends_line (&end))
4858         {
4859           gtk_text_iter_forward_line (&end);
4860           --count;
4861         }
4862
4863       while (count > 0)
4864         {
4865           if (!gtk_text_iter_forward_to_line_end (&end))
4866             break;
4867
4868           --count;
4869         }
4870
4871       /* FIXME figure out what a negative count means
4872          and support that */
4873       break;
4874
4875     case GTK_DELETE_PARAGRAPHS:
4876       if (count > 0)
4877         {
4878           gtk_text_iter_set_line_offset (&start, 0);
4879           gtk_text_iter_forward_to_line_end (&end);
4880
4881           /* Do the lines beyond the first. */
4882           while (count > 1)
4883             {
4884               gtk_text_iter_forward_to_line_end (&end);
4885
4886               --count;
4887             }
4888         }
4889
4890       /* FIXME negative count? */
4891
4892       break;
4893
4894     case GTK_DELETE_WHITESPACE:
4895       {
4896         find_whitepace_region (&insert, &start, &end);
4897       }
4898       break;
4899
4900     default:
4901       break;
4902     }
4903
4904   if (!gtk_text_iter_equal (&start, &end))
4905     {
4906       gtk_text_buffer_begin_user_action (get_buffer (text_view));
4907
4908       if (gtk_text_buffer_delete_interactive (get_buffer (text_view), &start, &end,
4909                                               text_view->editable))
4910         {
4911           if (leave_one)
4912             gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view),
4913                                                           " ", 1,
4914                                                           text_view->editable);
4915         }
4916
4917       gtk_text_buffer_end_user_action (get_buffer (text_view));
4918
4919       DV(g_print (G_STRLOC": scrolling onscreen\n"));
4920       gtk_text_view_scroll_mark_onscreen (text_view,
4921                                           gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
4922     }
4923 }
4924
4925 static void
4926 gtk_text_view_cut_clipboard (GtkTextView *text_view)
4927 {
4928   gtk_text_buffer_cut_clipboard (get_buffer (text_view),
4929                                  gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
4930                                  text_view->editable);
4931   DV(g_print (G_STRLOC": scrolling onscreen\n"));
4932   gtk_text_view_scroll_mark_onscreen (text_view,
4933                                       gtk_text_buffer_get_mark (get_buffer (text_view),
4934                                                                 "insert"));
4935 }
4936
4937 static void
4938 gtk_text_view_copy_clipboard (GtkTextView *text_view)
4939 {
4940   gtk_text_buffer_copy_clipboard (get_buffer (text_view),
4941                                   gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
4942   DV(g_print (G_STRLOC": scrolling onscreen\n"));
4943   gtk_text_view_scroll_mark_onscreen (text_view,
4944                                       gtk_text_buffer_get_mark (get_buffer (text_view),
4945                                                                 "insert"));
4946 }
4947
4948 static void
4949 gtk_text_view_paste_clipboard (GtkTextView *text_view)
4950 {
4951   gtk_text_buffer_paste_clipboard (get_buffer (text_view),
4952                                    gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
4953                                    NULL,
4954                                    text_view->editable);
4955   DV(g_print (G_STRLOC": scrolling onscreen\n"));
4956   gtk_text_view_scroll_mark_onscreen (text_view,
4957                                       gtk_text_buffer_get_mark (get_buffer (text_view),
4958                                                                 "insert"));
4959 }
4960
4961 static void
4962 gtk_text_view_toggle_overwrite (GtkTextView *text_view)
4963 {
4964   text_view->overwrite_mode = !text_view->overwrite_mode;
4965 }
4966
4967 static void
4968 gtk_text_view_move_focus (GtkTextView     *text_view,
4969                           GtkDirectionType direction_type)
4970 {
4971   GtkWidget *toplevel;
4972
4973   toplevel =
4974     gtk_widget_get_ancestor (GTK_WIDGET (text_view), GTK_TYPE_WINDOW);
4975
4976   if (toplevel == NULL)
4977     return;
4978
4979   /* Propagate to toplevel */
4980   g_signal_emit_by_name (G_OBJECT (toplevel), "move_focus",
4981                          direction_type);
4982 }
4983
4984 /*
4985  * Selections
4986  */
4987
4988 static void
4989 gtk_text_view_unselect (GtkTextView *text_view)
4990 {
4991   GtkTextIter insert;
4992
4993   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
4994                                     &insert,
4995                                     gtk_text_buffer_get_mark (get_buffer (text_view),
4996                                                               "insert"));
4997
4998   gtk_text_buffer_move_mark (get_buffer (text_view),
4999                              gtk_text_buffer_get_mark (get_buffer (text_view),
5000                                                        "selection_bound"),
5001                              &insert);
5002 }
5003
5004 static void
5005 move_mark_to_pointer_and_scroll (GtkTextView *text_view,
5006                                  const gchar *mark_name)
5007 {
5008   gint x, y;
5009   GdkModifierType state;
5010   GtkTextIter newplace;
5011
5012   /*   DV(g_print (G_STRLOC": begin\n")); */
5013   
5014   gdk_window_get_pointer (text_view->text_window->bin_window,
5015                           &x, &y, &state);
5016
5017   /*   DV(g_print (G_STRLOC": get iter at pixel\n"); */
5018   gtk_text_layout_get_iter_at_pixel (text_view->layout,
5019                                      &newplace,
5020                                      x + text_view->xoffset,
5021                                      y + text_view->yoffset);
5022
5023   {
5024     GtkTextMark *mark =
5025       gtk_text_buffer_get_mark (get_buffer (text_view), mark_name);
5026
5027     DV(g_print (G_STRLOC": move mark\n"));
5028     gtk_text_buffer_move_mark (get_buffer (text_view),
5029                                mark,
5030                                &newplace);
5031
5032     DV(g_print (G_STRLOC": scrolling onscreen\n"));
5033     gtk_text_view_scroll_mark_onscreen (text_view, mark);
5034   }
5035 }
5036
5037 static gint
5038 selection_scan_timeout (gpointer data)
5039 {
5040   GtkTextView *text_view;
5041
5042   GDK_THREADS_ENTER ();
5043   
5044   text_view = GTK_TEXT_VIEW (data);
5045
5046   DV(g_print (G_STRLOC": calling move_mark_to_pointer_and_scroll\n"));
5047   move_mark_to_pointer_and_scroll (text_view, "insert");
5048
5049   GDK_THREADS_LEAVE ();
5050   
5051   return TRUE; /* remain installed. */
5052 }
5053
5054 #define DND_SCROLL_MARGIN 0.20
5055
5056 static gint
5057 drag_scan_timeout (gpointer data)
5058 {
5059   GtkTextView *text_view;
5060   gint x, y;
5061   GdkModifierType state;
5062   GtkTextIter newplace;
5063
5064   GDK_THREADS_ENTER ();
5065   
5066   text_view = GTK_TEXT_VIEW (data);
5067
5068   gdk_window_get_pointer (text_view->text_window->bin_window,
5069                           &x, &y, &state);
5070   
5071   gtk_text_layout_get_iter_at_pixel (text_view->layout,
5072                                      &newplace,
5073                                      x + text_view->xoffset,
5074                                      y + text_view->yoffset);
5075   
5076   gtk_text_buffer_move_mark (get_buffer (text_view),
5077                              text_view->dnd_mark,
5078                              &newplace);
5079
5080   DV(g_print (G_STRLOC": scrolling onscreen\n"));
5081   gtk_text_view_scroll_to_mark (text_view,
5082                                 text_view->dnd_mark,
5083                                 DND_SCROLL_MARGIN, FALSE, 0.0, 0.0);
5084
5085   GDK_THREADS_LEAVE ();
5086   
5087   return TRUE;
5088 }
5089
5090 static gint
5091 selection_motion_event_handler (GtkTextView *text_view, GdkEventMotion *event, gpointer data)
5092 {
5093   DV(g_print (G_STRLOC": calling move_mark_to_pointer_and_scroll\n"));
5094   move_mark_to_pointer_and_scroll (text_view, "insert");
5095
5096   /* If we had to scroll offscreen, insert a timeout to do so
5097    * again. Note that in the timeout, even if the mouse doesn't
5098    * move, due to this scroll xoffset/yoffset will have changed
5099    * and we'll need to scroll again.
5100    */
5101   if (text_view->scroll_timeout != 0) /* reset on every motion event */
5102     gtk_timeout_remove (text_view->scroll_timeout);
5103   
5104   text_view->scroll_timeout =
5105     gtk_timeout_add (50, selection_scan_timeout, text_view);
5106
5107   return TRUE;
5108 }
5109
5110 static void
5111 gtk_text_view_start_selection_drag (GtkTextView       *text_view,
5112                                     const GtkTextIter *iter,
5113                                     GdkEventButton    *button)
5114 {
5115   GtkTextIter newplace;
5116   GtkTextBuffer *buffer;
5117   
5118   g_return_if_fail (text_view->selection_drag_handler == 0);
5119
5120   gtk_grab_add (GTK_WIDGET (text_view));
5121
5122   buffer = get_buffer (text_view);
5123   
5124   newplace = *iter;
5125
5126   if (button->state & GDK_SHIFT_MASK)
5127     {
5128       /* Extend selection */
5129       GtkTextIter start, end;
5130
5131       gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
5132
5133       if (gtk_text_iter_compare (&newplace, &start) <= 0)
5134         {
5135           gtk_text_buffer_move_mark_by_name (buffer, "insert",
5136                                              &newplace);
5137
5138           gtk_text_buffer_move_mark_by_name (buffer, "selection_bound",
5139                                              &end);
5140         }
5141       else if (gtk_text_iter_compare (&newplace, &end) >= 0)
5142         {
5143           gtk_text_buffer_move_mark_by_name (buffer, "insert",
5144                                              &newplace);
5145
5146           gtk_text_buffer_move_mark_by_name (buffer, "selection_bound",
5147                                              &start);
5148         }
5149     }
5150   else
5151     {
5152       /* Replace selection */
5153       gtk_text_buffer_place_cursor (buffer, &newplace);
5154     }
5155
5156   text_view->selection_drag_handler = gtk_signal_connect (GTK_OBJECT (text_view),
5157                                                           "motion_notify_event",
5158                                                           GTK_SIGNAL_FUNC (selection_motion_event_handler),
5159                                                           NULL);
5160 }
5161
5162 /* returns whether we were really dragging */
5163 static gboolean
5164 gtk_text_view_end_selection_drag (GtkTextView *text_view, GdkEventButton *event)
5165 {
5166   if (text_view->selection_drag_handler == 0)
5167     return FALSE;
5168
5169   gtk_signal_disconnect (GTK_OBJECT (text_view), text_view->selection_drag_handler);
5170   text_view->selection_drag_handler = 0;
5171
5172   if (text_view->scroll_timeout != 0)
5173     {
5174       gtk_timeout_remove (text_view->scroll_timeout);
5175       text_view->scroll_timeout = 0;
5176     }
5177
5178   /* one last update to current position */
5179   DV(g_print (G_STRLOC": calling move_mark_to_pointer_and_scroll\n"));
5180   move_mark_to_pointer_and_scroll (text_view, "insert");
5181
5182   gtk_grab_remove (GTK_WIDGET (text_view));
5183
5184   return TRUE;
5185 }
5186
5187 /*
5188  * Layout utils
5189  */
5190
5191 static void
5192 gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
5193                                          GtkTextAttributes  *values,
5194                                          GtkStyle           *style)
5195 {
5196   values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
5197   values->appearance.fg_color = style->text[GTK_STATE_NORMAL];
5198
5199   if (values->font)
5200     pango_font_description_free (values->font);
5201
5202   values->font = pango_font_description_copy (style->font_desc);
5203 }
5204
5205 static void
5206 gtk_text_view_check_keymap_direction (GtkTextView *text_view)
5207 {
5208   if (text_view->layout)
5209     {
5210       gboolean split_cursor;
5211       GtkTextDirection new_dir;
5212       GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
5213   
5214       g_object_get (G_OBJECT (settings),
5215                     "gtk-split-cursor", &split_cursor,
5216                     NULL);
5217       if (split_cursor)
5218         new_dir = GTK_TEXT_DIR_NONE;
5219       else
5220         new_dir = (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
5221           GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
5222       
5223       if (text_view->layout->cursor_direction != new_dir)
5224         gtk_text_layout_set_cursor_direction (text_view->layout, new_dir);
5225     }
5226 }
5227
5228 static void
5229 gtk_text_view_ensure_layout (GtkTextView *text_view)
5230 {
5231   GtkWidget *widget;
5232
5233   widget = GTK_WIDGET (text_view);
5234
5235   if (text_view->layout == NULL)
5236     {
5237       GtkTextAttributes *style;
5238       PangoContext *ltr_context, *rtl_context;
5239       GSList *tmp_list;
5240
5241       DV(g_print(G_STRLOC"\n"));
5242       
5243       text_view->layout = gtk_text_layout_new ();
5244
5245       g_signal_connect (G_OBJECT (text_view->layout),
5246                         "invalidated",
5247                         G_CALLBACK (invalidated_handler),
5248                         text_view);
5249
5250       g_signal_connect (G_OBJECT (text_view->layout),
5251                         "changed",
5252                         G_CALLBACK (changed_handler),
5253                         text_view);
5254
5255       g_signal_connect (G_OBJECT (text_view->layout),
5256                         "allocate_child",
5257                         G_CALLBACK (gtk_text_view_child_allocated),
5258                         text_view);
5259       
5260       if (get_buffer (text_view))
5261         gtk_text_layout_set_buffer (text_view->layout, get_buffer (text_view));
5262
5263       if ((GTK_WIDGET_HAS_FOCUS (text_view) && text_view->cursor_visible))
5264         gtk_text_view_pend_cursor_blink (text_view);
5265       else
5266         gtk_text_layout_set_cursor_visible (text_view->layout, FALSE);
5267
5268       ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
5269       pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
5270       rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
5271       pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
5272
5273       gtk_text_layout_set_contexts (text_view->layout, ltr_context, rtl_context);
5274
5275       g_object_unref (G_OBJECT (ltr_context));
5276       g_object_unref (G_OBJECT (rtl_context));
5277
5278       gtk_text_view_check_keymap_direction (text_view);
5279
5280       style = gtk_text_attributes_new ();
5281
5282       gtk_widget_ensure_style (widget);
5283       gtk_text_view_set_attributes_from_style (text_view,
5284                                                style, widget->style);
5285
5286       style->pixels_above_lines = text_view->pixels_above_lines;
5287       style->pixels_below_lines = text_view->pixels_below_lines;
5288       style->pixels_inside_wrap = text_view->pixels_inside_wrap;
5289       style->left_margin = text_view->left_margin;
5290       style->right_margin = text_view->right_margin;
5291       style->indent = text_view->indent;
5292       style->tabs = text_view->tabs ? pango_tab_array_copy (text_view->tabs) : NULL;
5293
5294       style->wrap_mode = text_view->wrap_mode;
5295       style->justification = text_view->justify;
5296       style->direction = gtk_widget_get_direction (GTK_WIDGET (text_view));
5297
5298       gtk_text_layout_set_default_style (text_view->layout, style);
5299
5300       gtk_text_attributes_unref (style);
5301
5302       /* Set layout for all anchored children */
5303
5304       tmp_list = text_view->children;
5305       while (tmp_list != NULL)
5306         {
5307           GtkTextViewChild *vc = tmp_list->data;
5308
5309           if (vc->anchor)
5310             {
5311               gtk_text_anchored_child_set_layout (vc->widget,
5312                                                   text_view->layout);
5313               /* vc may now be invalid! */
5314             }
5315
5316           tmp_list = g_slist_next (tmp_list);
5317         }
5318     }
5319 }
5320
5321 /**
5322  * gtk_text_view_get_default_attributes:
5323  * @text_view: a #GtkTextView
5324  * 
5325  * Obtains a copy of the default text attributes. These are the
5326  * attributes used for text unless a tag overrides them.
5327  * You'd typically pass the default attributes in to
5328  * gtk_text_iter_get_attributes() in order to get the
5329  * attributes in effect at a given text position.
5330  *
5331  * The return value is a copy owned by the caller of this function,
5332  * and should be freed.
5333  * 
5334  * Return value: a new #GtkTextAttributes
5335  **/
5336 GtkTextAttributes*
5337 gtk_text_view_get_default_attributes (GtkTextView *text_view)
5338 {
5339   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
5340   
5341   gtk_text_view_ensure_layout (text_view);
5342
5343   return gtk_text_attributes_copy (text_view->layout->default_style);
5344 }
5345
5346 static void
5347 gtk_text_view_destroy_layout (GtkTextView *text_view)
5348 {
5349   if (text_view->layout)
5350     {
5351       GSList *tmp_list;
5352
5353       gtk_text_view_remove_validate_idles (text_view);
5354       
5355       /* Remove layout from all anchored children */
5356       tmp_list = text_view->children;
5357       while (tmp_list != NULL)
5358         {
5359           GtkTextViewChild *vc = tmp_list->data;
5360
5361           if (vc->anchor)
5362             {
5363               gtk_text_anchored_child_set_layout (vc->widget, NULL);
5364               /* vc may now be invalid! */
5365             }
5366
5367           tmp_list = g_slist_next (tmp_list);
5368         }
5369       
5370       gtk_text_view_stop_cursor_blink (text_view);
5371       gtk_text_view_end_selection_drag (text_view, NULL);
5372
5373       g_signal_handlers_disconnect_by_func (G_OBJECT (text_view->layout),
5374                                             invalidated_handler, text_view);
5375       g_signal_handlers_disconnect_by_func (G_OBJECT (text_view->layout),
5376                                             changed_handler, text_view);
5377       g_object_unref (G_OBJECT (text_view->layout));
5378       text_view->layout = NULL;
5379     }
5380 }
5381
5382 static void
5383 gtk_text_view_reset_im_context (GtkTextView *text_view)
5384 {
5385   if (text_view->need_im_reset)
5386     {
5387       text_view->need_im_reset = FALSE;
5388       gtk_im_context_reset (text_view->im_context);
5389     }
5390 }
5391
5392 /*
5393  * DND feature
5394  */
5395
5396 static void
5397 gtk_text_view_start_selection_dnd (GtkTextView       *text_view,
5398                                    const GtkTextIter *iter,
5399                                    GdkEventMotion    *event)
5400 {
5401   GdkDragContext *context;
5402   GtkTargetList *target_list;
5403
5404   text_view->drag_start_x = -1;
5405   text_view->drag_start_y = -1;
5406   text_view->pending_place_cursor_button = 0;
5407   
5408   target_list = gtk_target_list_new (target_table,
5409                                      G_N_ELEMENTS (target_table));
5410
5411   context = gtk_drag_begin (GTK_WIDGET (text_view), target_list,
5412                             GDK_ACTION_COPY | GDK_ACTION_MOVE,
5413                             1, (GdkEvent*)event);
5414
5415   gtk_target_list_unref (target_list);
5416
5417   gtk_drag_set_icon_default (context);
5418 }
5419
5420 static void
5421 gtk_text_view_drag_begin (GtkWidget        *widget,
5422                           GdkDragContext   *context)
5423 {
5424   /* do nothing */
5425 }
5426
5427 static void
5428 gtk_text_view_drag_end (GtkWidget        *widget,
5429                         GdkDragContext   *context)
5430 {
5431   GtkTextView *text_view;
5432
5433   text_view = GTK_TEXT_VIEW (widget);
5434 }
5435
5436 static void
5437 gtk_text_view_drag_data_get (GtkWidget        *widget,
5438                              GdkDragContext   *context,
5439                              GtkSelectionData *selection_data,
5440                              guint             info,
5441                              guint             time)
5442 {
5443   GtkTextView *text_view;
5444
5445   text_view = GTK_TEXT_VIEW (widget);
5446
5447   if (selection_data->target == gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE))
5448     {
5449       GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
5450
5451       gtk_selection_data_set (selection_data,
5452                               gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE),
5453                               8, /* bytes */
5454                               (void*)&buffer,
5455                               sizeof (buffer));
5456     }
5457   else
5458     {
5459       gchar *str;
5460       GtkTextIter start;
5461       GtkTextIter end;
5462
5463       str = NULL;
5464
5465       if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
5466                                                 &start, &end))
5467         {
5468           /* Extract the selected text */
5469           str = gtk_text_iter_get_visible_text (&start, &end);
5470         }
5471
5472       if (str)
5473         {
5474           gtk_selection_data_set_text (selection_data, str, -1);
5475           g_free (str);
5476         }
5477     }
5478 }
5479
5480 static void
5481 gtk_text_view_drag_data_delete (GtkWidget        *widget,
5482                                 GdkDragContext   *context)
5483 {
5484   GtkTextView *text_view;
5485
5486   text_view = GTK_TEXT_VIEW (widget);
5487
5488   gtk_text_buffer_delete_selection (GTK_TEXT_VIEW (widget)->buffer,
5489                                     TRUE, GTK_TEXT_VIEW (widget)->editable);
5490 }
5491
5492 static void
5493 gtk_text_view_drag_leave (GtkWidget        *widget,
5494                           GdkDragContext   *context,
5495                           guint             time)
5496 {
5497   GtkTextView *text_view;
5498
5499   text_view = GTK_TEXT_VIEW (widget);
5500
5501   gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
5502   
5503   if (text_view->scroll_timeout != 0)
5504     gtk_timeout_remove (text_view->scroll_timeout);
5505
5506   text_view->scroll_timeout = 0;
5507 }
5508
5509 static gboolean
5510 gtk_text_view_drag_motion (GtkWidget        *widget,
5511                            GdkDragContext   *context,
5512                            gint              x,
5513                            gint              y,
5514                            guint             time)
5515 {
5516   GtkTextIter newplace;
5517   GtkTextView *text_view;
5518   GtkTextIter start;
5519   GtkTextIter end;
5520   GdkRectangle target_rect;
5521   gint bx, by;
5522   GdkDragAction suggested_action = 0;
5523   
5524   text_view = GTK_TEXT_VIEW (widget);
5525
5526   target_rect = text_view->text_window->allocation;
5527   
5528   if (x < target_rect.x ||
5529       y < target_rect.y ||
5530       x > (target_rect.x + target_rect.width) ||
5531       y > (target_rect.y + target_rect.height))
5532     return FALSE; /* outside the text window, allow parent widgets to handle event */
5533
5534   gtk_text_view_window_to_buffer_coords (text_view,
5535                                          GTK_TEXT_WINDOW_WIDGET,
5536                                          x, y,
5537                                          &bx, &by);
5538
5539   gtk_text_layout_get_iter_at_pixel (text_view->layout,
5540                                      &newplace,
5541                                      bx, by);  
5542
5543   if (gtk_drag_dest_find_target (widget, context,
5544                                  gtk_drag_dest_get_target_list (widget)) == GDK_NONE)
5545     {
5546       /* can't accept any of the offered targets */
5547     }                                 
5548   else if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
5549                                             &start, &end) &&
5550            gtk_text_iter_in_range (&newplace, &start, &end))
5551     {
5552       /* We're inside the selection. */
5553     }
5554   else
5555     {      
5556       if (gtk_text_iter_can_insert (&newplace, text_view->editable))
5557         {
5558           GtkWidget *source_widget;
5559           
5560           suggested_action = context->suggested_action;
5561           
5562           source_widget = gtk_drag_get_source_widget (context);
5563           
5564           if (source_widget == widget)
5565             {
5566               /* Default to MOVE, unless the user has
5567                * pressed ctrl or alt to affect available actions
5568                */
5569               if ((context->actions & GDK_ACTION_MOVE) != 0)
5570                 suggested_action = GDK_ACTION_MOVE;
5571             }
5572         }
5573       else
5574         {
5575           /* Can't drop here. */
5576         }
5577     }
5578
5579   if (suggested_action != 0)
5580     {
5581       gtk_text_mark_set_visible (text_view->dnd_mark,
5582                                  text_view->cursor_visible);
5583       
5584       gdk_drag_status (context, suggested_action, time);
5585     }
5586   else
5587     {
5588       gdk_drag_status (context, 0, time);
5589       gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
5590     }
5591       
5592   gtk_text_buffer_move_mark (get_buffer (text_view),
5593                              text_view->dnd_mark,
5594                              &newplace);
5595
5596   DV(g_print (G_STRLOC": scrolling to mark\n"));
5597   gtk_text_view_scroll_to_mark (text_view,
5598                                 text_view->dnd_mark,
5599                                 DND_SCROLL_MARGIN, FALSE, 0.0, 0.0);
5600   
5601   if (text_view->scroll_timeout != 0) /* reset on every motion event */
5602     gtk_timeout_remove (text_view->scroll_timeout);
5603       
5604   text_view->scroll_timeout =
5605     gtk_timeout_add (50, drag_scan_timeout, text_view);
5606
5607   /* TRUE return means don't propagate the drag motion to parent
5608    * widgets that may also be drop sites.
5609    */
5610   return TRUE;
5611 }
5612
5613 static gboolean
5614 gtk_text_view_drag_drop (GtkWidget        *widget,
5615                          GdkDragContext   *context,
5616                          gint              x,
5617                          gint              y,
5618                          guint             time)
5619 {
5620   GtkTextView *text_view;
5621   GtkTextIter drop_point;
5622   GdkAtom target = GDK_NONE;
5623   
5624   text_view = GTK_TEXT_VIEW (widget);
5625   
5626   if (text_view->scroll_timeout != 0)
5627     gtk_timeout_remove (text_view->scroll_timeout);
5628
5629   text_view->scroll_timeout = 0;
5630
5631   gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
5632
5633   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
5634                                     &drop_point,
5635                                     text_view->dnd_mark);
5636
5637   if (gtk_text_iter_can_insert (&drop_point, text_view->editable))
5638     target = gtk_drag_dest_find_target (widget, context, NULL);
5639
5640   if (target != GDK_NONE)
5641     gtk_drag_get_data (widget, context, target, time);
5642   else
5643     gtk_drag_finish (context, FALSE, FALSE, time);
5644
5645   return TRUE;
5646 }
5647
5648 static void
5649 insert_text_data (GtkTextView      *text_view,
5650                   GtkTextIter      *drop_point,
5651                   GtkSelectionData *selection_data)
5652 {
5653   gchar *str;
5654
5655   str = gtk_selection_data_get_text (selection_data);
5656
5657   if (str)
5658     {
5659       gtk_text_buffer_insert_interactive (get_buffer (text_view),
5660                                           drop_point, str, -1,
5661                                           text_view->editable);
5662       g_free (str);
5663     }
5664 }
5665
5666 static void
5667 gtk_text_view_drag_data_received (GtkWidget        *widget,
5668                                   GdkDragContext   *context,
5669                                   gint              x,
5670                                   gint              y,
5671                                   GtkSelectionData *selection_data,
5672                                   guint             info,
5673                                   guint             time)
5674 {
5675   GtkTextIter drop_point;
5676   GtkTextView *text_view;
5677   gboolean success = FALSE;
5678
5679   text_view = GTK_TEXT_VIEW (widget);
5680
5681   if (!text_view->dnd_mark)
5682     goto done;
5683
5684   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
5685                                     &drop_point,
5686                                     text_view->dnd_mark);
5687   
5688   if (!gtk_text_iter_can_insert (&drop_point, text_view->editable))
5689     goto done;
5690
5691   if (selection_data->target == gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE))
5692     {
5693       GtkTextBuffer *src_buffer = NULL;
5694       GtkTextIter start, end;
5695       gboolean copy_tags = TRUE;
5696
5697       if (selection_data->length != sizeof (src_buffer))
5698         return;
5699
5700       memcpy (&src_buffer, selection_data->data, sizeof (src_buffer));
5701
5702       if (src_buffer == NULL)
5703         return;
5704
5705       g_return_if_fail (GTK_IS_TEXT_BUFFER (src_buffer));
5706
5707       if (gtk_text_buffer_get_tag_table (src_buffer) !=
5708           gtk_text_buffer_get_tag_table (get_buffer (text_view)))
5709         copy_tags = FALSE;
5710
5711       if (gtk_text_buffer_get_selection_bounds (src_buffer,
5712                                                 &start,
5713                                                 &end))
5714         {
5715           if (copy_tags)
5716             gtk_text_buffer_insert_range_interactive (get_buffer (text_view),
5717                                                       &drop_point,
5718                                                       &start,
5719                                                       &end,
5720                                                       text_view->editable);
5721           else
5722             {
5723               gchar *str;
5724
5725               str = gtk_text_iter_get_visible_text (&start, &end);
5726               gtk_text_buffer_insert_interactive (get_buffer (text_view),
5727                                                   &drop_point, str, -1,
5728                                                   text_view->editable);
5729               g_free (str);
5730             }
5731         }
5732     }
5733   else
5734     insert_text_data (text_view, &drop_point, selection_data);
5735
5736   success = TRUE;
5737
5738  done:
5739   gtk_drag_finish (context, success,
5740                    success && context->action == GDK_ACTION_MOVE,
5741                    time);
5742 }
5743
5744 static GtkAdjustment*
5745 get_hadjustment (GtkTextView *text_view)
5746 {
5747   if (text_view->hadjustment == NULL)
5748     gtk_text_view_set_scroll_adjustments (text_view,
5749                                           NULL, /* forces creation */
5750                                           text_view->vadjustment);
5751
5752   return text_view->hadjustment;
5753 }
5754
5755 static GtkAdjustment*
5756 get_vadjustment (GtkTextView *text_view)
5757 {
5758   if (text_view->vadjustment == NULL)
5759     gtk_text_view_set_scroll_adjustments (text_view,
5760                                           text_view->hadjustment,
5761                                           NULL); /* forces creation */
5762   return text_view->vadjustment;
5763 }
5764
5765
5766 static void
5767 gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
5768                                       GtkAdjustment *hadj,
5769                                       GtkAdjustment *vadj)
5770 {
5771   gboolean need_adjust = FALSE;
5772
5773   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
5774
5775   if (hadj)
5776     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
5777   else
5778     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
5779   if (vadj)
5780     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
5781   else
5782     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
5783
5784   if (text_view->hadjustment && (text_view->hadjustment != hadj))
5785     {
5786       gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->hadjustment), text_view);
5787       g_object_unref (G_OBJECT (text_view->hadjustment));
5788     }
5789
5790   if (text_view->vadjustment && (text_view->vadjustment != vadj))
5791     {
5792       gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->vadjustment), text_view);
5793       g_object_unref (G_OBJECT (text_view->vadjustment));
5794     }
5795
5796   if (text_view->hadjustment != hadj)
5797     {
5798       text_view->hadjustment = hadj;
5799       g_object_ref (G_OBJECT (text_view->hadjustment));
5800       gtk_object_sink (GTK_OBJECT (text_view->hadjustment));
5801       
5802       gtk_signal_connect (GTK_OBJECT (text_view->hadjustment), "value_changed",
5803                           (GtkSignalFunc) gtk_text_view_value_changed,
5804                           text_view);
5805       need_adjust = TRUE;
5806     }
5807
5808   if (text_view->vadjustment != vadj)
5809     {
5810       text_view->vadjustment = vadj;
5811       g_object_ref (G_OBJECT (text_view->vadjustment));
5812       gtk_object_sink (GTK_OBJECT (text_view->vadjustment));
5813       
5814       gtk_signal_connect (GTK_OBJECT (text_view->vadjustment), "value_changed",
5815                           (GtkSignalFunc) gtk_text_view_value_changed,
5816                           text_view);
5817       need_adjust = TRUE;
5818     }
5819
5820   if (need_adjust)
5821     gtk_text_view_value_changed (NULL, text_view);
5822 }
5823
5824 static void
5825 gtk_text_view_value_changed (GtkAdjustment *adj,
5826                              GtkTextView   *text_view)
5827 {
5828   GtkTextIter iter;
5829   gint line_top;
5830   gint dx = 0;
5831   gint dy = 0;
5832   
5833   /* Note that we oddly call this function with adj == NULL
5834    * sometimes
5835    */
5836   
5837   text_view->onscreen_validated = FALSE;
5838
5839   DV(g_print(">Scroll offset changed %s/%g, onscreen_validated = FALSE ("G_STRLOC")\n",
5840              adj == text_view->hadjustment ? "hadj" : adj == text_view->vadjustment ? "vadj" : "none",
5841              adj ? adj->value : 0.0));
5842   
5843   if (adj == text_view->hadjustment)
5844     {
5845       dx = text_view->xoffset - (gint)adj->value;
5846       text_view->xoffset = adj->value;
5847     }
5848   else if (adj == text_view->vadjustment)
5849     {
5850       dy = text_view->yoffset - (gint)adj->value;
5851       text_view->yoffset = adj->value;
5852
5853       if (text_view->layout)
5854         {
5855           gtk_text_layout_get_line_at_y (text_view->layout, &iter, adj->value, &line_top);
5856
5857           gtk_text_buffer_move_mark (get_buffer (text_view), text_view->first_para_mark, &iter);
5858
5859           text_view->first_para_pixels = adj->value - line_top;
5860         }
5861     }
5862   
5863   if (dx != 0 || dy != 0)
5864     {
5865       GSList *tmp_list;
5866
5867       if (GTK_WIDGET_REALIZED (text_view))
5868         {
5869           if (dy != 0)
5870             {
5871               if (text_view->left_window)
5872                 text_window_scroll (text_view->left_window, 0, dy);
5873               if (text_view->right_window)
5874                 text_window_scroll (text_view->right_window, 0, dy);
5875             }
5876       
5877           if (dx != 0)
5878             {
5879               if (text_view->top_window)
5880                 text_window_scroll (text_view->top_window, dx, 0);
5881               if (text_view->bottom_window)
5882                 text_window_scroll (text_view->bottom_window, dx, 0);
5883             }
5884       
5885           /* It looks nicer to scroll the main area last, because
5886            * it takes a while, and making the side areas update
5887            * afterward emphasizes the slowness of scrolling the
5888            * main area.
5889            */
5890           text_window_scroll (text_view->text_window, dx, dy);
5891         }
5892       
5893       /* Children are now "moved" in the text window, poke
5894        * into widget->allocation for each child
5895        */
5896       tmp_list = text_view->children;
5897       while (tmp_list != NULL)
5898         {
5899           GtkTextViewChild *child = tmp_list->data;
5900           
5901           if (child->anchor)
5902             {              
5903               child->widget->allocation.x -= dx;
5904               child->widget->allocation.y -= dy;
5905
5906 #if 0
5907               g_print ("allocation for %p tweaked to %d,%d\n",
5908                        child->widget,
5909                        child->widget->allocation.x,
5910                        child->widget->allocation.y);
5911 #endif
5912             }
5913           
5914           tmp_list = g_slist_next (tmp_list);
5915         }
5916     }
5917
5918   /* This could result in invalidation, which would install the
5919    * first_validate_idle, which would validate onscreen;
5920    * but we're going to go ahead and validate here, so
5921    * first_validate_idle shouldn't have anything to do.
5922    */
5923   gtk_text_view_update_layout_width (text_view);
5924   
5925   /* note that validation of onscreen could invoke this function
5926    * recursively, by scrolling to maintain first_para, or in response
5927    * to updating the layout width, however there is no problem with
5928    * that, or shouldn't be.
5929    */
5930   gtk_text_view_validate_onscreen (text_view);
5931   
5932   /* process exposes */
5933   if (GTK_WIDGET_REALIZED (text_view))
5934     {
5935       if (text_view->left_window)
5936         gdk_window_process_updates (text_view->left_window->bin_window, TRUE);
5937
5938       if (text_view->right_window)
5939         gdk_window_process_updates (text_view->right_window->bin_window, TRUE);
5940
5941       if (text_view->top_window)
5942         gdk_window_process_updates (text_view->top_window->bin_window, TRUE);
5943       
5944       if (text_view->bottom_window)
5945         gdk_window_process_updates (text_view->bottom_window->bin_window, TRUE);
5946   
5947       gdk_window_process_updates (text_view->text_window->bin_window, TRUE);
5948     }
5949
5950   /* If this got installed, get rid of it, it's just a waste of time. */
5951   if (text_view->first_validate_idle != 0)
5952     {
5953       g_source_remove (text_view->first_validate_idle);
5954       text_view->first_validate_idle = 0;
5955     }
5956
5957   gtk_text_view_update_im_spot_location (text_view);
5958   
5959   DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n"));
5960 }
5961
5962 static void
5963 gtk_text_view_commit_handler (GtkIMContext  *context,
5964                               const gchar   *str,
5965                               GtkTextView   *text_view)
5966 {
5967   gtk_text_view_commit_text (text_view, str);
5968 }
5969
5970 static void
5971 gtk_text_view_commit_text (GtkTextView   *text_view,
5972                            const gchar   *str)
5973 {
5974   gboolean had_selection;
5975   
5976   gtk_text_buffer_begin_user_action (get_buffer (text_view));
5977
5978   had_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
5979                                                         NULL, NULL);
5980   
5981   gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
5982                                     text_view->editable);
5983
5984   if (!strcmp (str, "\n"))
5985     {
5986       gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
5987                                                     text_view->editable);
5988     }
5989   else
5990     {
5991       if (!had_selection && text_view->overwrite_mode)
5992         gtk_text_view_delete_from_cursor (text_view, GTK_DELETE_CHARS, 1);
5993       gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
5994                                                     text_view->editable);
5995     }
5996
5997   gtk_text_buffer_end_user_action (get_buffer (text_view));
5998
5999   DV(g_print (G_STRLOC": scrolling onscreen\n"));
6000   gtk_text_view_scroll_mark_onscreen (text_view,
6001                                       gtk_text_buffer_get_mark (get_buffer (text_view),
6002                                                                 "insert"));
6003 }
6004
6005 static void
6006 gtk_text_view_preedit_changed_handler (GtkIMContext *context,
6007                                        GtkTextView  *text_view)
6008 {
6009   gchar *str;
6010   PangoAttrList *attrs;
6011   gint cursor_pos;
6012
6013   gtk_im_context_get_preedit_string (context, &str, &attrs, &cursor_pos);
6014   gtk_text_layout_set_preedit_string (text_view->layout, str, attrs, cursor_pos);
6015
6016   pango_attr_list_unref (attrs);
6017   g_free (str);
6018 }
6019
6020 static gboolean
6021 gtk_text_view_retrieve_surrounding_handler (GtkIMContext  *context,
6022                                             GtkTextView   *text_view)
6023 {
6024   GtkTextIter start;
6025   GtkTextIter end;
6026   gint pos;
6027   gchar *text;
6028
6029   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &start,  
6030                                     gtk_text_buffer_get_insert (text_view->buffer));
6031   end = start;
6032
6033   pos = gtk_text_iter_get_line_index (&start);
6034   gtk_text_iter_set_line_offset (&start, 0);
6035   gtk_text_iter_forward_to_line_end (&end);
6036
6037   text = gtk_text_iter_get_slice (&start, &end);
6038   gtk_im_context_set_surrounding (context, text, -1, pos);
6039   g_free (text);
6040
6041   return TRUE;
6042 }
6043
6044 static gboolean
6045 gtk_text_view_delete_surrounding_handler (GtkIMContext  *context,
6046                                           gint           offset,
6047                                           gint           n_chars,
6048                                           GtkTextView   *text_view)
6049 {
6050   GtkTextIter start;
6051   GtkTextIter end;
6052
6053   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &start,  
6054                                     gtk_text_buffer_get_insert (text_view->buffer));
6055   end = start;
6056
6057   gtk_text_iter_forward_chars (&start, offset);
6058   gtk_text_iter_forward_chars (&end, offset + n_chars);
6059
6060   gtk_text_buffer_delete (text_view->buffer, &start, &end);
6061
6062   return TRUE;
6063 }
6064
6065 static void
6066 gtk_text_view_mark_set_handler (GtkTextBuffer     *buffer,
6067                                 const GtkTextIter *location,
6068                                 GtkTextMark       *mark,
6069                                 gpointer           data)
6070 {
6071   GtkTextView *text_view = GTK_TEXT_VIEW (data);
6072   gboolean need_reset = FALSE;
6073
6074   if (mark == gtk_text_buffer_get_insert (buffer))
6075     {
6076       text_view->virtual_cursor_x = -1;
6077       text_view->virtual_cursor_y = -1;
6078       gtk_text_view_update_im_spot_location (text_view);
6079       need_reset = TRUE;
6080     }
6081   else if (mark == gtk_text_buffer_get_selection_bound (buffer))
6082     {
6083       need_reset = TRUE;
6084     }
6085
6086   if (need_reset)
6087     gtk_text_view_reset_im_context (text_view);
6088 }
6089
6090 static void
6091 gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
6092                                       gint        *x,
6093                                       gint        *y)
6094 {
6095   GdkRectangle strong_pos;
6096   GtkTextIter insert;
6097
6098   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
6099                                     gtk_text_buffer_get_mark (get_buffer (text_view),
6100                                                               "insert"));
6101
6102   if ((x && text_view->virtual_cursor_x == -1) ||
6103       (y && text_view->virtual_cursor_y == -1))
6104     gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
6105
6106   if (x)
6107     {
6108       if (text_view->virtual_cursor_x != -1)
6109         *x = text_view->virtual_cursor_x;
6110       else
6111         *x = strong_pos.x;
6112     }
6113
6114   if (y)
6115     {
6116       if (text_view->virtual_cursor_x != -1)
6117         *y = text_view->virtual_cursor_y;
6118       else
6119         *y = strong_pos.y + strong_pos.height / 2;
6120     }
6121 }
6122
6123 static void
6124 gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
6125                                       gint         x,
6126                                       gint         y)
6127 {
6128   GdkRectangle strong_pos;
6129   GtkTextIter insert;
6130
6131   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
6132                                     gtk_text_buffer_get_mark (get_buffer (text_view),
6133                                                               "insert"));
6134
6135   if (x == -1 || y == -1)
6136     gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
6137
6138   text_view->virtual_cursor_x = (x == -1) ? strong_pos.x : x;
6139   text_view->virtual_cursor_y = (y == -1) ? strong_pos.y + strong_pos.height / 2 : y;
6140 }
6141
6142 /* Quick hack of a popup menu
6143  */
6144 static void
6145 activate_cb (GtkWidget   *menuitem,
6146              GtkTextView *text_view)
6147 {
6148   const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
6149   gtk_signal_emit_by_name (GTK_OBJECT (text_view), signal);
6150 }
6151
6152 static void
6153 append_action_signal (GtkTextView  *text_view,
6154                       GtkWidget    *menu,
6155                       const gchar  *stock_id,
6156                       const gchar  *signal,
6157                       gboolean      sensitive)
6158 {
6159   GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
6160
6161   g_object_set_data (G_OBJECT (menuitem), "gtk-signal", (char *)signal);
6162   gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
6163                       GTK_SIGNAL_FUNC (activate_cb), text_view);
6164
6165   gtk_widget_set_sensitive (menuitem, sensitive);
6166   
6167   gtk_widget_show (menuitem);
6168   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6169 }
6170
6171 static void
6172 popup_menu_detach (GtkWidget *attach_widget,
6173                    GtkMenu   *menu)
6174 {
6175   GTK_TEXT_VIEW (attach_widget)->popup_menu = NULL;
6176 }
6177
6178 static void
6179 popup_position_func (GtkMenu   *menu,
6180                      gint      *x,
6181                      gint      *y,
6182                      gboolean  *push_in,
6183                      gpointer   user_data)
6184 {
6185   GtkTextView *text_view;
6186   GtkWidget *widget;
6187   GdkRectangle cursor_rect;
6188   GdkRectangle onscreen_rect;
6189   gint root_x, root_y;
6190   GtkTextIter iter;
6191   GtkRequisition req;      
6192   
6193   text_view = GTK_TEXT_VIEW (user_data);
6194   widget = GTK_WIDGET (text_view);
6195   
6196   g_return_if_fail (GTK_WIDGET_REALIZED (text_view));
6197
6198   gdk_window_get_origin (widget->window, &root_x, &root_y);
6199
6200   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
6201                                     &iter,
6202                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
6203
6204   gtk_text_view_get_iter_location (text_view,
6205                                    &iter,
6206                                    &cursor_rect);
6207
6208   gtk_text_view_get_visible_rect (text_view, &onscreen_rect);
6209   
6210   gtk_widget_size_request (text_view->popup_menu, &req);
6211
6212   /* can't use rectangle_intersect since cursor rect can have 0 width */
6213   if (cursor_rect.x >= onscreen_rect.x &&
6214       cursor_rect.x < onscreen_rect.x + onscreen_rect.width &&
6215       cursor_rect.y >= onscreen_rect.y &&
6216       cursor_rect.y < onscreen_rect.y + onscreen_rect.height)
6217     {    
6218       gtk_text_view_buffer_to_window_coords (text_view,
6219                                              GTK_TEXT_WINDOW_WIDGET,
6220                                              cursor_rect.x, cursor_rect.y,
6221                                              &cursor_rect.x, &cursor_rect.y);
6222
6223       *x = root_x + cursor_rect.x + cursor_rect.width;
6224       *y = root_y + cursor_rect.y + cursor_rect.height;
6225     }
6226   else
6227     {
6228       /* Just center the menu, since cursor is offscreen. */      
6229       *x = root_x + (widget->allocation.width / 2 - req.width / 2);
6230       *y = root_y + (widget->allocation.height / 2 - req.height / 2);      
6231     }
6232
6233   /* Ensure sanity */
6234   *x = CLAMP (*x, root_x, (root_x + widget->allocation.width));
6235   *y = CLAMP (*y, root_y, (root_y + widget->allocation.height));
6236
6237   *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
6238   *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
6239 }
6240
6241 typedef struct
6242 {
6243   GtkTextView *text_view;
6244   gint button;
6245   guint time;
6246 } PopupInfo;
6247
6248 static gboolean
6249 range_contains_editable_text (const GtkTextIter *start,
6250                               const GtkTextIter *end,
6251                               gboolean default_editability)
6252 {
6253   GtkTextIter iter = *start;
6254
6255   while (gtk_text_iter_compare (&iter, end) < 0)
6256     {
6257       if (gtk_text_iter_editable (&iter, default_editability))
6258         return TRUE;
6259       
6260       gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
6261     }
6262
6263   return FALSE;
6264 }                             
6265
6266 static void
6267 unichar_chosen_func (const char *text,
6268                      gpointer    data)
6269 {
6270   GtkTextView *text_view = GTK_TEXT_VIEW (data);
6271
6272   gtk_text_view_commit_text (text_view, text);
6273 }
6274
6275 static void
6276 popup_targets_received (GtkClipboard     *clipboard,
6277                         GtkSelectionData *data,
6278                         gpointer          user_data)
6279 {
6280   PopupInfo *info = user_data;
6281   GtkTextView *text_view = info->text_view;
6282   
6283   if (GTK_WIDGET_REALIZED (text_view))
6284     {
6285       /* We implicitely rely here on the fact that if we are pasting ourself, we'll
6286        * have text targets as well as the private GTK_TEXT_BUFFER_CONTENTS target.
6287        */
6288       gboolean clipboard_contains_text = gtk_selection_data_targets_include_text (data);
6289       GtkWidget *menuitem;
6290       GtkWidget *submenu;
6291       gboolean have_selection;
6292       gboolean can_insert;
6293       GtkTextIter iter;
6294       GtkTextIter sel_start, sel_end;
6295       
6296       if (text_view->popup_menu)
6297         gtk_widget_destroy (text_view->popup_menu);
6298
6299       text_view->popup_menu = gtk_menu_new ();
6300       
6301       gtk_menu_attach_to_widget (GTK_MENU (text_view->popup_menu),
6302                                  GTK_WIDGET (text_view),
6303                                  popup_menu_detach);
6304       
6305       have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
6306                                                              &sel_start, &sel_end);
6307       
6308       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
6309                                         &iter,
6310                                         gtk_text_buffer_get_insert (get_buffer (text_view)));
6311       
6312       can_insert = gtk_text_iter_can_insert (&iter, text_view->editable);
6313       
6314       append_action_signal (text_view, text_view->popup_menu, GTK_STOCK_CUT, "cut_clipboard",
6315                             have_selection &&
6316                             range_contains_editable_text (&sel_start, &sel_end,
6317                                                           text_view->editable));
6318       append_action_signal (text_view, text_view->popup_menu, GTK_STOCK_COPY, "copy_clipboard",
6319                             have_selection);
6320       append_action_signal (text_view, text_view->popup_menu, GTK_STOCK_PASTE, "paste_clipboard",
6321                             can_insert && clipboard_contains_text);
6322       
6323       menuitem = gtk_separator_menu_item_new ();
6324       gtk_widget_show (menuitem);
6325       gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
6326       
6327       menuitem = gtk_menu_item_new_with_mnemonic (_("Input _Methods"));
6328       gtk_widget_show (menuitem);
6329       submenu = gtk_menu_new ();
6330       gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
6331       gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
6332       
6333       gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (text_view->im_context),
6334                                             GTK_MENU_SHELL (submenu));
6335
6336       menuitem = gtk_menu_item_new_with_mnemonic (_("_Insert Unicode control character"));
6337       gtk_widget_show (menuitem);
6338       gtk_widget_set_sensitive (menuitem, can_insert);
6339       
6340       submenu = gtk_menu_new ();
6341       gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
6342       gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);      
6343
6344       _gtk_text_util_append_special_char_menuitems (GTK_MENU_SHELL (submenu),
6345                                                     unichar_chosen_func,
6346                                                     text_view);
6347       
6348       gtk_signal_emit (GTK_OBJECT (text_view),
6349                        signals[POPULATE_POPUP],
6350                        text_view->popup_menu);
6351       
6352       if (info->button)
6353         gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
6354                         NULL, NULL,
6355                         info->button, info->time);
6356       else
6357         {
6358           gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
6359                           popup_position_func, text_view,
6360                           0, gtk_get_current_event_time ());
6361           _gtk_menu_shell_select_first (GTK_MENU_SHELL (text_view->popup_menu));
6362         }
6363     }
6364
6365   g_object_unref (text_view);
6366   g_free (info);
6367 }
6368
6369 static void
6370 gtk_text_view_do_popup (GtkTextView    *text_view,
6371                         GdkEventButton *event)
6372 {
6373   PopupInfo *info = g_new (PopupInfo, 1);
6374
6375   /* In order to know what entries we should make sensitive, we
6376    * ask for the current targets of the clipboard, and when
6377    * we get them, then we actually pop up the menu.
6378    */
6379   info->text_view = g_object_ref (text_view);
6380   
6381   if (event)
6382     {
6383       info->button = event->button;
6384       info->time = event->time;
6385     }
6386   else
6387     {
6388       info->button = 0;
6389       info->time = gtk_get_current_event_time ();
6390     }
6391
6392   gtk_clipboard_request_contents (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
6393                                   gdk_atom_intern ("TARGETS", FALSE),
6394                                   popup_targets_received,
6395                                   info);
6396 }
6397
6398 static void
6399 gtk_text_view_popup_menu (GtkWidget *widget)
6400 {
6401   gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL);  
6402 }
6403
6404 /* Child GdkWindows */
6405
6406
6407 static GtkTextWindow*
6408 text_window_new (GtkTextWindowType  type,
6409                  GtkWidget         *widget,
6410                  gint               width_request,
6411                  gint               height_request)
6412 {
6413   GtkTextWindow *win;
6414
6415   win = g_new (GtkTextWindow, 1);
6416
6417   win->type = type;
6418   win->widget = widget;
6419   win->window = NULL;
6420   win->bin_window = NULL;
6421   win->requisition.width = width_request;
6422   win->requisition.height = height_request;
6423   win->allocation.width = width_request;
6424   win->allocation.height = height_request;
6425   win->allocation.x = 0;
6426   win->allocation.y = 0;
6427
6428   return win;
6429 }
6430
6431 static void
6432 text_window_free (GtkTextWindow *win)
6433 {
6434   if (win->window)
6435     text_window_unrealize (win);
6436
6437   g_free (win);
6438 }
6439
6440 static void
6441 text_window_realize (GtkTextWindow *win,
6442                      GdkWindow     *parent)
6443 {
6444   GdkWindowAttr attributes;
6445   gint attributes_mask;
6446   GdkCursor *cursor;
6447
6448   attributes.window_type = GDK_WINDOW_CHILD;
6449   attributes.x = win->allocation.x;
6450   attributes.y = win->allocation.y;
6451   attributes.width = win->allocation.width;
6452   attributes.height = win->allocation.height;
6453   attributes.wclass = GDK_INPUT_OUTPUT;
6454   attributes.visual = gtk_widget_get_visual (win->widget);
6455   attributes.colormap = gtk_widget_get_colormap (win->widget);
6456   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
6457
6458   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
6459
6460   win->window = gdk_window_new (parent,
6461                                 &attributes,
6462                                 attributes_mask);
6463
6464   gdk_window_show (win->window);
6465   gdk_window_set_user_data (win->window, win->widget);
6466
6467   attributes.x = 0;
6468   attributes.y = 0;
6469   attributes.width = win->allocation.width;
6470   attributes.height = win->allocation.height;
6471   attributes.event_mask = (GDK_EXPOSURE_MASK            |
6472                            GDK_SCROLL_MASK              |
6473                            GDK_KEY_PRESS_MASK           |
6474                            GDK_BUTTON_PRESS_MASK        |
6475                            GDK_BUTTON_RELEASE_MASK      |
6476                            GDK_POINTER_MOTION_MASK      |
6477                            GDK_POINTER_MOTION_HINT_MASK |
6478                            gtk_widget_get_events (win->widget));
6479
6480   win->bin_window = gdk_window_new (win->window,
6481                                     &attributes,
6482                                     attributes_mask);
6483
6484   gdk_window_show (win->bin_window);
6485   gdk_window_set_user_data (win->bin_window, win->widget);
6486
6487   if (win->type == GTK_TEXT_WINDOW_TEXT)
6488     {
6489       /* I-beam cursor */
6490       cursor = gdk_cursor_new (GDK_XTERM);
6491       gdk_window_set_cursor (win->bin_window, cursor);
6492       gdk_cursor_unref (cursor);
6493
6494       gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->im_context,
6495                                         win->window);
6496
6497
6498       gdk_window_set_background (win->bin_window,
6499                                  &win->widget->style->base[GTK_WIDGET_STATE (win->widget)]);
6500     }
6501   else
6502     {
6503       gdk_window_set_background (win->bin_window,
6504                                  &win->widget->style->bg[GTK_WIDGET_STATE (win->widget)]);
6505     }
6506
6507   g_object_set_qdata (G_OBJECT (win->window),
6508                       g_quark_from_static_string ("gtk-text-view-text-window"),
6509                       win);
6510
6511   g_object_set_qdata (G_OBJECT (win->bin_window),
6512                       g_quark_from_static_string ("gtk-text-view-text-window"),
6513                       win);
6514 }
6515
6516 static void
6517 text_window_unrealize (GtkTextWindow *win)
6518 {
6519   if (win->type == GTK_TEXT_WINDOW_TEXT)
6520     {
6521       gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->im_context,
6522                                         NULL);
6523     }
6524
6525   gdk_window_set_user_data (win->window, NULL);
6526   gdk_window_set_user_data (win->bin_window, NULL);
6527   gdk_window_destroy (win->bin_window);
6528   gdk_window_destroy (win->window);
6529   win->window = NULL;
6530   win->bin_window = NULL;
6531 }
6532
6533 static void
6534 text_window_size_allocate (GtkTextWindow *win,
6535                            GdkRectangle  *rect)
6536 {
6537   win->allocation = *rect;
6538
6539   if (win->window)
6540     {
6541       gdk_window_move_resize (win->window,
6542                               rect->x, rect->y,
6543                               rect->width, rect->height);
6544
6545       gdk_window_resize (win->bin_window,
6546                          rect->width, rect->height);
6547     }
6548 }
6549
6550 static void
6551 text_window_scroll        (GtkTextWindow *win,
6552                            gint           dx,
6553                            gint           dy)
6554 {
6555   if (dx != 0 || dy != 0)
6556     {
6557       gdk_window_scroll (win->bin_window, dx, dy);
6558     }
6559 }
6560
6561 static void
6562 text_window_invalidate_rect (GtkTextWindow *win,
6563                              GdkRectangle  *rect)
6564 {
6565   GdkRectangle window_rect;
6566
6567   gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (win->widget),
6568                                          win->type,
6569                                          rect->x,
6570                                          rect->y,
6571                                          &window_rect.x,
6572                                          &window_rect.y);
6573
6574   window_rect.width = rect->width;
6575   window_rect.height = rect->height;
6576   
6577   /* Adjust the rect as appropriate */
6578   
6579   switch (win->type)
6580     {
6581     case GTK_TEXT_WINDOW_TEXT:
6582       break;
6583
6584     case GTK_TEXT_WINDOW_LEFT:
6585     case GTK_TEXT_WINDOW_RIGHT:
6586       window_rect.x = 0;
6587       window_rect.width = win->allocation.width;
6588       break;
6589
6590     case GTK_TEXT_WINDOW_TOP:
6591     case GTK_TEXT_WINDOW_BOTTOM:
6592       window_rect.y = 0;
6593       window_rect.height = win->allocation.height;
6594       break;
6595
6596     default:
6597       g_warning ("%s: bug!", G_STRLOC);
6598       return;
6599       break;
6600     }
6601           
6602   gdk_window_invalidate_rect (win->bin_window, &window_rect, FALSE);
6603
6604 #if 0
6605   {
6606     GdkColor color = { 0, 65535, 0, 0 };
6607     GdkGC *gc = gdk_gc_new (win->bin_window);
6608     gdk_gc_set_rgb_fg_color (gc, &color);
6609     gdk_draw_rectangle (win->bin_window,
6610                         gc, TRUE, window_rect.x, window_rect.y,
6611                         window_rect.width, window_rect.height);
6612     gdk_gc_unref (gc);
6613   }
6614 #endif
6615 }
6616
6617 static gint
6618 text_window_get_width (GtkTextWindow *win)
6619 {
6620   return win->allocation.width;
6621 }
6622
6623 static gint
6624 text_window_get_height (GtkTextWindow *win)
6625 {
6626   return win->allocation.height;
6627 }
6628
6629 static void
6630 text_window_get_allocation (GtkTextWindow *win,
6631                             GdkRectangle  *rect)
6632 {
6633   *rect = win->allocation;
6634 }
6635
6636 /* Windows */
6637
6638
6639 /**
6640  * gtk_text_view_get_window:
6641  * @text_view: a #GtkTextView
6642  * @win: window to get
6643  *
6644  * Retrieves the #GdkWindow corresponding to an area of the text view;
6645  * possible windows include the overall widget window, child windows
6646  * on the left, right, top, bottom, and the window that displays the
6647  * text buffer. Windows are %NULL and nonexistent if their width or
6648  * height is 0, and are nonexistent before the widget has been
6649  * realized.
6650  *
6651  * Return value: a #GdkWindow, or %NULL
6652  **/
6653 GdkWindow*
6654 gtk_text_view_get_window (GtkTextView *text_view,
6655                           GtkTextWindowType win)
6656 {
6657   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
6658
6659   switch (win)
6660     {
6661     case GTK_TEXT_WINDOW_WIDGET:
6662       return GTK_WIDGET (text_view)->window;
6663       break;
6664
6665     case GTK_TEXT_WINDOW_TEXT:
6666       return text_view->text_window->bin_window;
6667       break;
6668
6669     case GTK_TEXT_WINDOW_LEFT:
6670       if (text_view->left_window)
6671         return text_view->left_window->bin_window;
6672       else
6673         return NULL;
6674       break;
6675
6676     case GTK_TEXT_WINDOW_RIGHT:
6677       if (text_view->right_window)
6678         return text_view->right_window->bin_window;
6679       else
6680         return NULL;
6681       break;
6682
6683     case GTK_TEXT_WINDOW_TOP:
6684       if (text_view->top_window)
6685         return text_view->top_window->bin_window;
6686       else
6687         return NULL;
6688       break;
6689
6690     case GTK_TEXT_WINDOW_BOTTOM:
6691       if (text_view->bottom_window)
6692         return text_view->bottom_window->bin_window;
6693       else
6694         return NULL;
6695       break;
6696
6697     case GTK_TEXT_WINDOW_PRIVATE:
6698       g_warning ("%s: You can't get GTK_TEXT_WINDOW_PRIVATE, it has \"PRIVATE\" in the name because it is private.", __FUNCTION__);
6699       return NULL;
6700       break;
6701     }
6702
6703   g_warning ("%s: Unknown GtkTextWindowType", __FUNCTION__);
6704   return NULL;
6705 }
6706
6707 /**
6708  * gtk_text_view_get_window_type:
6709  * @text_view: a #GtkTextView
6710  * @window: a window type
6711  *
6712  * Usually used to find out which window an event corresponds to.
6713  * If you connect to an event signal on @text_view, this function
6714  * should be called on <literal>event-&gt;window</literal> to
6715  * see which window it was.
6716  *
6717  * Return value: the window type.
6718  **/
6719 GtkTextWindowType
6720 gtk_text_view_get_window_type (GtkTextView *text_view,
6721                                GdkWindow   *window)
6722 {
6723   GtkTextWindow *win;
6724
6725   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
6726   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
6727
6728   if (window == GTK_WIDGET (text_view)->window)
6729     return GTK_TEXT_WINDOW_WIDGET;
6730
6731   win = g_object_get_qdata (G_OBJECT (window),
6732                             g_quark_try_string ("gtk-text-view-text-window"));
6733
6734   if (win)
6735     return win->type;
6736   else
6737     {
6738       return GTK_TEXT_WINDOW_PRIVATE;
6739     }
6740 }
6741
6742 static void
6743 buffer_to_widget (GtkTextView      *text_view,
6744                   gint              buffer_x,
6745                   gint              buffer_y,
6746                   gint             *window_x,
6747                   gint             *window_y)
6748 {
6749   gint focus_edge_width;
6750   gboolean interior_focus;
6751   gint focus_width;
6752   
6753   gtk_widget_style_get (GTK_WIDGET (text_view),
6754                         "interior_focus", &interior_focus,
6755                         "focus_line_width", &focus_width,
6756                         NULL);
6757
6758   if (interior_focus)
6759     focus_edge_width = 0;
6760   else
6761     focus_edge_width = focus_width;
6762   
6763   if (window_x)
6764     {
6765       *window_x = buffer_x - text_view->xoffset + focus_edge_width;
6766       if (text_view->left_window)
6767         *window_x += text_view->left_window->allocation.width;
6768     }
6769
6770   if (window_y)
6771     {
6772       *window_y = buffer_y - text_view->yoffset + focus_edge_width;
6773       if (text_view->top_window)
6774         *window_y += text_view->top_window->allocation.height;
6775     }
6776 }
6777
6778 static void
6779 widget_to_text_window (GtkTextWindow *win,
6780                        gint           widget_x,
6781                        gint           widget_y,
6782                        gint          *window_x,
6783                        gint          *window_y)
6784 {
6785   if (window_x)
6786     *window_x = widget_x - win->allocation.x;
6787
6788   if (window_y)
6789     *window_y = widget_y - win->allocation.y;
6790 }
6791
6792 static void
6793 buffer_to_text_window (GtkTextView   *text_view,
6794                        GtkTextWindow *win,
6795                        gint           buffer_x,
6796                        gint           buffer_y,
6797                        gint          *window_x,
6798                        gint          *window_y)
6799 {
6800   if (win == NULL)
6801     {
6802       g_warning ("Attempt to convert text buffer coordinates to coordinates "
6803                  "for a nonexistent or private child window of GtkTextView");
6804       return;
6805     }
6806
6807   buffer_to_widget (text_view,
6808                     buffer_x, buffer_y,
6809                     window_x, window_y);
6810
6811   widget_to_text_window (win,
6812                          window_x ? *window_x : 0,
6813                          window_y ? *window_y : 0,
6814                          window_x,
6815                          window_y);
6816 }
6817
6818 /**
6819  * gtk_text_view_buffer_to_window_coords:
6820  * @text_view: a #GtkTextView
6821  * @win: a #GtkTextWindowType
6822  * @buffer_x: buffer x coordinate
6823  * @buffer_y: buffer y coordinate
6824  * @window_x: window x coordinate return location
6825  * @window_y: window y coordinate return location
6826  *
6827  * Converts coordinate (@buffer_x, @buffer_y) to coordinates for the window
6828  * @win, and stores the result in (@window_x, @window_y).
6829  **/
6830 void
6831 gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
6832                                        GtkTextWindowType win,
6833                                        gint              buffer_x,
6834                                        gint              buffer_y,
6835                                        gint             *window_x,
6836                                        gint             *window_y)
6837 {
6838   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
6839
6840   switch (win)
6841     {
6842     case GTK_TEXT_WINDOW_WIDGET:
6843       buffer_to_widget (text_view,
6844                         buffer_x, buffer_y,
6845                         window_x, window_y);
6846       break;
6847
6848     case GTK_TEXT_WINDOW_TEXT:
6849       if (window_x)
6850         *window_x = buffer_x - text_view->xoffset;
6851       if (window_y)
6852         *window_y = buffer_y - text_view->yoffset;
6853       break;
6854
6855     case GTK_TEXT_WINDOW_LEFT:
6856       buffer_to_text_window (text_view,
6857                              text_view->left_window,
6858                              buffer_x, buffer_y,
6859                              window_x, window_y);
6860       break;
6861
6862     case GTK_TEXT_WINDOW_RIGHT:
6863       buffer_to_text_window (text_view,
6864                              text_view->right_window,
6865                              buffer_x, buffer_y,
6866                              window_x, window_y);
6867       break;
6868
6869     case GTK_TEXT_WINDOW_TOP:
6870       buffer_to_text_window (text_view,
6871                              text_view->top_window,
6872                              buffer_x, buffer_y,
6873                              window_x, window_y);
6874       break;
6875
6876     case GTK_TEXT_WINDOW_BOTTOM:
6877       buffer_to_text_window (text_view,
6878                              text_view->bottom_window,
6879                              buffer_x, buffer_y,
6880                              window_x, window_y);
6881       break;
6882
6883     case GTK_TEXT_WINDOW_PRIVATE:
6884       g_warning ("%s: can't get coords for private windows", G_STRLOC);
6885       break;
6886
6887     default:
6888       g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC);
6889       break;
6890     }
6891 }
6892
6893 static void
6894 widget_to_buffer (GtkTextView *text_view,
6895                   gint         widget_x,
6896                   gint         widget_y,
6897                   gint        *buffer_x,
6898                   gint        *buffer_y)
6899 {
6900   gint focus_edge_width;
6901   gboolean interior_focus;
6902   gint focus_width;
6903   
6904   gtk_widget_style_get (GTK_WIDGET (text_view),
6905                         "interior_focus", &interior_focus,
6906                         "focus_line_width", &focus_width,
6907                         NULL);
6908
6909   if (interior_focus)
6910     focus_edge_width = 0;
6911   else
6912     focus_edge_width = focus_width;
6913   
6914   if (buffer_x)
6915     {
6916       *buffer_x = widget_x - focus_edge_width + text_view->xoffset;
6917       if (text_view->left_window)
6918         *buffer_x -= text_view->left_window->allocation.width;
6919     }
6920
6921   if (buffer_y)
6922     {
6923       *buffer_y = widget_y - focus_edge_width + text_view->yoffset;
6924       if (text_view->top_window)
6925         *buffer_y -= text_view->top_window->allocation.height;
6926     }
6927 }
6928
6929 static void
6930 text_window_to_widget (GtkTextWindow *win,
6931                        gint           window_x,
6932                        gint           window_y,
6933                        gint          *widget_x,
6934                        gint          *widget_y)
6935 {
6936   if (widget_x)
6937     *widget_x = window_x + win->allocation.x;
6938
6939   if (widget_y)
6940     *widget_y = window_y + win->allocation.y;
6941 }
6942
6943 static void
6944 text_window_to_buffer (GtkTextView   *text_view,
6945                        GtkTextWindow *win,
6946                        gint           window_x,
6947                        gint           window_y,
6948                        gint          *buffer_x,
6949                        gint          *buffer_y)
6950 {
6951   if (win == NULL)
6952     {
6953       g_warning ("Attempt to convert GtkTextView buffer coordinates into "
6954                  "coordinates for a nonexistent child window.");
6955       return;
6956     }
6957
6958   text_window_to_widget (win,
6959                          window_x,
6960                          window_y,
6961                          buffer_x,
6962                          buffer_y);
6963
6964   widget_to_buffer (text_view,
6965                     buffer_x ? *buffer_x : 0,
6966                     buffer_y ? *buffer_y : 0,
6967                     buffer_x,
6968                     buffer_y);
6969 }
6970
6971 /**
6972  * gtk_text_view_window_to_buffer_coords:
6973  * @text_view: a #GtkTextView
6974  * @win: a #GtkTextWindowType
6975  * @window_x: window x coordinate
6976  * @window_y: window y coordinate
6977  * @buffer_x: buffer x coordinate return location
6978  * @buffer_y: buffer y coordinate return location
6979  *
6980  * Converts coordinates on the window identified by @win to buffer
6981  * coordinates, storing the result in (@buffer_x,@buffer_y).
6982  **/
6983 void
6984 gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
6985                                        GtkTextWindowType win,
6986                                        gint              window_x,
6987                                        gint              window_y,
6988                                        gint             *buffer_x,
6989                                        gint             *buffer_y)
6990 {
6991   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
6992
6993   switch (win)
6994     {
6995     case GTK_TEXT_WINDOW_WIDGET:
6996       widget_to_buffer (text_view,
6997                         window_x, window_y,
6998                         buffer_x, buffer_y);
6999       break;
7000
7001     case GTK_TEXT_WINDOW_TEXT:
7002       if (buffer_x)
7003         *buffer_x = window_x + text_view->xoffset;
7004       if (buffer_y)
7005         *buffer_y = window_y + text_view->yoffset;
7006       break;
7007
7008     case GTK_TEXT_WINDOW_LEFT:
7009       text_window_to_buffer (text_view,
7010                              text_view->left_window,
7011                              window_x, window_y,
7012                              buffer_x, buffer_y);
7013       break;
7014
7015     case GTK_TEXT_WINDOW_RIGHT:
7016       text_window_to_buffer (text_view,
7017                              text_view->right_window,
7018                              window_x, window_y,
7019                              buffer_x, buffer_y);
7020       break;
7021
7022     case GTK_TEXT_WINDOW_TOP:
7023       text_window_to_buffer (text_view,
7024                              text_view->top_window,
7025                              window_x, window_y,
7026                              buffer_x, buffer_y);
7027       break;
7028
7029     case GTK_TEXT_WINDOW_BOTTOM:
7030       text_window_to_buffer (text_view,
7031                              text_view->bottom_window,
7032                              window_x, window_y,
7033                              buffer_x, buffer_y);
7034       break;
7035
7036     case GTK_TEXT_WINDOW_PRIVATE:
7037       g_warning ("%s: can't get coords for private windows", G_STRLOC);
7038       break;
7039
7040     default:
7041       g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC);
7042       break;
7043     }
7044 }
7045
7046 static void
7047 set_window_width (GtkTextView      *text_view,
7048                   gint              width,
7049                   GtkTextWindowType type,
7050                   GtkTextWindow   **winp)
7051 {
7052   if (width == 0)
7053     {
7054       if (*winp)
7055         {
7056           text_window_free (*winp);
7057           *winp = NULL;
7058           gtk_widget_queue_resize (GTK_WIDGET (text_view));
7059         }
7060     }
7061   else
7062     {
7063       if (*winp == NULL)
7064         {
7065           *winp = text_window_new (type,
7066                                    GTK_WIDGET (text_view),
7067                                    width, 0);
7068           /* if the widget is already realized we need to realize the child manually */
7069           if (GTK_WIDGET_REALIZED (text_view))
7070             text_window_realize (*winp, GTK_WIDGET (text_view)->window);
7071         }
7072       else
7073         {
7074           if ((*winp)->requisition.width == width)
7075             return;
7076
7077           (*winp)->requisition.width = width;
7078         }
7079
7080       gtk_widget_queue_resize (GTK_WIDGET (text_view));
7081     }
7082 }
7083
7084
7085 static void
7086 set_window_height (GtkTextView      *text_view,
7087                    gint              height,
7088                    GtkTextWindowType type,
7089                    GtkTextWindow   **winp)
7090 {
7091   if (height == 0)
7092     {
7093       if (*winp)
7094         {
7095           text_window_free (*winp);
7096           *winp = NULL;
7097           gtk_widget_queue_resize (GTK_WIDGET (text_view));
7098         }
7099     }
7100   else
7101     {
7102       if (*winp == NULL)
7103         {
7104           *winp = text_window_new (type,
7105                                    GTK_WIDGET (text_view),
7106                                    0, height);
7107
7108           /* if the widget is already realized we need to realize the child manually */
7109           if (GTK_WIDGET_REALIZED (text_view))
7110             text_window_realize (*winp, GTK_WIDGET (text_view)->window);
7111         }
7112       else
7113         {
7114           if ((*winp)->requisition.height == height)
7115             return;
7116
7117           (*winp)->requisition.height = height;
7118         }
7119
7120       gtk_widget_queue_resize (GTK_WIDGET (text_view));
7121     }
7122 }
7123
7124 /**
7125  * gtk_text_view_set_border_window_size:
7126  * @text_view: a #GtkTextView
7127  * @type: window to affect
7128  * @size: width or height of the window
7129  *
7130  * Sets the width of %GTK_TEXT_WINDOW_LEFT or %GTK_TEXT_WINDOW_RIGHT,
7131  * or the height of %GTK_TEXT_WINDOW_TOP or %GTK_TEXT_WINDOW_BOTTOM.
7132  * Automatically destroys the corresponding window if the size is set
7133  * to 0, and creates the window if the size is set to non-zero.  This
7134  * function can only be used for the "border windows," it doesn't work
7135  * with #GTK_TEXT_WINDOW_WIDGET, #GTK_TEXT_WINDOW_TEXT, or
7136  * #GTK_TEXT_WINDOW_PRIVATE.
7137  **/
7138 void
7139 gtk_text_view_set_border_window_size (GtkTextView      *text_view,
7140                                       GtkTextWindowType type,
7141                                       gint              size)
7142
7143 {
7144   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7145   g_return_if_fail (size >= 0);
7146
7147   switch (type)
7148     {
7149     case GTK_TEXT_WINDOW_LEFT:
7150       set_window_width (text_view, size, GTK_TEXT_WINDOW_LEFT,
7151                         &text_view->left_window);
7152       break;
7153
7154     case GTK_TEXT_WINDOW_RIGHT:
7155       set_window_width (text_view, size, GTK_TEXT_WINDOW_RIGHT,
7156                         &text_view->right_window);
7157       break;
7158
7159     case GTK_TEXT_WINDOW_TOP:
7160       set_window_height (text_view, size, GTK_TEXT_WINDOW_TOP,
7161                          &text_view->top_window);
7162       break;
7163
7164     case GTK_TEXT_WINDOW_BOTTOM:
7165       set_window_height (text_view, size, GTK_TEXT_WINDOW_BOTTOM,
7166                          &text_view->bottom_window);
7167       break;
7168
7169     default:
7170       g_warning ("Can only set size of left/right/top/bottom border windows with gtk_text_view_set_border_window_size()");
7171       break;
7172     }
7173 }
7174
7175 /**
7176  * gtk_text_view_get_border_window_size:
7177  * @text_view: a #GtkTextView
7178  * @type: window to return size from
7179  *
7180  * Gets the width of the specified border window. See
7181  * gtk_text_view_set_border_window_size().
7182  *
7183  * Return value: width of window
7184  **/
7185 gint
7186 gtk_text_view_get_border_window_size (GtkTextView       *text_view,
7187                                       GtkTextWindowType  type)
7188 {
7189   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
7190   
7191   switch (type)
7192     {
7193     case GTK_TEXT_WINDOW_LEFT:
7194       if (text_view->left_window)
7195         return text_view->left_window->requisition.width;
7196       
7197     case GTK_TEXT_WINDOW_RIGHT:
7198       if (text_view->right_window)
7199         return text_view->right_window->requisition.width;
7200       
7201     case GTK_TEXT_WINDOW_TOP:
7202       if (text_view->top_window)
7203         return text_view->top_window->requisition.height;
7204
7205     case GTK_TEXT_WINDOW_BOTTOM:
7206       if (text_view->bottom_window)
7207         return text_view->bottom_window->requisition.height;
7208       
7209     default:
7210       g_warning ("Can only get size of left/right/top/bottom border windows with gtk_text_view_get_border_window_size()");
7211       break;
7212     }
7213
7214   return 0;
7215 }
7216
7217 /*
7218  * Child widgets
7219  */
7220
7221 static GtkTextViewChild*
7222 text_view_child_new_anchored (GtkWidget          *child,
7223                               GtkTextChildAnchor *anchor,
7224                               GtkTextLayout      *layout)
7225 {
7226   GtkTextViewChild *vc;
7227
7228   vc = g_new (GtkTextViewChild, 1);
7229
7230   vc->widget = child;
7231   vc->anchor = anchor;
7232
7233   vc->from_top_of_line = 0;
7234   vc->from_left_of_buffer = 0;
7235   
7236   g_object_ref (G_OBJECT (vc->widget));
7237   g_object_ref (G_OBJECT (vc->anchor));
7238
7239   g_object_set_data (G_OBJECT (child),
7240                      "gtk-text-view-child",
7241                      vc);
7242
7243   gtk_text_child_anchor_register_child (anchor, child, layout);
7244   
7245   return vc;
7246 }
7247
7248 static GtkTextViewChild*
7249 text_view_child_new_window (GtkWidget          *child,
7250                             GtkTextWindowType   type,
7251                             gint                x,
7252                             gint                y)
7253 {
7254   GtkTextViewChild *vc;
7255
7256   vc = g_new (GtkTextViewChild, 1);
7257
7258   vc->widget = child;
7259   vc->anchor = NULL;
7260
7261   vc->from_top_of_line = 0;
7262   vc->from_left_of_buffer = 0;
7263   
7264   g_object_ref (G_OBJECT (vc->widget));
7265
7266   vc->type = type;
7267   vc->x = x;
7268   vc->y = y;
7269
7270   g_object_set_data (G_OBJECT (child),
7271                      "gtk-text-view-child",
7272                      vc);
7273   
7274   return vc;
7275 }
7276
7277 static void
7278 text_view_child_free (GtkTextViewChild *child)
7279 {
7280   g_object_set_data (G_OBJECT (child->widget),
7281                      "gtk-text-view-child", NULL);
7282
7283   if (child->anchor)
7284     {
7285       gtk_text_child_anchor_unregister_child (child->anchor,
7286                                               child->widget);
7287       g_object_unref (G_OBJECT (child->anchor));
7288     }
7289
7290   g_object_unref (G_OBJECT (child->widget));
7291
7292   g_free (child);
7293 }
7294
7295 static void
7296 text_view_child_set_parent_window (GtkTextView      *text_view,
7297                                    GtkTextViewChild *vc)
7298 {
7299   if (vc->anchor)
7300     gtk_widget_set_parent_window (vc->widget,
7301                                   text_view->text_window->bin_window);
7302   else
7303     {
7304       GdkWindow *window;
7305       window = gtk_text_view_get_window (text_view,
7306                                          vc->type);
7307       gtk_widget_set_parent_window (vc->widget, window);
7308     }
7309 }
7310
7311 static void
7312 add_child (GtkTextView      *text_view,
7313            GtkTextViewChild *vc)
7314 {
7315   text_view->children = g_slist_prepend (text_view->children,
7316                                          vc);
7317
7318   if (GTK_WIDGET_REALIZED (text_view))
7319     text_view_child_set_parent_window (text_view, vc);
7320   
7321   gtk_widget_set_parent (vc->widget, GTK_WIDGET (text_view));
7322 }
7323
7324 /**
7325  * gtk_text_view_add_child_at_anchor:
7326  * @text_view: a #GtkTextView
7327  * @child: a #GtkWidget
7328  * @anchor: a #GtkTextChildAnchor in the #GtkTextBuffer for @text_view
7329  * 
7330  * Adds a child widget in the text buffer, at the given @anchor.
7331  * 
7332  **/
7333 void
7334 gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
7335                                    GtkWidget            *child,
7336                                    GtkTextChildAnchor   *anchor)
7337 {
7338   GtkTextViewChild *vc;
7339
7340   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7341   g_return_if_fail (GTK_IS_WIDGET (child));
7342   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
7343   g_return_if_fail (child->parent == NULL);
7344
7345   gtk_text_view_ensure_layout (text_view);
7346
7347   vc = text_view_child_new_anchored (child, anchor,
7348                                      text_view->layout);
7349
7350   add_child (text_view, vc);
7351
7352   g_assert (vc->widget == child);
7353   g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
7354 }
7355
7356 /**
7357  * gtk_text_view_add_child_in_window:
7358  * @text_view: a #GtkTextView
7359  * @child: a #GtkWidget
7360  * @which_window: which window the child should appear in
7361  * @xpos: X position of child in window coordinates
7362  * @ypos: Y position of child in window coordinates
7363  *
7364  * Adds a child at fixed coordinates in one of the text widget's
7365  * windows.  The window must have nonzero size (see
7366  * gtk_text_view_set_border_window_size()).  Note that the child
7367  * coordinates are given relative to the #GdkWindow in question, and
7368  * that these coordinates have no sane relationship to scrolling. When
7369  * placing a child in #GTK_TEXT_WINDOW_WIDGET, scrolling is
7370  * irrelevant, the child floats above all scrollable areas. But when
7371  * placing a child in one of the scrollable windows (border windows or
7372  * text window), you'll need to compute the child's correct position
7373  * in buffer coordinates any time scrolling occurs or buffer changes
7374  * occur, and then call gtk_text_view_move_child() to update the
7375  * child's position. Unfortunately there's no good way to detect that
7376  * scrolling has occurred, using the current API; a possible hack
7377  * would be to update all child positions when the scroll adjustments
7378  * change or the text buffer changes. See bug 64518 on
7379  * bugzilla.gnome.org for status of fixing this issue.
7380  *
7381  **/
7382 void
7383 gtk_text_view_add_child_in_window (GtkTextView          *text_view,
7384                                    GtkWidget            *child,
7385                                    GtkTextWindowType     which_window,
7386                                    gint                  xpos,
7387                                    gint                  ypos)
7388 {
7389   GtkTextViewChild *vc;
7390
7391   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7392   g_return_if_fail (GTK_IS_WIDGET (child));
7393   g_return_if_fail (child->parent == NULL);
7394
7395   vc = text_view_child_new_window (child, which_window,
7396                                    xpos, ypos);
7397
7398   add_child (text_view, vc);
7399
7400   g_assert (vc->widget == child);
7401   g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
7402 }
7403
7404 /**
7405  * gtk_text_view_move_child:
7406  * @text_view: a #GtkTextView
7407  * @child: child widget already added to the text view
7408  * @xpos: new X position in window coordinates
7409  * @ypos: new Y position in window coordinates
7410  *
7411  * Updates the position of a child, as for gtk_text_view_add_child_in_window().
7412  * 
7413  **/
7414 void
7415 gtk_text_view_move_child          (GtkTextView          *text_view,
7416                                    GtkWidget            *child,
7417                                    gint                  xpos,
7418                                    gint                  ypos)
7419 {
7420   GtkTextViewChild *vc;
7421
7422   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7423   g_return_if_fail (GTK_IS_WIDGET (child));
7424   g_return_if_fail (child->parent == (GtkWidget*) text_view);
7425
7426   vc = g_object_get_data (G_OBJECT (child),
7427                           "gtk-text-view-child");
7428
7429   g_assert (vc != NULL);
7430
7431   if (vc->x == xpos &&
7432       vc->y == ypos)
7433     return;
7434   
7435   vc->x = xpos;
7436   vc->y = ypos;
7437
7438   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (text_view))
7439     gtk_widget_queue_resize (child);
7440 }
7441
7442
7443 /* Iterator operations */
7444
7445 /**
7446  * gtk_text_view_forward_display_line:
7447  * @text_view: a #GtkTextView
7448  * @iter: a #GtkTextIter
7449  * 
7450  * Moves the given @iter forward by one display (wrapped) line.  A
7451  * display line is different from a paragraph. Paragraphs are
7452  * separated by newlines or other paragraph separator characters.
7453  * Display lines are created by line-wrapping a paragraph.  If
7454  * wrapping is turned off, display lines and paragraphs will be the
7455  * same. Display lines are divided differently for each view, since
7456  * they depend on the view's width; paragraphs are the same in all
7457  * views, since they depend on the contents of the #GtkTextBuffer.
7458  * 
7459  * Return value: %TRUE if @iter was moved and is not on the end iterator
7460  **/
7461 gboolean
7462 gtk_text_view_forward_display_line (GtkTextView *text_view,
7463                                     GtkTextIter *iter)
7464 {
7465   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7466   g_return_val_if_fail (iter != NULL, FALSE);
7467
7468   gtk_text_view_ensure_layout (text_view);
7469
7470   return gtk_text_layout_move_iter_to_next_line (text_view->layout, iter);
7471 }
7472
7473 /**
7474  * gtk_text_view_backward_display_line:
7475  * @text_view: a #GtkTextView
7476  * @iter: a #GtkTextIter
7477  * 
7478  * Moves the given @iter backward by one display (wrapped) line.  A
7479  * display line is different from a paragraph. Paragraphs are
7480  * separated by newlines or other paragraph separator characters.
7481  * Display lines are created by line-wrapping a paragraph.  If
7482  * wrapping is turned off, display lines and paragraphs will be the
7483  * same. Display lines are divided differently for each view, since
7484  * they depend on the view's width; paragraphs are the same in all
7485  * views, since they depend on the contents of the #GtkTextBuffer.
7486  * 
7487  * Return value: %TRUE if @iter was moved and is not on the end iterator
7488  **/
7489 gboolean
7490 gtk_text_view_backward_display_line (GtkTextView *text_view,
7491                                      GtkTextIter *iter)
7492 {
7493   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7494   g_return_val_if_fail (iter != NULL, FALSE);
7495
7496   gtk_text_view_ensure_layout (text_view);
7497
7498   return gtk_text_layout_move_iter_to_previous_line (text_view->layout, iter);
7499 }
7500
7501 /**
7502  * gtk_text_view_forward_display_line_end:
7503  * @text_view: a #GtkTextView
7504  * @iter: a #GtkTextIter
7505  * 
7506  * Moves the given @iter forward to the next display line end.  A
7507  * display line is different from a paragraph. Paragraphs are
7508  * separated by newlines or other paragraph separator characters.
7509  * Display lines are created by line-wrapping a paragraph.  If
7510  * wrapping is turned off, display lines and paragraphs will be the
7511  * same. Display lines are divided differently for each view, since
7512  * they depend on the view's width; paragraphs are the same in all
7513  * views, since they depend on the contents of the #GtkTextBuffer.
7514  * 
7515  * Return value: %TRUE if @iter was moved and is not on the end iterator
7516  **/
7517 gboolean
7518 gtk_text_view_forward_display_line_end (GtkTextView *text_view,
7519                                         GtkTextIter *iter)
7520 {
7521   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7522   g_return_val_if_fail (iter != NULL, FALSE);
7523
7524   gtk_text_view_ensure_layout (text_view);
7525
7526   return gtk_text_layout_move_iter_to_line_end (text_view->layout, iter, 1);
7527 }
7528
7529 /**
7530  * gtk_text_view_backward_display_line_start:
7531  * @text_view: a #GtkTextView
7532  * @iter: a #GtkTextIter
7533  * 
7534  * Moves the given @iter backward to the next display line start.  A
7535  * display line is different from a paragraph. Paragraphs are
7536  * separated by newlines or other paragraph separator characters.
7537  * Display lines are created by line-wrapping a paragraph.  If
7538  * wrapping is turned off, display lines and paragraphs will be the
7539  * same. Display lines are divided differently for each view, since
7540  * they depend on the view's width; paragraphs are the same in all
7541  * views, since they depend on the contents of the #GtkTextBuffer.
7542  * 
7543  * Return value: %TRUE if @iter was moved and is not on the end iterator
7544  **/
7545 gboolean
7546 gtk_text_view_backward_display_line_start (GtkTextView *text_view,
7547                                            GtkTextIter *iter)
7548 {
7549   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7550   g_return_val_if_fail (iter != NULL, FALSE);
7551
7552   gtk_text_view_ensure_layout (text_view);
7553
7554   return gtk_text_layout_move_iter_to_line_end (text_view->layout, iter, -1);
7555 }
7556
7557 /**
7558  * gtk_text_view_starts_display_line:
7559  * @text_view: a #GtkTextView
7560  * @iter: a #GtkTextIter
7561  * 
7562  * Determines whether @iter is at the start of a display line.
7563  * See gtk_text_view_forward_display_line() for an explanation of
7564  * display lines vs. paragraphs.
7565  * 
7566  * Return value: %TRUE if @iter begins a wrapped line
7567  **/
7568 gboolean
7569 gtk_text_view_starts_display_line (GtkTextView       *text_view,
7570                                    const GtkTextIter *iter)
7571 {
7572   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7573   g_return_val_if_fail (iter != NULL, FALSE);
7574
7575   gtk_text_view_ensure_layout (text_view);
7576
7577   return gtk_text_layout_iter_starts_line (text_view->layout, iter);
7578 }
7579
7580 /**
7581  * gtk_text_view_move_visually:
7582  * @text_view: a #GtkTextView
7583  * @iter: a #GtkTextIter
7584  * @count: number of lines to move
7585  * 
7586  * Moves @iter up or down by @count display (wrapped) lines.
7587  * See gtk_text_view_forward_display_line() for an explanation of
7588  * display lines vs. paragraphs.
7589  * 
7590  * Return value: %TRUE if @iter moved and is not on the end iterator
7591  **/
7592 gboolean
7593 gtk_text_view_move_visually (GtkTextView *text_view,
7594                              GtkTextIter *iter,
7595                              gint         count)
7596 {
7597   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7598   g_return_val_if_fail (iter != NULL, FALSE);
7599
7600   gtk_text_view_ensure_layout (text_view);
7601
7602   return gtk_text_layout_move_iter_visually (text_view->layout, iter, count);
7603 }