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