]> Pileus Git - ~andy/gtk/blob - gtk/gtktextview.c
And fix the marshaller for GtkTextView::delete.
[~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 "gtkmain.h"
32 #include "gtksignal.h"
33 #include "gtktext.h"
34 #include "gtktextdisplay.h"
35 #include "gtktextview.h"
36 #include "gtkimmulticontext.h"
37 #include "gdk/gdkkeysyms.h"
38 #include "gtktexttypes.h"
39 #include <string.h>
40
41 #define FOCUS_EDGE_WIDTH 1
42 #define DRAG_THRESHOLD 8
43
44 #define SCREEN_WIDTH(widget) text_window_get_width (GTK_TEXT_VIEW (widget)->text_window)
45 #define SCREEN_HEIGHT(widget) text_window_get_height (GTK_TEXT_VIEW (widget)->text_window)
46
47 enum {
48   MOVE,
49   SET_ANCHOR,
50   INSERT,
51   DELETE,
52   CUT_CLIPBOARD,
53   COPY_CLIPBOARD,
54   PASTE_CLIPBOARD,
55   TOGGLE_OVERWRITE,
56   SET_SCROLL_ADJUSTMENTS,
57   LAST_SIGNAL
58 };
59
60 enum {
61   ARG_0,
62   ARG_HEIGHT_LINES,
63   ARG_WIDTH_COLUMNS,
64   ARG_PIXELS_ABOVE_LINES,
65   ARG_PIXELS_BELOW_LINES,
66   ARG_PIXELS_INSIDE_WRAP,
67   ARG_EDITABLE,
68   ARG_WRAP_MODE,
69   LAST_ARG
70 };
71
72 static void gtk_text_view_init                 (GtkTextView      *text_view);
73 static void gtk_text_view_class_init           (GtkTextViewClass *klass);
74 static void gtk_text_view_destroy              (GtkObject        *object);
75 static void gtk_text_view_finalize             (GObject          *object);
76 static void gtk_text_view_set_arg              (GtkObject        *object,
77                                                 GtkArg           *arg,
78                                                 guint             arg_id);
79 static void gtk_text_view_get_arg              (GtkObject        *object,
80                                                 GtkArg           *arg,
81                                                 guint             arg_id);
82 static void gtk_text_view_size_request         (GtkWidget        *widget,
83                                                 GtkRequisition   *requisition);
84 static void gtk_text_view_size_allocate        (GtkWidget        *widget,
85                                                 GtkAllocation    *allocation);
86 static void gtk_text_view_realize              (GtkWidget        *widget);
87 static void gtk_text_view_unrealize            (GtkWidget        *widget);
88 static void gtk_text_view_style_set            (GtkWidget        *widget,
89                                                 GtkStyle         *previous_style);
90 static void gtk_text_view_direction_changed    (GtkWidget        *widget,
91                                                 GtkTextDirection  previous_direction);
92 static gint gtk_text_view_event                (GtkWidget        *widget,
93                                                 GdkEvent         *event);
94 static gint gtk_text_view_key_press_event      (GtkWidget        *widget,
95                                                 GdkEventKey      *event);
96 static gint gtk_text_view_key_release_event    (GtkWidget        *widget,
97                                                 GdkEventKey      *event);
98 static gint gtk_text_view_button_press_event   (GtkWidget        *widget,
99                                                 GdkEventButton   *event);
100 static gint gtk_text_view_button_release_event (GtkWidget        *widget,
101                                                 GdkEventButton   *event);
102 static gint gtk_text_view_focus_in_event       (GtkWidget        *widget,
103                                                 GdkEventFocus    *event);
104 static gint gtk_text_view_focus_out_event      (GtkWidget        *widget,
105                                                 GdkEventFocus    *event);
106 static gint gtk_text_view_motion_event         (GtkWidget        *widget,
107                                                 GdkEventMotion   *event);
108 static void gtk_text_view_draw                 (GtkWidget        *widget,
109                                                 GdkRectangle     *area);
110 static gint gtk_text_view_expose_event         (GtkWidget        *widget,
111                                                 GdkEventExpose   *expose);
112 static void gtk_text_view_draw_focus           (GtkWidget        *widget);
113
114 /* Source side drag signals */
115 static void gtk_text_view_drag_begin       (GtkWidget        *widget,
116                                             GdkDragContext   *context);
117 static void gtk_text_view_drag_end         (GtkWidget        *widget,
118                                             GdkDragContext   *context);
119 static void gtk_text_view_drag_data_get    (GtkWidget        *widget,
120                                             GdkDragContext   *context,
121                                             GtkSelectionData *selection_data,
122                                             guint             info,
123                                             guint             time);
124 static void gtk_text_view_drag_data_delete (GtkWidget        *widget,
125                                             GdkDragContext   *context);
126
127 /* Target side drag signals */
128 static void     gtk_text_view_drag_leave         (GtkWidget        *widget,
129                                                   GdkDragContext   *context,
130                                                   guint             time);
131 static gboolean gtk_text_view_drag_motion        (GtkWidget        *widget,
132                                                   GdkDragContext   *context,
133                                                   gint              x,
134                                                   gint              y,
135                                                   guint             time);
136 static gboolean gtk_text_view_drag_drop          (GtkWidget        *widget,
137                                                   GdkDragContext   *context,
138                                                   gint              x,
139                                                   gint              y,
140                                                   guint             time);
141 static void     gtk_text_view_drag_data_received (GtkWidget        *widget,
142                                                   GdkDragContext   *context,
143                                                   gint              x,
144                                                   gint              y,
145                                                   GtkSelectionData *selection_data,
146                                                   guint             info,
147                                                   guint             time);
148
149 static void gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
150                                                   GtkAdjustment *hadj,
151                                                   GtkAdjustment *vadj);
152
153 static void gtk_text_view_move             (GtkTextView           *text_view,
154                                             GtkMovementStep        step,
155                                             gint                   count,
156                                             gboolean               extend_selection);
157 static void gtk_text_view_set_anchor       (GtkTextView           *text_view);
158 static void gtk_text_view_scroll_pages     (GtkTextView           *text_view,
159                                             gint                   count);
160 static void gtk_text_view_insert           (GtkTextView           *text_view,
161                                             const gchar           *str);
162 static void gtk_text_view_delete           (GtkTextView           *text_view,
163                                             GtkDeleteType          type,
164                                             gint                   count);
165 static void gtk_text_view_cut_clipboard    (GtkTextView           *text_view);
166 static void gtk_text_view_copy_clipboard   (GtkTextView           *text_view);
167 static void gtk_text_view_paste_clipboard  (GtkTextView           *text_view);
168 static void gtk_text_view_toggle_overwrite (GtkTextView           *text_view);
169 static void gtk_text_view_unselect         (GtkTextView           *text_view);
170
171 static void     gtk_text_view_validate_onscreen     (GtkTextView        *text_view);
172 static void     gtk_text_view_get_first_para_iter   (GtkTextView        *text_view,
173                                                      GtkTextIter        *iter);
174 static void     gtk_text_view_scroll_calc_now       (GtkTextView        *text_view);
175 static void     gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
176                                                          GtkTextAttributes *values,
177                                                          GtkStyle           *style);
178 static void     gtk_text_view_ensure_layout         (GtkTextView        *text_view);
179 static void     gtk_text_view_destroy_layout        (GtkTextView        *text_view);
180 static void     gtk_text_view_start_selection_drag  (GtkTextView        *text_view,
181                                                      const GtkTextIter  *iter,
182                                                      GdkEventButton     *event);
183 static gboolean gtk_text_view_end_selection_drag    (GtkTextView        *text_view,
184                                                      GdkEventButton     *event);
185 static void     gtk_text_view_start_selection_dnd   (GtkTextView        *text_view,
186                                                      const GtkTextIter  *iter,
187                                                      GdkEventMotion     *event);
188 static void     gtk_text_view_start_cursor_blink    (GtkTextView        *text_view);
189 static void     gtk_text_view_stop_cursor_blink     (GtkTextView        *text_view);
190
191 static void gtk_text_view_value_changed             (GtkAdjustment *adj,
192                                                      GtkTextView   *view);
193 static void gtk_text_view_commit_handler            (GtkIMContext  *context,
194                                                      const gchar   *str,
195                                                      GtkTextView   *text_view);
196
197 static void gtk_text_view_mark_set_handler       (GtkTextBuffer     *buffer,
198                                                   const GtkTextIter *location,
199                                                   GtkTextMark       *mark,
200                                                   gpointer           data);
201 static void gtk_text_view_get_virtual_cursor_pos (GtkTextView       *text_view,
202                                                   gint              *x,
203                                                   gint              *y);
204 static void gtk_text_view_set_virtual_cursor_pos (GtkTextView       *text_view,
205                                                   gint               x,
206                                                   gint               y);
207
208 static GtkAdjustment* get_hadjustment            (GtkTextView       *text_view);
209 static GtkAdjustment* get_vadjustment            (GtkTextView       *text_view);
210
211 /* Container methods */
212 static void gtk_text_view_add    (GtkContainer *container,
213                                   GtkWidget    *child);
214 static void gtk_text_view_remove (GtkContainer *container,
215                                   GtkWidget    *child);
216 static void gtk_text_view_forall (GtkContainer *container,
217                                   gboolean      include_internals,
218                                   GtkCallback   callback,
219                                   gpointer      callback_data);
220
221 /* FIXME probably need the focus methods. */
222
223 typedef struct _GtkTextViewChild GtkTextViewChild;
224
225 struct _GtkTextViewChild
226 {
227   GtkWidget *widget;
228
229   GtkTextChildAnchor *anchor;
230
231   /* These are ignored if anchor != NULL */
232   GtkTextWindowType type;
233   gint x;
234   gint y;
235 };
236
237 static GtkTextViewChild* text_view_child_new_anchored (GtkWidget          *child,
238                                                        GtkTextChildAnchor *anchor);
239 static GtkTextViewChild* text_view_child_new_window   (GtkWidget          *child,
240                                                        GtkTextWindowType   type,
241                                                        gint                x,
242                                                        gint                y);
243 static void              text_view_child_free         (GtkTextViewChild   *child);
244
245 static void              text_view_child_realize      (GtkTextView      *text_view,
246                                                        GtkTextViewChild *child);
247 static void              text_view_child_unrealize    (GtkTextViewChild *child);
248
249 struct _GtkTextWindow
250 {
251   GtkTextWindowType type;
252   GtkWidget *widget;
253   GdkWindow *window;
254   GdkWindow *bin_window;
255   GtkRequisition requisition;
256   GdkRectangle allocation;
257 };
258
259 static GtkTextWindow *text_window_new             (GtkTextWindowType  type,
260                                                    GtkWidget         *widget,
261                                                    gint               width_request,
262                                                    gint               height_request);
263 static void           text_window_free            (GtkTextWindow     *win);
264 static void           text_window_realize         (GtkTextWindow     *win,
265                                                    GdkWindow         *parent);
266 static void           text_window_unrealize       (GtkTextWindow     *win);
267 static void           text_window_size_allocate   (GtkTextWindow     *win,
268                                                    GdkRectangle      *rect);
269 static void           text_window_scroll          (GtkTextWindow     *win,
270                                                    gint               dx,
271                                                    gint               dy);
272 static void           text_window_invalidate_rect (GtkTextWindow     *win,
273                                                    GdkRectangle      *rect);
274
275 static gint           text_window_get_width       (GtkTextWindow     *win);
276 static gint           text_window_get_height      (GtkTextWindow     *win);
277 static void           text_window_get_allocation  (GtkTextWindow     *win,
278                                                    GdkRectangle      *rect);
279
280
281 enum {
282   TARGET_STRING,
283   TARGET_TEXT,
284   TARGET_COMPOUND_TEXT,
285   TARGET_UTF8_STRING
286 };
287
288 static GtkTargetEntry target_table[] = {
289   { "UTF8_STRING", 0, TARGET_UTF8_STRING },
290   { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
291   { "TEXT", 0, TARGET_TEXT },
292   { "text/plain", 0, TARGET_STRING },
293   { "STRING",     0, TARGET_STRING }
294 };
295
296 static GtkContainerClass *parent_class = NULL;
297 static guint signals[LAST_SIGNAL] = { 0 };
298
299 GtkType
300 gtk_text_view_get_type (void)
301 {
302   static GtkType our_type = 0;
303
304   if (our_type == 0)
305     {
306       static const GtkTypeInfo our_info =
307       {
308         "GtkTextView",
309         sizeof (GtkTextView),
310         sizeof (GtkTextViewClass),
311         (GtkClassInitFunc) gtk_text_view_class_init,
312         (GtkObjectInitFunc) gtk_text_view_init,
313         /* reserved_1 */ NULL,
314         /* reserved_2 */ NULL,
315         (GtkClassInitFunc) NULL
316       };
317
318     our_type = gtk_type_unique (GTK_TYPE_CONTAINER, &our_info);
319   }
320
321   return our_type;
322 }
323
324 static void
325 add_move_binding (GtkBindingSet  *binding_set,
326                   guint           keyval,
327                   guint           modmask,
328                   GtkMovementStep step,
329                   gint            count)
330 {
331   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
332   
333   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
334                                 "move", 3,
335                                 GTK_TYPE_ENUM, step,
336                                 GTK_TYPE_INT, count,
337                                 GTK_TYPE_BOOL, FALSE);
338
339   /* Selection-extending version */
340   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
341                                 "move", 3,
342                                 GTK_TYPE_ENUM, step,
343                                 GTK_TYPE_INT, count,
344                                 GTK_TYPE_BOOL, TRUE);
345 }
346
347 static void
348 gtk_text_view_class_init (GtkTextViewClass *klass)
349 {
350   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
351   GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
352   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
353   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
354   GtkBindingSet *binding_set;
355   
356   parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
357
358   /*
359    * Arguments
360    */
361   gtk_object_add_arg_type ("GtkTextView::height_lines", GTK_TYPE_INT,
362                            GTK_ARG_READWRITE, ARG_HEIGHT_LINES);
363   gtk_object_add_arg_type ("GtkTextView::width_columns", GTK_TYPE_INT,
364                            GTK_ARG_READWRITE, ARG_WIDTH_COLUMNS);
365   gtk_object_add_arg_type ("GtkTextView::pixels_above_lines", GTK_TYPE_INT,
366                            GTK_ARG_READWRITE, ARG_PIXELS_ABOVE_LINES);
367   gtk_object_add_arg_type ("GtkTextView::pixels_below_lines", GTK_TYPE_INT,
368                            GTK_ARG_READWRITE, ARG_PIXELS_BELOW_LINES);
369   gtk_object_add_arg_type ("GtkTextView::pixels_inside_wrap", GTK_TYPE_INT,
370                            GTK_ARG_READWRITE, ARG_PIXELS_INSIDE_WRAP);
371   gtk_object_add_arg_type ("GtkTextView::editable", GTK_TYPE_BOOL,
372                            GTK_ARG_READWRITE, ARG_EDITABLE);
373   gtk_object_add_arg_type ("GtkTextView::wrap_mode", GTK_TYPE_ENUM,
374                            GTK_ARG_READWRITE, ARG_WRAP_MODE);
375
376
377   /*
378    * Signals
379    */
380
381   signals[MOVE] = 
382       gtk_signal_new ("move",
383                       GTK_RUN_LAST | GTK_RUN_ACTION,
384                       GTK_CLASS_TYPE (object_class),
385                       GTK_SIGNAL_OFFSET (GtkTextViewClass, move),
386                       gtk_marshal_VOID__ENUM_INT_BOOLEAN,
387                       GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL);
388
389   signals[SET_ANCHOR] = 
390       gtk_signal_new ("set_anchor",
391                       GTK_RUN_LAST | GTK_RUN_ACTION,
392                       GTK_CLASS_TYPE (object_class),
393                       GTK_SIGNAL_OFFSET (GtkTextViewClass, set_anchor),
394                       gtk_marshal_VOID__VOID,
395                       GTK_TYPE_NONE, 0);
396
397   signals[INSERT] = 
398       gtk_signal_new ("insert",
399                       GTK_RUN_LAST | GTK_RUN_ACTION,
400                       GTK_CLASS_TYPE (object_class),
401                       GTK_SIGNAL_OFFSET (GtkTextViewClass, insert),
402                       gtk_marshal_VOID__POINTER,
403                       GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
404
405   signals[DELETE] = 
406       gtk_signal_new ("delete",
407                       GTK_RUN_LAST | GTK_RUN_ACTION,
408                       GTK_CLASS_TYPE (object_class),
409                       GTK_SIGNAL_OFFSET (GtkTextViewClass, delete),
410                       gtk_marshal_VOID__ENUM_INT,
411                       GTK_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, GTK_TYPE_INT);
412
413   signals[CUT_CLIPBOARD] =
414     gtk_signal_new ("cut_clipboard",
415                     GTK_RUN_LAST | GTK_RUN_ACTION,
416                     GTK_CLASS_TYPE (object_class),
417                     GTK_SIGNAL_OFFSET (GtkTextViewClass, cut_clipboard),
418                     gtk_marshal_VOID__VOID,
419                     GTK_TYPE_NONE, 0);
420
421   signals[COPY_CLIPBOARD] =
422     gtk_signal_new ("copy_clipboard",
423                     GTK_RUN_LAST | GTK_RUN_ACTION,
424                     GTK_CLASS_TYPE (object_class),
425                     GTK_SIGNAL_OFFSET (GtkTextViewClass, copy_clipboard),
426                     gtk_marshal_VOID__VOID,
427                     GTK_TYPE_NONE, 0);
428
429   signals[PASTE_CLIPBOARD] =
430     gtk_signal_new ("paste_clipboard",
431                     GTK_RUN_LAST | GTK_RUN_ACTION,
432                     GTK_CLASS_TYPE (object_class),
433                     GTK_SIGNAL_OFFSET (GtkTextViewClass, paste_clipboard),
434                     gtk_marshal_VOID__VOID,
435                     GTK_TYPE_NONE, 0);
436
437   signals[TOGGLE_OVERWRITE] =
438     gtk_signal_new ("toggle_overwrite",
439                     GTK_RUN_LAST | GTK_RUN_ACTION,
440                     GTK_CLASS_TYPE (object_class),
441                     GTK_SIGNAL_OFFSET (GtkTextViewClass, toggle_overwrite),
442                     gtk_marshal_VOID__VOID,
443                     GTK_TYPE_NONE, 0);
444
445   signals[SET_SCROLL_ADJUSTMENTS] = widget_class->set_scroll_adjustments_signal =
446     gtk_signal_new ("set_scroll_adjustments",
447                     GTK_RUN_LAST,
448                     GTK_CLASS_TYPE (object_class),
449                     GTK_SIGNAL_OFFSET (GtkTextViewClass, set_scroll_adjustments),
450                     gtk_marshal_VOID__POINTER_POINTER,
451                     GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
452   
453   gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
454
455   /*
456    * Key bindings
457    */
458
459   binding_set = gtk_binding_set_by_class (klass);
460
461   /* Moving the insertion point */
462   add_move_binding (binding_set, GDK_Right, 0,
463                     GTK_MOVEMENT_POSITIONS, 1);
464   
465   add_move_binding (binding_set, GDK_Left, 0,
466                     GTK_MOVEMENT_POSITIONS, -1);
467
468   add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
469                     GTK_MOVEMENT_CHARS, 1);
470   
471   add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
472                     GTK_MOVEMENT_CHARS, -1);
473   
474   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
475                     GTK_MOVEMENT_WORDS, 1);
476
477   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
478                     GTK_MOVEMENT_WORDS, -1);
479   
480   /* Eventually we want to move by display lines, not paragraphs */
481   add_move_binding (binding_set, GDK_Up, 0,
482                     GTK_MOVEMENT_DISPLAY_LINES, -1);
483   
484   add_move_binding (binding_set, GDK_Down, 0,
485                     GTK_MOVEMENT_DISPLAY_LINES, 1);
486
487   add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK,
488                     GTK_MOVEMENT_DISPLAY_LINES, -1);
489   
490   add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK,
491                     GTK_MOVEMENT_DISPLAY_LINES, 1);
492   
493   add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
494                     GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
495
496   add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
497                     GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
498
499   add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
500                     GTK_MOVEMENT_WORDS, 1);
501
502   add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
503                     GTK_MOVEMENT_WORDS, -1);
504
505   add_move_binding (binding_set, GDK_Home, 0,
506                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
507
508   add_move_binding (binding_set, GDK_End, 0,
509                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
510   
511   add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
512                     GTK_MOVEMENT_BUFFER_ENDS, -1);
513
514   add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
515                     GTK_MOVEMENT_BUFFER_ENDS, 1);
516
517   add_move_binding (binding_set, GDK_Page_Up, 0,
518                     GTK_MOVEMENT_PAGES, -1);
519
520   add_move_binding (binding_set, GDK_Page_Down, 0,
521                     GTK_MOVEMENT_PAGES, 1);
522   
523   
524   /* Setting the cut/paste/copy anchor */
525   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK,
526                                 "set_anchor", 0);
527
528   /* Deleting text */
529   gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
530                                 "delete", 2,
531                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
532                                 GTK_TYPE_INT, 1);
533   
534   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK,
535                                 "delete", 2,
536                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
537                                 GTK_TYPE_INT, 1);
538
539   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
540                                 "delete", 2,
541                                 GTK_TYPE_ENUM, GTK_DELETE_CHARS,
542                                 GTK_TYPE_INT, -1);
543
544   gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
545                                 "delete", 2,
546                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
547                                 GTK_TYPE_INT, 1);
548
549   gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_MOD1_MASK,
550                                 "delete", 2,
551                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
552                                 GTK_TYPE_INT, 1);
553
554   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
555                                 "delete", 2,
556                                 GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
557                                 GTK_TYPE_INT, -1);
558
559   gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
560                                 "delete", 2,
561                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
562                                 GTK_TYPE_INT, 1);
563
564   gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
565                                 "delete", 2,
566                                 GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPHS,
567                                 GTK_TYPE_INT, 1);
568
569   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
570                                 "delete", 2,
571                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
572                                 GTK_TYPE_INT, 1);
573   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
574                                 "insert", 1,
575                                 GTK_TYPE_STRING, " ");
576
577   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
578                                 "delete", 2,
579                                 GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
580                                 GTK_TYPE_INT, 1);
581   
582   /* Cut/copy/paste */
583
584   gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
585                                 "cut_clipboard", 0);
586
587   gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
588                                 "cut_clipboard", 0);
589   
590   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
591                                 "copy_clipboard", 0);
592   
593   gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
594                                 "paste_clipboard", 0);
595
596   gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
597                                 "paste_clipboard", 0);
598
599   /* Overwrite */
600   gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
601                                 "toggle_overwrite", 0);
602   
603   /*
604    * Default handlers and virtual methods
605    */
606   object_class->set_arg = gtk_text_view_set_arg;
607   object_class->get_arg = gtk_text_view_get_arg;
608
609   object_class->destroy = gtk_text_view_destroy;
610   gobject_class->finalize = gtk_text_view_finalize;
611
612   widget_class->realize = gtk_text_view_realize;
613   widget_class->unrealize = gtk_text_view_unrealize;
614   widget_class->style_set = gtk_text_view_style_set;
615   widget_class->direction_changed = gtk_text_view_direction_changed;
616   widget_class->size_request = gtk_text_view_size_request;
617   widget_class->size_allocate = gtk_text_view_size_allocate;
618   widget_class->event = gtk_text_view_event;
619   widget_class->key_press_event = gtk_text_view_key_press_event;
620   widget_class->key_release_event = gtk_text_view_key_release_event;
621   widget_class->button_press_event = gtk_text_view_button_press_event;
622   widget_class->button_release_event = gtk_text_view_button_release_event;
623   widget_class->focus_in_event = gtk_text_view_focus_in_event;
624   widget_class->focus_out_event = gtk_text_view_focus_out_event;
625   widget_class->motion_notify_event = gtk_text_view_motion_event;
626   widget_class->expose_event = gtk_text_view_expose_event;
627   widget_class->draw = gtk_text_view_draw;
628   widget_class->draw_focus = gtk_text_view_draw_focus;
629   
630   widget_class->drag_begin = gtk_text_view_drag_begin;
631   widget_class->drag_end = gtk_text_view_drag_end;
632   widget_class->drag_data_get = gtk_text_view_drag_data_get;
633   widget_class->drag_data_delete = gtk_text_view_drag_data_delete;
634
635   widget_class->drag_leave = gtk_text_view_drag_leave;
636   widget_class->drag_motion = gtk_text_view_drag_motion;
637   widget_class->drag_drop = gtk_text_view_drag_drop;
638   widget_class->drag_data_received = gtk_text_view_drag_data_received;
639
640   container_class->add = gtk_text_view_add;
641   container_class->remove = gtk_text_view_remove;
642   container_class->forall = gtk_text_view_forall;
643   
644   klass->move = gtk_text_view_move;
645   klass->set_anchor = gtk_text_view_set_anchor;
646   klass->insert = gtk_text_view_insert;
647   klass->delete = gtk_text_view_delete;
648   klass->cut_clipboard = gtk_text_view_cut_clipboard;
649   klass->copy_clipboard = gtk_text_view_copy_clipboard;
650   klass->paste_clipboard = gtk_text_view_paste_clipboard;
651   klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
652   klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments;
653 }
654
655 void
656 gtk_text_view_init (GtkTextView *text_view)
657 {
658   GtkWidget *widget;
659   
660   widget = GTK_WIDGET (text_view);
661
662   GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
663
664   text_view->wrap_mode = GTK_WRAPMODE_NONE;
665
666   gtk_drag_dest_set (widget,
667                     GTK_DEST_DEFAULT_DROP,
668                     target_table, G_N_ELEMENTS (target_table),
669                     GDK_ACTION_COPY | GDK_ACTION_MOVE);
670
671   text_view->virtual_cursor_x = -1;
672   text_view->virtual_cursor_y = -1;
673
674   /* This object is completely private. No external entity can gain a reference
675    * to it; so we create it here and destroy it in finalize().
676    */
677   text_view->im_context = gtk_im_multicontext_new ();
678   
679   gtk_signal_connect (GTK_OBJECT (text_view->im_context), "commit",
680                       GTK_SIGNAL_FUNC (gtk_text_view_commit_handler), text_view);
681
682   text_view->editable = TRUE;
683   text_view->cursor_visible = TRUE;
684
685   text_view->text_window = text_window_new (GTK_TEXT_WINDOW_TEXT,
686                                             widget, 200, 200);
687
688   text_view->drag_start_x = -1;
689   text_view->drag_start_y = -1;
690 }
691
692 GtkWidget*
693 gtk_text_view_new (void)
694 {
695   return GTK_WIDGET (gtk_type_new (gtk_text_view_get_type ()));
696 }
697
698 GtkWidget*
699 gtk_text_view_new_with_buffer (GtkTextBuffer *buffer)
700 {
701   GtkTextView *text_view;
702
703   text_view = (GtkTextView*)gtk_text_view_new ();
704
705   gtk_text_view_set_buffer (text_view, buffer);
706
707   return GTK_WIDGET (text_view);
708 }
709
710 void
711 gtk_text_view_set_buffer (GtkTextView *text_view,
712                           GtkTextBuffer *buffer)
713 {
714   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
715   g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
716   
717   if (text_view->buffer == buffer)
718     return;
719
720   if (text_view->buffer != NULL)
721     {
722       gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->buffer),
723                                      gtk_text_view_mark_set_handler, text_view);
724       gtk_object_unref (GTK_OBJECT (text_view->buffer));
725       text_view->dnd_mark = NULL;
726     }
727
728   text_view->buffer = buffer;
729   
730   if (buffer != NULL)
731     {
732       GtkTextIter start;
733       
734       gtk_object_ref (GTK_OBJECT (buffer));
735       gtk_object_sink (GTK_OBJECT (buffer));
736
737       if (text_view->layout)
738         gtk_text_layout_set_buffer (text_view->layout, buffer);
739
740       gtk_text_buffer_get_iter_at_offset (text_view->buffer, &start, 0);
741       
742       text_view->dnd_mark = gtk_text_buffer_create_mark (text_view->buffer,
743                                                          "gtk_drag_target",
744                                                          &start, FALSE);
745
746       text_view->first_para_mark = gtk_text_buffer_create_mark (text_view->buffer,
747                                                                 NULL,
748                                                                 &start, TRUE);
749       
750       text_view->first_para_pixels = 0;
751       
752       gtk_signal_connect (GTK_OBJECT (text_view->buffer), "mark_set",
753                           gtk_text_view_mark_set_handler, text_view);
754     }
755
756   if (GTK_WIDGET_VISIBLE (text_view)) 
757     gtk_widget_queue_draw (GTK_WIDGET (text_view));
758 }
759
760 GtkTextBuffer*
761 gtk_text_view_get_buffer (GtkTextView *text_view)
762 {
763   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
764   
765   return text_view->buffer;
766 }
767
768 void
769 gtk_text_view_get_iter_at_location (GtkTextView *text_view,
770                                     GtkTextIter *iter,
771                                     gint x, gint y)
772 {
773   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
774   g_return_if_fail (iter != NULL);
775   g_return_if_fail (text_view->layout != NULL);
776
777   gtk_text_layout_get_iter_at_pixel (text_view->layout,
778                                      iter,
779                                      x,
780                                      y);
781 }
782
783 void
784 gtk_text_view_get_iter_location (GtkTextView       *text_view,
785                                  const GtkTextIter *iter,
786                                  GdkRectangle      *location)
787 {
788   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
789   g_return_if_fail (gtk_text_iter_get_buffer (iter) == text_view->buffer);
790
791   gtk_text_layout_get_iter_location (text_view->layout, iter, location);
792 }
793
794 void
795 gtk_text_view_get_line_yrange (GtkTextView       *text_view,
796                                const GtkTextIter *iter,
797                                gint              *y,
798                                gint              *height)
799 {
800   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
801   g_return_if_fail (gtk_text_iter_get_buffer (iter) == text_view->buffer);
802   
803   gtk_text_layout_get_line_yrange (text_view->layout,
804                                    iter,
805                                    y,
806                                    height);
807 }
808
809 void
810 gtk_text_view_get_line_at_y (GtkTextView *text_view,
811                              GtkTextIter *target_iter,
812                              gint         y,
813                              gint        *line_top)
814 {
815   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
816
817   gtk_text_layout_get_line_at_y (text_view->layout,
818                                  target_iter,
819                                  y,
820                                  line_top);
821 }
822
823 static void
824 set_adjustment_clamped (GtkAdjustment *adj, gfloat val)
825 {
826   /* We don't really want to clamp to upper; we want to clamp to
827      upper - page_size which is the highest value the scrollbar
828      will let us reach. */
829   if (val > (adj->upper - adj->page_size))
830     val = adj->upper - adj->page_size;
831
832   if (val < adj->lower)
833     val = adj->lower;
834   
835   gtk_adjustment_set_value (adj, val);
836 }
837
838 static gboolean
839 gtk_text_view_scroll_to_mark_adjusted (GtkTextView *text_view,
840                                        GtkTextMark *mark,
841                                        gint margin,
842                                        gfloat percentage)
843 {
844   GtkTextIter iter;
845   GdkRectangle rect;
846   GdkRectangle screen;
847   gint screen_bottom;
848   gint screen_right;
849   GtkWidget *widget;
850   gboolean retval = FALSE;
851   gint scroll_inc;
852
853   gint current_x_scroll, current_y_scroll;
854   
855   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
856   g_return_val_if_fail (mark != NULL, FALSE);
857
858   widget = GTK_WIDGET (text_view);
859   
860   if (!GTK_WIDGET_MAPPED (widget))
861     {
862       g_warning ("FIXME need to implement scroll_to_mark for unmapped GtkTextView?");
863       return FALSE;
864     }
865
866   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &iter, mark);
867   
868   gtk_text_layout_get_iter_location (text_view->layout,
869                                      &iter,
870                                      &rect);
871   
872   /* Be sure the scroll region is up-to-date */
873   gtk_text_view_scroll_calc_now (text_view);
874   
875   current_x_scroll = text_view->xoffset;
876   current_y_scroll = text_view->yoffset;
877
878   screen.x = current_x_scroll;
879   screen.y = current_y_scroll;
880   screen.width = SCREEN_WIDTH (widget);
881   screen.height = SCREEN_HEIGHT (widget);
882
883   {
884     /* Clamp margin so it's not too large. */
885     gint small_dimension = MIN (screen.width, screen.height);
886     gint max_rect_dim;
887     
888     if (margin > (small_dimension/2 - 5)) /* 5 is arbitrary */
889       margin = (small_dimension/2 - 5);
890
891     if (margin < 0)
892       margin = 0;
893     
894     /* make sure rectangle fits in the leftover space */
895
896     max_rect_dim = MAX (rect.width, rect.height);
897     
898     if (max_rect_dim > (small_dimension - margin*2))
899       margin -= max_rect_dim - (small_dimension - margin*2);
900                  
901     if (margin < 0)
902       margin = 0;
903   }
904
905   g_assert (margin >= 0);
906   
907   screen.x += margin;
908   screen.y += margin;
909
910   screen.width -= margin*2;
911   screen.height -= margin*2;
912
913   screen_bottom = screen.y + screen.height;
914   screen_right = screen.x + screen.width;
915   
916   /* Vertical scroll (only vertical gets adjusted) */
917
918   scroll_inc = 0;
919   if (rect.y < screen.y)
920     {
921       gint scroll_dest = rect.y;
922       scroll_inc = (scroll_dest - screen.y) * percentage;
923     }
924   else if ((rect.y + rect.height) > screen_bottom)
925     {
926       gint scroll_dest = rect.y + rect.height;
927       scroll_inc = (scroll_dest - screen_bottom) * percentage;
928     }
929
930   if (scroll_inc != 0)
931     {
932       set_adjustment_clamped (get_vadjustment (text_view),
933                               current_y_scroll + scroll_inc);
934       retval = TRUE;
935     }
936   
937   /* Horizontal scroll */
938
939   scroll_inc = 0;
940   if (rect.x < screen.x)
941     {
942       gint scroll_dest = rect.x;
943       scroll_inc = scroll_dest - screen.x;
944     }
945   else if ((rect.x + rect.width) > screen_right)
946     {
947       gint scroll_dest = rect.x + rect.width;
948       scroll_inc = scroll_dest - screen_right;
949     }
950
951   if (scroll_inc != 0)
952     {
953       set_adjustment_clamped (get_hadjustment (text_view),
954                               current_x_scroll + scroll_inc);
955       retval = TRUE;
956     }
957
958   return retval;
959 }
960
961 gboolean
962 gtk_text_view_scroll_to_mark (GtkTextView *text_view,
963                               GtkTextMark *mark,
964                               gint mark_within_margin)
965 {
966   g_return_val_if_fail (mark_within_margin >= 0, FALSE);
967   
968   return gtk_text_view_scroll_to_mark_adjusted (text_view, mark,
969                                                 mark_within_margin, 1.0);
970 }
971
972 static gboolean
973 clamp_iter_onscreen (GtkTextView *text_view, GtkTextIter *iter)
974 {
975   GdkRectangle visible_rect;
976   gtk_text_view_get_visible_rect (text_view, &visible_rect);
977
978   return gtk_text_layout_clamp_iter_to_vrange (text_view->layout, iter,
979                                                visible_rect.y,
980                                                visible_rect.y + visible_rect.height);
981 }
982
983 gboolean
984 gtk_text_view_move_mark_onscreen (GtkTextView *text_view,
985                                   GtkTextMark *mark)
986 {
987   GtkTextIter iter;
988   
989   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
990   g_return_val_if_fail (mark != NULL, FALSE);
991   
992   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &iter, mark);
993   
994   if (clamp_iter_onscreen (text_view, &iter))
995     {
996       gtk_text_buffer_move_mark (text_view->buffer, mark, &iter);
997       return TRUE;
998     }
999   else
1000     return FALSE;
1001 }
1002
1003 void
1004 gtk_text_view_get_visible_rect (GtkTextView  *text_view,
1005                                 GdkRectangle *visible_rect)
1006 {
1007   GtkWidget *widget;
1008
1009   g_return_if_fail (text_view != NULL);
1010   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1011
1012   widget = GTK_WIDGET (text_view);
1013   
1014   if (visible_rect)
1015     {
1016       visible_rect->x = text_view->xoffset;
1017       visible_rect->y = text_view->yoffset;
1018       visible_rect->width = SCREEN_WIDTH (widget);
1019       visible_rect->height = SCREEN_HEIGHT (widget);
1020     }
1021 }
1022
1023 void
1024 gtk_text_view_set_wrap_mode (GtkTextView *text_view,
1025                              GtkWrapMode  wrap_mode)
1026 {
1027   g_return_if_fail (text_view != NULL);
1028   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1029
1030   if (text_view->wrap_mode != wrap_mode)
1031     {
1032       text_view->wrap_mode = wrap_mode;
1033
1034       if (text_view->layout)
1035         {
1036           text_view->layout->default_style->wrap_mode = wrap_mode;
1037           gtk_text_layout_default_style_changed (text_view->layout);
1038         }
1039     }
1040 }
1041
1042 GtkWrapMode
1043 gtk_text_view_get_wrap_mode (GtkTextView *text_view)
1044 {
1045   g_return_val_if_fail (text_view != NULL, GTK_WRAPMODE_NONE);
1046   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_WRAPMODE_NONE);
1047
1048   return text_view->wrap_mode;
1049 }
1050
1051 void
1052 gtk_text_view_set_editable (GtkTextView *text_view,
1053                             gboolean     setting)
1054 {
1055   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1056
1057   if (text_view->editable != setting)
1058     {
1059       text_view->editable = setting;
1060
1061       if (text_view->layout)
1062         {
1063           text_view->layout->default_style->editable = text_view->editable;
1064           gtk_text_layout_default_style_changed (text_view->layout);
1065         }
1066     }
1067 }
1068
1069 gboolean
1070 gtk_text_view_get_editable (GtkTextView *text_view)
1071 {
1072   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1073   
1074   return text_view->editable;
1075 }
1076
1077 void
1078 gtk_text_view_set_cursor_visible    (GtkTextView   *text_view,
1079                                      gboolean       setting)
1080 {
1081   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1082
1083   setting = (setting != FALSE);
1084
1085   if (text_view->cursor_visible != setting)
1086     {
1087       text_view->cursor_visible = setting;
1088
1089       if (GTK_WIDGET_HAS_FOCUS (text_view))
1090         {
1091           if (text_view->layout)
1092             {
1093               gtk_text_layout_set_cursor_visible (text_view->layout, setting);
1094           
1095               if (setting)
1096                 gtk_text_view_start_cursor_blink (text_view);
1097               else
1098                 gtk_text_view_stop_cursor_blink (text_view);
1099             }
1100         }
1101     }
1102 }
1103
1104 gboolean
1105 gtk_text_view_get_cursor_visible    (GtkTextView   *text_view)
1106 {
1107   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1108
1109   return text_view->cursor_visible;
1110 }
1111
1112
1113 gboolean
1114 gtk_text_view_place_cursor_onscreen (GtkTextView *text_view)
1115 {
1116   GtkTextIter insert;
1117   
1118   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1119   
1120   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
1121                                     gtk_text_buffer_get_mark (text_view->buffer,
1122                                                               "insert"));
1123   
1124   if (clamp_iter_onscreen (text_view, &insert))
1125     {
1126       gtk_text_buffer_place_cursor (text_view->buffer, &insert);
1127       return TRUE;
1128     }
1129   else
1130     return FALSE;
1131 }
1132
1133 static void
1134 gtk_text_view_destroy (GtkObject *object)
1135 {
1136   GtkTextView *text_view;
1137
1138   text_view = GTK_TEXT_VIEW (object);
1139
1140   gtk_text_view_destroy_layout (text_view);
1141   gtk_text_view_set_buffer (text_view, NULL);
1142
1143   (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
1144 }
1145
1146 static void
1147 gtk_text_view_finalize (GObject *object)
1148 {
1149   GtkTextView *text_view;
1150
1151   text_view = GTK_TEXT_VIEW (object);  
1152
1153   if (text_view->hadjustment)
1154     gtk_object_unref (GTK_OBJECT (text_view->hadjustment));
1155   if (text_view->vadjustment)
1156     gtk_object_unref (GTK_OBJECT (text_view->vadjustment));
1157
1158   text_window_free (text_view->text_window);
1159
1160   if (text_view->left_window)
1161     text_window_free (text_view->left_window);
1162
1163   if (text_view->top_window)
1164     text_window_free (text_view->top_window);
1165
1166   if (text_view->right_window)
1167     text_window_free (text_view->right_window);
1168   
1169   if (text_view->bottom_window)
1170     text_window_free (text_view->bottom_window);
1171   
1172   gtk_object_unref (GTK_OBJECT (text_view->im_context));  
1173   
1174   (* G_OBJECT_CLASS (parent_class)->finalize) (object);
1175 }
1176
1177 static void
1178 gtk_text_view_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
1179 {
1180   GtkTextView *text_view;
1181
1182   text_view = GTK_TEXT_VIEW (object);
1183
1184   switch (arg_id)
1185     {
1186     case ARG_HEIGHT_LINES:
1187       break;
1188
1189     case ARG_WIDTH_COLUMNS:
1190       break;
1191
1192     case ARG_PIXELS_ABOVE_LINES:
1193       break;
1194
1195     case ARG_PIXELS_BELOW_LINES:
1196       break;
1197
1198     case ARG_PIXELS_INSIDE_WRAP:
1199       break;
1200
1201     case ARG_EDITABLE:
1202       break;
1203
1204     case ARG_WRAP_MODE:
1205       break;
1206
1207     default:
1208       g_assert_not_reached ();
1209       break;
1210     }
1211 }
1212
1213 static void
1214 gtk_text_view_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
1215 {
1216   GtkTextView *text_view;
1217
1218   text_view = GTK_TEXT_VIEW (object);
1219
1220   switch (arg_id)
1221     {
1222     case ARG_HEIGHT_LINES:
1223       break;
1224
1225     case ARG_WIDTH_COLUMNS:
1226       break;
1227
1228     case ARG_PIXELS_ABOVE_LINES:
1229       break;
1230
1231     case ARG_PIXELS_BELOW_LINES:
1232       break;
1233
1234     case ARG_PIXELS_INSIDE_WRAP:
1235       break;
1236
1237     case ARG_EDITABLE:
1238       break;
1239
1240     case ARG_WRAP_MODE:
1241       break;
1242
1243     default:
1244       arg->type = GTK_TYPE_INVALID;
1245       break;
1246     }
1247 }
1248
1249 static void
1250 gtk_text_view_size_request (GtkWidget      *widget,
1251                             GtkRequisition *requisition)
1252 {
1253   GtkTextView *text_view;
1254
1255   text_view = GTK_TEXT_VIEW (widget);
1256
1257   requisition->width = text_view->text_window->requisition.width + FOCUS_EDGE_WIDTH * 2;
1258   requisition->height = text_view->text_window->requisition.height + FOCUS_EDGE_WIDTH * 2;
1259
1260   if (text_view->left_window)
1261     requisition->width += text_view->left_window->requisition.width;
1262
1263   if (text_view->right_window)
1264     requisition->width += text_view->right_window->requisition.width;
1265
1266   if (text_view->top_window)
1267     requisition->height += text_view->top_window->requisition.height;
1268
1269   if (text_view->bottom_window)
1270     requisition->height += text_view->bottom_window->requisition.height;
1271 }
1272
1273 static void
1274 gtk_text_view_size_allocate (GtkWidget *widget,
1275                              GtkAllocation *allocation)
1276 {
1277   GtkTextView *text_view;
1278   GtkTextIter first_para;
1279   gint y;
1280   GtkAdjustment *vadj;
1281   gboolean yoffset_changed = FALSE;
1282   gint width, height;
1283   GdkRectangle text_rect;
1284   GdkRectangle left_rect;
1285   GdkRectangle right_rect;
1286   GdkRectangle top_rect;
1287   GdkRectangle bottom_rect;
1288   
1289   text_view = GTK_TEXT_VIEW (widget);
1290   
1291   widget->allocation = *allocation;
1292
1293   if (GTK_WIDGET_REALIZED (widget))
1294     {
1295       gdk_window_move_resize (widget->window,
1296                               allocation->x, allocation->y,
1297                               allocation->width, allocation->height);
1298     }
1299
1300   /* distribute width/height among child windows. Ensure all
1301    * windows get at least a 1x1 allocation.
1302    */
1303
1304   width = allocation->width - FOCUS_EDGE_WIDTH * 2;
1305
1306   if (text_view->left_window)
1307     left_rect.width = text_view->left_window->requisition.width;
1308   else
1309     left_rect.width = 1;
1310
1311   width -= left_rect.width;
1312
1313   if (text_view->right_window)
1314     right_rect.width = text_view->right_window->requisition.width;
1315   else
1316     right_rect.width = 1;
1317
1318   width -= right_rect.width;
1319
1320   text_rect.width = MAX (1, width);
1321
1322   top_rect.width = text_rect.width;
1323   bottom_rect.width = text_rect.width;
1324
1325
1326   height = allocation->height - FOCUS_EDGE_WIDTH * 2;
1327
1328   if (text_view->top_window)
1329     top_rect.height = text_view->top_window->requisition.height;
1330   else
1331     top_rect.height = 1;
1332
1333   height -= top_rect.height;
1334
1335   if (text_view->bottom_window)
1336     bottom_rect.height = text_view->bottom_window->requisition.height;
1337   else
1338     bottom_rect.height = 1;
1339
1340   height -= bottom_rect.height;
1341
1342   text_rect.height = MAX (1, height);
1343
1344   left_rect.height = text_rect.height;
1345   right_rect.height = text_rect.height;
1346
1347   /* Origins */
1348   left_rect.x = FOCUS_EDGE_WIDTH;
1349   top_rect.y = FOCUS_EDGE_WIDTH;
1350
1351   text_rect.x = left_rect.x + left_rect.width;
1352   text_rect.y = top_rect.y + top_rect.height;
1353   
1354   left_rect.y = text_rect.y;
1355   right_rect.y = text_rect.y;
1356
1357   top_rect.x = text_rect.x;
1358   bottom_rect.x = text_rect.x;
1359   
1360   right_rect.x = text_rect.x + text_rect.width;
1361   bottom_rect.y = text_rect.y + text_rect.height;  
1362   
1363   text_window_size_allocate (text_view->text_window,
1364                              &text_rect);
1365
1366   if (text_view->left_window)
1367     text_window_size_allocate (text_view->left_window,
1368                                &left_rect);
1369
1370   if (text_view->right_window)
1371     text_window_size_allocate (text_view->right_window,
1372                                &right_rect);
1373
1374   if (text_view->top_window)
1375     text_window_size_allocate (text_view->top_window,
1376                                &top_rect);
1377
1378   if (text_view->bottom_window)
1379     text_window_size_allocate (text_view->bottom_window,
1380                                &bottom_rect);
1381   
1382   gtk_text_view_ensure_layout (text_view);
1383   gtk_text_layout_set_screen_width (text_view->layout,
1384                                     SCREEN_WIDTH (text_view));
1385       
1386   gtk_text_view_validate_onscreen (text_view);
1387   gtk_text_view_scroll_calc_now (text_view);
1388
1389   /* Now adjust the value of the adjustment to keep the cursor at the
1390    * same place in the buffer
1391    */
1392   gtk_text_view_get_first_para_iter (text_view, &first_para);
1393   gtk_text_layout_get_line_yrange (text_view->layout, &first_para, &y, NULL);
1394
1395   y += text_view->first_para_pixels;
1396
1397   /* Ensure h/v adj exist */
1398   get_hadjustment (text_view);
1399   get_vadjustment (text_view);
1400   
1401   vadj = text_view->vadjustment;
1402   if (y > vadj->upper - vadj->page_size)
1403     y = MAX (0, vadj->upper - vadj->page_size);
1404
1405   if (y != text_view->yoffset)
1406     {
1407       vadj->value = text_view->yoffset = y;
1408       yoffset_changed = TRUE;
1409     }
1410   
1411   text_view->hadjustment->page_size = SCREEN_WIDTH (text_view);
1412   text_view->hadjustment->page_increment = SCREEN_WIDTH (text_view) / 2;
1413   text_view->hadjustment->lower = 0;
1414   text_view->hadjustment->upper = MAX (SCREEN_WIDTH (text_view),
1415                                        text_view->width);
1416   gtk_signal_emit_by_name (GTK_OBJECT (text_view->hadjustment), "changed");
1417
1418   text_view->vadjustment->page_size = SCREEN_HEIGHT (text_view);
1419   text_view->vadjustment->page_increment = SCREEN_HEIGHT (text_view) / 2;
1420   text_view->vadjustment->lower = 0;
1421   text_view->vadjustment->upper = MAX (SCREEN_HEIGHT (text_view),
1422                                        text_view->height);
1423   gtk_signal_emit_by_name (GTK_OBJECT (text_view->vadjustment), "changed");
1424
1425   if (yoffset_changed)
1426     gtk_adjustment_value_changed (vadj);
1427 }
1428
1429 static void
1430 gtk_text_view_get_first_para_iter (GtkTextView *text_view,
1431                                    GtkTextIter *iter)
1432 {
1433   gtk_text_buffer_get_iter_at_mark (text_view->buffer, iter,
1434                                     text_view->first_para_mark);
1435 }
1436
1437 static void
1438 gtk_text_view_validate_onscreen (GtkTextView *text_view)
1439 {
1440   GtkWidget *widget = GTK_WIDGET (text_view);
1441   
1442   if (SCREEN_HEIGHT (widget) > 0)
1443     {
1444       GtkTextIter first_para;
1445       gtk_text_view_get_first_para_iter (text_view, &first_para);
1446       gtk_text_layout_validate_yrange (text_view->layout,
1447                                        &first_para,
1448                                        0,
1449                                        text_view->first_para_pixels +
1450                                        SCREEN_HEIGHT (widget));
1451     }
1452 }
1453
1454 static gboolean
1455 first_validate_callback (gpointer data)
1456 {
1457   GtkTextView *text_view = data;
1458
1459   gtk_text_view_validate_onscreen (text_view);
1460   
1461   text_view->first_validate_idle = 0;
1462   return FALSE;
1463 }
1464
1465 static gboolean
1466 incremental_validate_callback (gpointer data)
1467 {
1468   GtkTextView *text_view = data;
1469
1470   gtk_text_layout_validate (text_view->layout, 2000);
1471   if (gtk_text_layout_is_valid (text_view->layout))
1472     {
1473       text_view->incremental_validate_idle = 0;
1474       return FALSE;
1475     }
1476   else
1477     return TRUE;
1478 }
1479
1480 static void
1481 invalidated_handler (GtkTextLayout *layout,
1482                      gpointer       data)
1483 {
1484   GtkTextView *text_view;
1485
1486   text_view = GTK_TEXT_VIEW (data);
1487
1488   if (!text_view->first_validate_idle)
1489     text_view->first_validate_idle = g_idle_add_full (GTK_PRIORITY_RESIZE - 1, first_validate_callback, text_view, NULL);
1490
1491   if (!text_view->incremental_validate_idle)
1492     text_view->incremental_validate_idle = g_idle_add_full (GDK_PRIORITY_REDRAW + 1, incremental_validate_callback, text_view, NULL);
1493 }
1494
1495 static void
1496 changed_handler (GtkTextLayout *layout,
1497                  gint           start_y,
1498                  gint           old_height,
1499                  gint           new_height,
1500                  gpointer       data)
1501 {
1502   GtkTextView *text_view;
1503   GtkWidget *widget;
1504   GdkRectangle visible_rect;
1505   GdkRectangle redraw_rect;
1506   
1507   text_view = GTK_TEXT_VIEW (data);
1508   widget = GTK_WIDGET (data);
1509
1510   if (GTK_WIDGET_REALIZED (text_view))
1511     {
1512       gtk_text_view_get_visible_rect (text_view, &visible_rect);
1513
1514       redraw_rect.x = visible_rect.x;
1515       redraw_rect.width = visible_rect.width;
1516       redraw_rect.y = start_y;
1517
1518       if (old_height == new_height)
1519         redraw_rect.height = old_height;
1520       else
1521         redraw_rect.height = MAX (0, visible_rect.y + visible_rect.height - start_y);
1522       
1523       if (gdk_rectangle_intersect (&redraw_rect, &visible_rect, &redraw_rect))
1524         {
1525           redraw_rect.y -= text_view->yoffset;
1526           text_window_invalidate_rect (text_view->text_window,
1527                                        &redraw_rect);
1528         }
1529     }
1530
1531   if (old_height != new_height)
1532     {
1533       gboolean yoffset_changed = FALSE;
1534
1535       if (start_y + old_height <= text_view->yoffset - text_view->first_para_pixels)
1536         {
1537           text_view->yoffset += new_height - old_height;
1538           get_vadjustment (text_view)->value = text_view->yoffset;
1539           yoffset_changed = TRUE;
1540         }
1541
1542       gtk_text_view_scroll_calc_now (text_view);
1543
1544       if (yoffset_changed)
1545         gtk_adjustment_value_changed (get_vadjustment (text_view));
1546     }
1547 }
1548
1549 static void
1550 gtk_text_view_realize (GtkWidget *widget)
1551 {
1552   GtkTextView *text_view;
1553   GdkWindowAttr attributes;
1554   gint attributes_mask;
1555   
1556   text_view = GTK_TEXT_VIEW (widget);
1557   GTK_WIDGET_SET_FLAGS (text_view, GTK_REALIZED);
1558   
1559   attributes.window_type = GDK_WINDOW_CHILD;
1560   attributes.x = widget->allocation.x;
1561   attributes.y = widget->allocation.y;
1562   attributes.width = widget->allocation.width;
1563   attributes.height = widget->allocation.height;
1564   attributes.wclass = GDK_INPUT_OUTPUT;
1565   attributes.visual = gtk_widget_get_visual (widget);
1566   attributes.colormap = gtk_widget_get_colormap (widget);
1567   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK;
1568
1569   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1570
1571   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1572                                    &attributes, attributes_mask);
1573   gdk_window_set_user_data (widget->window, widget);
1574
1575   /* must come before text_window_realize calls */
1576   widget->style = gtk_style_attach (widget->style, widget->window);
1577
1578   gdk_window_set_background (widget->window,
1579                              &widget->style->bg[GTK_WIDGET_STATE (widget)]);
1580   
1581   text_window_realize (text_view->text_window, widget->window);
1582
1583   if (text_view->left_window)
1584     text_window_realize (text_view->left_window,
1585                          widget->window);
1586
1587   if (text_view->top_window)
1588     text_window_realize (text_view->top_window,
1589                          widget->window);
1590
1591   if (text_view->right_window)
1592     text_window_realize (text_view->right_window,
1593                          widget->window);
1594   
1595   if (text_view->bottom_window)
1596     text_window_realize (text_view->bottom_window,
1597                          widget->window);
1598   
1599   gtk_text_view_ensure_layout (text_view);
1600 }
1601
1602 static void
1603 gtk_text_view_unrealize (GtkWidget *widget)
1604 {
1605   GtkTextView *text_view;
1606
1607   text_view = GTK_TEXT_VIEW (widget);
1608
1609   if (text_view->first_validate_idle)
1610     {
1611       g_source_remove (text_view->first_validate_idle);
1612       text_view->first_validate_idle = 0;
1613     }
1614     
1615   if (text_view->incremental_validate_idle)
1616     {
1617       g_source_remove (text_view->incremental_validate_idle);
1618       text_view->incremental_validate_idle = 0;
1619     }
1620
1621   text_window_unrealize (text_view->text_window);
1622
1623   if (text_view->left_window)
1624     text_window_unrealize (text_view->left_window);
1625
1626   if (text_view->top_window)
1627     text_window_unrealize (text_view->top_window);
1628
1629   if (text_view->right_window)
1630     text_window_unrealize (text_view->right_window);
1631   
1632   if (text_view->bottom_window)
1633     text_window_unrealize (text_view->bottom_window);
1634   
1635   gtk_text_view_destroy_layout (text_view);
1636   
1637   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1638 }
1639
1640 static void 
1641 gtk_text_view_style_set (GtkWidget *widget,
1642                          GtkStyle  *previous_style)
1643 {
1644   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
1645
1646   if (GTK_WIDGET_REALIZED (widget))
1647     {
1648       gdk_window_set_background (widget->window,
1649                                  &widget->style->bg[GTK_WIDGET_STATE (widget)]);
1650       
1651       gdk_window_set_background (text_view->text_window->bin_window,
1652                                  &widget->style->base[GTK_WIDGET_STATE (widget)]);
1653
1654       if (text_view->left_window)
1655         gdk_window_set_background (text_view->left_window->bin_window,
1656                                    &widget->style->bg[GTK_WIDGET_STATE (widget)]);
1657       if (text_view->right_window)
1658         gdk_window_set_background (text_view->right_window->bin_window,
1659                                    &widget->style->bg[GTK_WIDGET_STATE (widget)]);
1660
1661       if (text_view->top_window)
1662         gdk_window_set_background (text_view->top_window->bin_window,
1663                                    &widget->style->bg[GTK_WIDGET_STATE (widget)]);      
1664
1665       if (text_view->bottom_window)
1666         gdk_window_set_background (text_view->bottom_window->bin_window,
1667                                    &widget->style->bg[GTK_WIDGET_STATE (widget)]);
1668       
1669       gtk_text_view_set_attributes_from_style (text_view,
1670                                                text_view->layout->default_style,
1671                                                widget->style);
1672       gtk_text_layout_default_style_changed (text_view->layout);
1673     }
1674 }
1675
1676 static void 
1677 gtk_text_view_direction_changed (GtkWidget        *widget,
1678                                  GtkTextDirection  previous_direction)
1679 {
1680   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
1681
1682   if (text_view->layout)
1683     {
1684       text_view->layout->default_style->direction = gtk_widget_get_direction (widget);
1685       gtk_text_layout_default_style_changed (text_view->layout);
1686     }
1687 }
1688
1689 /*
1690  * Events
1691  */
1692
1693 static gboolean
1694 get_event_coordinates (GdkEvent *event, gint *x, gint *y)
1695 {
1696   if (event)
1697     switch (event->type)
1698       {
1699       case GDK_MOTION_NOTIFY:
1700         *x = event->motion.x;
1701         *y = event->motion.y;
1702         return TRUE;
1703         break;
1704         
1705       case GDK_BUTTON_PRESS:
1706       case GDK_2BUTTON_PRESS:
1707       case GDK_3BUTTON_PRESS:
1708       case GDK_BUTTON_RELEASE:
1709         *x = event->button.x;
1710         *y = event->button.y;
1711         return TRUE;
1712         break;
1713         
1714       case GDK_KEY_PRESS:
1715       case GDK_KEY_RELEASE:
1716       case GDK_ENTER_NOTIFY:
1717       case GDK_LEAVE_NOTIFY:
1718       case GDK_PROPERTY_NOTIFY:
1719       case GDK_SELECTION_CLEAR:
1720       case GDK_SELECTION_REQUEST:
1721       case GDK_SELECTION_NOTIFY:
1722       case GDK_PROXIMITY_IN:
1723       case GDK_PROXIMITY_OUT:
1724       case GDK_DRAG_ENTER:
1725       case GDK_DRAG_LEAVE:
1726       case GDK_DRAG_MOTION:
1727       case GDK_DRAG_STATUS:
1728       case GDK_DROP_START:
1729       case GDK_DROP_FINISHED:
1730       default:
1731         return FALSE;
1732         break;
1733       }
1734
1735   return FALSE;
1736 }
1737
1738 static gint
1739 emit_event_on_tags (GtkWidget   *widget,
1740                     GdkEvent    *event,
1741                     GtkTextIter *iter)
1742 {
1743   GSList *tags;
1744   GSList *tmp;
1745   gint retval = FALSE;
1746   GtkTextView *text_view;
1747
1748   text_view = GTK_TEXT_VIEW (widget);
1749   
1750   tags = gtk_text_buffer_get_tags (text_view->buffer, iter);
1751           
1752   tmp = tags;
1753   while (tmp != NULL)
1754     {
1755       GtkTextTag *tag = tmp->data;
1756
1757       if (gtk_text_tag_event (tag, GTK_OBJECT (widget), event, iter))
1758         {
1759           retval = TRUE;
1760           break;
1761         }
1762
1763       tmp = g_slist_next (tmp);
1764     }
1765
1766   g_slist_free (tags);
1767
1768   return retval;
1769 }
1770      
1771 static gint
1772 gtk_text_view_event (GtkWidget *widget, GdkEvent *event)
1773 {
1774   GtkTextView *text_view;
1775   gint x = 0, y = 0;
1776   
1777   text_view = GTK_TEXT_VIEW (widget);
1778   
1779   if (text_view->layout == NULL ||
1780       text_view->buffer == NULL)
1781     return FALSE;
1782
1783   if (event->any.window != text_view->text_window->bin_window)
1784     return FALSE;
1785   
1786   if (get_event_coordinates (event, &x, &y))
1787     {
1788       GtkTextIter iter;
1789
1790       x += text_view->xoffset;
1791       y += text_view->yoffset;
1792
1793       /* FIXME this is slow and we do it twice per event.
1794          My favorite solution is to have GtkTextLayout cache
1795          the last couple lookups. */
1796       gtk_text_layout_get_iter_at_pixel (text_view->layout,
1797                                          &iter,
1798                                          x, y);
1799
1800       return emit_event_on_tags (widget, event, &iter);
1801     }
1802   else if (event->type == GDK_KEY_PRESS ||
1803            event->type == GDK_KEY_RELEASE)
1804     {
1805       GtkTextMark *insert;
1806       GtkTextIter iter;
1807
1808       insert = gtk_text_buffer_get_mark (text_view->buffer,
1809                                          "insert");      
1810
1811       gtk_text_buffer_get_iter_at_mark (text_view->buffer, &iter, insert);
1812
1813       return emit_event_on_tags (widget, event, &iter);
1814     }
1815   else
1816     return FALSE;
1817 }
1818
1819 static gint
1820 gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
1821 {
1822   GtkTextView *text_view;
1823
1824   text_view = GTK_TEXT_VIEW (widget);
1825
1826   if (text_view->layout == NULL ||
1827       text_view->buffer == NULL)
1828     return FALSE;
1829
1830   if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
1831       GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1832     return TRUE;
1833
1834   if (event->window != text_view->text_window->bin_window)
1835     return FALSE;
1836   
1837   if (gtk_im_context_filter_keypress (text_view->im_context, event))
1838     return TRUE;
1839   else if (event->keyval == GDK_Return)
1840     {
1841       gtk_text_buffer_insert_interactive_at_cursor (text_view->buffer, "\n", 1,
1842                                                     text_view->editable);
1843       gtk_text_view_scroll_to_mark (text_view,
1844                                     gtk_text_buffer_get_mark (text_view->buffer,
1845                                                               "insert"),
1846                                     0);
1847       return TRUE;
1848     }
1849   /* Pass through Tab as literal tab, unless Control is held down */
1850   else if (event->keyval == GDK_Tab && !(event->state & GDK_CONTROL_MASK))
1851     {
1852       gtk_text_buffer_insert_interactive_at_cursor (text_view->buffer, "\t", 1,
1853                                                     text_view->editable);
1854       gtk_text_view_scroll_to_mark (text_view,
1855                                     gtk_text_buffer_get_mark (text_view->buffer,
1856                                                               "insert"),
1857                                     0);
1858       return TRUE;
1859     }
1860   else
1861     return FALSE;
1862 }
1863
1864 static gint
1865 gtk_text_view_key_release_event (GtkWidget *widget, GdkEventKey *event)
1866 {
1867   return FALSE;
1868 }
1869
1870 static gint
1871 gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
1872 {
1873   GtkTextView *text_view;
1874
1875   text_view = GTK_TEXT_VIEW (widget);
1876   
1877   gtk_widget_grab_focus (widget);
1878
1879   if (event->window != text_view->text_window->bin_window)
1880     {
1881       /* Remove selection if any. */
1882       gtk_text_view_unselect (text_view);
1883       return FALSE;
1884     }
1885   
1886   /* debug hack */
1887   if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0)
1888     _gtk_text_buffer_spew (GTK_TEXT_VIEW (widget)->buffer);
1889   else if (event->button == 3)
1890     gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout);
1891
1892   if (event->type == GDK_BUTTON_PRESS)
1893     {
1894       if (event->button == 1)
1895         {
1896           /* If we're in the selection, start a drag copy/move of the
1897            * selection; otherwise, start creating a new selection.
1898            */
1899           GtkTextIter iter;
1900           GtkTextIter start, end;
1901
1902           gtk_text_layout_get_iter_at_pixel (text_view->layout,
1903                                              &iter,
1904                                              event->x + text_view->xoffset,
1905                                              event->y + text_view->yoffset);
1906           
1907           if (gtk_text_buffer_get_selection_bounds (text_view->buffer,
1908                                                     &start, &end) &&
1909               gtk_text_iter_in_range (&iter, &start, &end))
1910             {
1911               text_view->drag_start_x = event->x;
1912               text_view->drag_start_y = event->y;
1913             }
1914           else
1915             {
1916               gtk_text_view_start_selection_drag (text_view, &iter, event);
1917             }
1918           
1919           return TRUE;
1920         }
1921       else if (event->button == 2)
1922         {
1923           GtkTextIter iter;
1924
1925           gtk_text_layout_get_iter_at_pixel (text_view->layout,
1926                                              &iter,
1927                                              event->x + text_view->xoffset,
1928                                              event->y + text_view->yoffset);
1929           
1930           gtk_text_buffer_paste_primary (text_view->buffer,
1931                                          &iter,
1932                                          text_view->editable);
1933           return TRUE;
1934         }
1935       else if (event->button == 3)
1936         {
1937           if (gtk_text_view_end_selection_drag (text_view, event))
1938             return TRUE;
1939           else
1940             return FALSE;
1941         }
1942     }
1943   
1944   return FALSE;
1945 }
1946
1947 static gint
1948 gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
1949 {
1950   GtkTextView *text_view;
1951
1952   text_view = GTK_TEXT_VIEW (widget);
1953   
1954   if (event->window != text_view->text_window->bin_window)
1955     return FALSE;
1956   
1957   if (event->button == 1)
1958     {
1959       if (text_view->drag_start_x >= 0)
1960         {
1961           text_view->drag_start_x = -1;
1962           text_view->drag_start_y = -1;
1963         }
1964
1965       if (gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget), event))
1966         return TRUE;
1967       else
1968         {
1969           /* Unselect everything; probably we were dragging, or clicked
1970            * outside the text.
1971            */
1972           gtk_text_view_unselect (text_view);
1973           return FALSE;
1974         }
1975     }
1976
1977   return FALSE;
1978 }
1979
1980 static gint
1981 gtk_text_view_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
1982 {
1983   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
1984   
1985   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1986   gtk_widget_draw_focus (widget);
1987   
1988   if (text_view->cursor_visible && text_view->layout)
1989     {
1990       gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);
1991       gtk_text_view_start_cursor_blink (text_view);
1992     }
1993
1994   gtk_im_context_focus_in (GTK_TEXT_VIEW (widget)->im_context);
1995   
1996   return FALSE;
1997 }
1998
1999 static gint
2000 gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
2001 {
2002   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
2003   
2004   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2005   gtk_widget_draw_focus (widget);
2006   
2007   if (text_view->cursor_visible && text_view->layout)
2008     {
2009       gtk_text_layout_set_cursor_visible (text_view->layout, FALSE);
2010       gtk_text_view_stop_cursor_blink (text_view);
2011     }
2012
2013   gtk_im_context_focus_out (GTK_TEXT_VIEW (widget)->im_context);
2014   
2015   return FALSE;
2016 }
2017
2018 static gint
2019 gtk_text_view_motion_event (GtkWidget *widget, GdkEventMotion *event)
2020 {
2021   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
2022   
2023   if (event->window == text_view->text_window->bin_window &&
2024       text_view->drag_start_x >= 0)
2025     {
2026       gint x, y;
2027       gint dx, dy;
2028       
2029       gdk_window_get_pointer (text_view->text_window->bin_window,
2030                               &x, &y, NULL);
2031       
2032       dx = text_view->drag_start_x - x;
2033       dy = text_view->drag_start_y - y;
2034
2035       if (ABS (dx) > DRAG_THRESHOLD ||
2036           ABS (dy) > DRAG_THRESHOLD)
2037         {
2038           GtkTextIter iter;
2039           gint buffer_x, buffer_y;
2040
2041           gtk_text_view_window_to_buffer_coords (text_view,
2042                                                  GTK_TEXT_WINDOW_TEXT,
2043                                                  text_view->drag_start_x,
2044                                                  text_view->drag_start_y,
2045                                                  &buffer_x,
2046                                                  &buffer_y);
2047           
2048           gtk_text_layout_get_iter_at_pixel (text_view->layout,
2049                                              &iter,
2050                                              buffer_x, buffer_y);
2051           
2052           gtk_text_view_start_selection_dnd (text_view, &iter, event);
2053           return TRUE;
2054         }
2055     }
2056   
2057   return FALSE;
2058 }
2059
2060 static void
2061 gtk_text_view_paint (GtkWidget *widget, GdkRectangle *area)
2062 {
2063   GtkTextView *text_view;
2064   
2065   text_view = GTK_TEXT_VIEW (widget);
2066
2067   g_return_if_fail (text_view->layout != NULL);
2068   g_return_if_fail (text_view->xoffset >= 0);
2069   g_return_if_fail (text_view->yoffset >= 0);
2070   
2071   gtk_text_view_validate_onscreen (text_view);
2072
2073 #if 0
2074   printf ("painting %d,%d  %d x %d\n",
2075          area->x, area->y,
2076          area->width, area->height);
2077 #endif
2078   
2079   gtk_text_layout_draw (text_view->layout,
2080                         widget,
2081                         text_view->text_window->bin_window,
2082                         text_view->xoffset,
2083                         text_view->yoffset,
2084                         area->x, area->y,
2085                         area->width, area->height);
2086 }
2087
2088 static void
2089 send_expose (GtkTextView   *text_view,
2090              GtkTextWindow *win,
2091              GdkRectangle  *area)
2092 {
2093   GdkEventExpose event;
2094   
2095   event.type = GDK_EXPOSE;
2096   event.send_event = TRUE;
2097   event.window = win->bin_window;
2098   event.area = *area;
2099   event.count = 0;
2100
2101   /* Fix coordinates (convert widget coords to window coords) */
2102   gtk_text_view_window_to_buffer_coords (text_view,
2103                                          GTK_TEXT_WINDOW_WIDGET,
2104                                          event.area.x,
2105                                          event.area.y,
2106                                          &event.area.x,
2107                                          &event.area.y);
2108   
2109   gtk_text_view_buffer_to_window_coords (text_view,
2110                                          win->type,
2111                                          event.area.x,
2112                                          event.area.y,
2113                                          &event.area.x,
2114                                          &event.area.y);
2115
2116       
2117   gdk_window_ref (event.window);
2118   gtk_widget_event (GTK_WIDGET (text_view), (GdkEvent*) &event);
2119   gdk_window_unref (event.window);
2120 }
2121
2122 static void
2123 gtk_text_view_draw (GtkWidget *widget, GdkRectangle *area)
2124 {
2125   GdkRectangle intersection;
2126   GtkTextView *text_view;
2127
2128   text_view = GTK_TEXT_VIEW (widget);
2129   
2130   gtk_text_view_paint (widget, area);
2131
2132   /* If the area overlaps the "edge" of the widget, draw the focus
2133    * rectangle
2134    */
2135   if (area->x < FOCUS_EDGE_WIDTH ||
2136       area->y < FOCUS_EDGE_WIDTH ||
2137       (area->x + area->width) > (widget->allocation.width - FOCUS_EDGE_WIDTH) ||
2138       (area->y + area->height) > (widget->allocation.height - FOCUS_EDGE_WIDTH))
2139     gtk_widget_draw_focus (widget);
2140
2141   /* Synthesize expose events for the user-drawn border windows,
2142    * just as we would for a drawing area.
2143    */
2144
2145   if (text_view->left_window &&
2146       gdk_rectangle_intersect (area, &text_view->left_window->allocation,
2147                                &intersection))
2148     send_expose (text_view, text_view->left_window, &intersection);
2149   
2150   if (text_view->right_window &&
2151       gdk_rectangle_intersect (area, &text_view->right_window->allocation,
2152                                &intersection))
2153     send_expose (text_view, text_view->right_window, &intersection);
2154
2155   if (text_view->top_window &&
2156       gdk_rectangle_intersect (area, &text_view->top_window->allocation,
2157                                &intersection))
2158     send_expose (text_view, text_view->top_window, &intersection);
2159   
2160   if (text_view->bottom_window &&
2161       gdk_rectangle_intersect (area, &text_view->bottom_window->allocation,
2162                                &intersection))
2163     send_expose (text_view, text_view->bottom_window, &intersection);
2164 }
2165
2166 static gint
2167 gtk_text_view_expose_event (GtkWidget *widget, GdkEventExpose *event)
2168 {
2169   if (event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (widget),
2170                                                  GTK_TEXT_WINDOW_TEXT))
2171     gtk_text_view_paint (widget, &event->area);
2172
2173   if (event->window == widget->window)
2174     gtk_widget_draw_focus (widget);
2175   
2176   return TRUE;
2177 }
2178
2179 static void
2180 gtk_text_view_draw_focus (GtkWidget *widget)
2181 {
2182   if (GTK_WIDGET_DRAWABLE (widget))
2183     {
2184       if (GTK_WIDGET_HAS_FOCUS (widget))
2185         {
2186            gtk_paint_focus (widget->style, widget->window, 
2187                             NULL, widget, "textview",
2188                             0, 0,
2189                             widget->allocation.width - 1,
2190                             widget->allocation.height - 1);
2191         }
2192       else
2193         {
2194           gdk_window_clear (widget->window);
2195         }
2196     }
2197 }
2198
2199 /*
2200  * Container
2201  */
2202
2203 static void
2204 gtk_text_view_add (GtkContainer *container,
2205                    GtkWidget    *child)
2206 {
2207   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
2208   g_return_if_fail (GTK_IS_WIDGET (child));
2209
2210   /* This is pretty random. */
2211   gtk_text_view_add_child_in_window (GTK_TEXT_VIEW (container),
2212                                      child,
2213                                      GTK_TEXT_WINDOW_WIDGET,
2214                                      0, 0);
2215 }
2216
2217 static void
2218 gtk_text_view_remove (GtkContainer *container,
2219                       GtkWidget    *child)
2220 {
2221   GSList *iter;
2222   GtkTextView *text_view;
2223   GtkTextViewChild *vc;
2224   
2225   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
2226   g_return_if_fail (GTK_IS_WIDGET (child));
2227   g_return_if_fail (child->parent == (GtkWidget*) container);
2228   
2229   text_view = GTK_TEXT_VIEW (container);
2230
2231   vc = NULL;
2232   iter = text_view->children;
2233
2234   while (iter != NULL)
2235     {
2236       vc = iter->data;
2237
2238       if (vc->widget == child)
2239         break;
2240
2241       iter = g_slist_next (iter);
2242     }
2243
2244   g_assert (iter != NULL); /* be sure we had the child in the list */
2245   
2246   text_view->children = g_slist_remove (text_view->children, vc);
2247
2248   gtk_widget_unparent (vc->widget);
2249   
2250   text_view_child_free (vc);
2251 }
2252
2253 static void
2254 gtk_text_view_forall (GtkContainer *container,
2255                       gboolean      include_internals,
2256                       GtkCallback   callback,
2257                       gpointer      callback_data)
2258 {
2259   GSList *iter;
2260   GtkTextView *text_view;
2261
2262   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
2263   g_return_if_fail (callback != NULL);
2264
2265   text_view = GTK_TEXT_VIEW (container);
2266   
2267   iter = text_view->children;
2268
2269   while (iter != NULL)
2270     {
2271       GtkTextViewChild *vc = iter->data;
2272
2273       (* callback) (vc->widget, callback_data);
2274       
2275       iter = g_slist_next (iter);
2276     }
2277 }
2278
2279 /*
2280  * Blink!
2281  */
2282
2283 static gint
2284 blink_cb (gpointer data)
2285 {
2286   GtkTextView *text_view;
2287   
2288   text_view = GTK_TEXT_VIEW (data);
2289
2290   g_assert (text_view->layout && GTK_WIDGET_HAS_FOCUS (text_view) && text_view->cursor_visible);
2291
2292   gtk_text_layout_set_cursor_visible (text_view->layout,
2293                                       !gtk_text_layout_get_cursor_visible (text_view->layout));
2294   return TRUE;
2295 }
2296
2297 static void
2298 gtk_text_view_start_cursor_blink (GtkTextView *text_view)
2299 {
2300   if (text_view->blink_timeout != 0)
2301     return;
2302
2303   text_view->blink_timeout = gtk_timeout_add (500, blink_cb, text_view);
2304 }
2305
2306 static void
2307 gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
2308 {
2309   if (text_view->blink_timeout == 0)
2310     return;
2311
2312   gtk_timeout_remove (text_view->blink_timeout);
2313   text_view->blink_timeout = 0;
2314 }
2315
2316 /*
2317  * Key binding handlers
2318  */
2319
2320 static void
2321 gtk_text_view_move_iter_by_lines (GtkTextView *text_view,
2322                                   GtkTextIter *newplace,
2323                                   gint         count)
2324 {
2325   while (count < 0)
2326     {
2327       gtk_text_layout_move_iter_to_previous_line (text_view->layout, newplace);
2328       count++;
2329     }
2330
2331   while (count > 0)
2332     {
2333       gtk_text_layout_move_iter_to_next_line (text_view->layout, newplace);
2334       count--;
2335     }
2336 }
2337
2338 static void
2339 gtk_text_view_move (GtkTextView     *text_view,
2340                     GtkMovementStep  step,
2341                     gint             count,
2342                     gboolean         extend_selection)
2343 {
2344   GtkTextIter insert;
2345   GtkTextIter newplace;
2346   
2347   gint cursor_x_pos = 0;
2348
2349   if (step == GTK_MOVEMENT_PAGES)
2350     {
2351       gtk_text_view_scroll_pages (text_view, count);
2352       return;
2353     }
2354   
2355   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
2356                                     gtk_text_buffer_get_mark (text_view->buffer,
2357                                                               "insert"));
2358   newplace = insert;
2359
2360   if (step == GTK_MOVEMENT_DISPLAY_LINES)
2361     gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, NULL);
2362
2363   switch (step)
2364     {
2365     case GTK_MOVEMENT_CHARS:
2366       gtk_text_iter_forward_chars (&newplace, count);
2367       break;
2368
2369     case GTK_MOVEMENT_POSITIONS:
2370       gtk_text_layout_move_iter_visually (text_view->layout,
2371                                           &newplace, count);
2372       break;
2373
2374     case GTK_MOVEMENT_WORDS:
2375       if (count < 0)
2376         gtk_text_iter_backward_word_starts (&newplace, -count);
2377       else if (count > 0)
2378         gtk_text_iter_forward_word_ends (&newplace, count);
2379       break;
2380
2381     case GTK_MOVEMENT_DISPLAY_LINES:
2382       gtk_text_view_move_iter_by_lines (text_view, &newplace, count);
2383       gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
2384       break;
2385       
2386     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
2387       if (count > 1)
2388         gtk_text_view_move_iter_by_lines (text_view, &newplace, --count);
2389       else if (count < -1)
2390         gtk_text_view_move_iter_by_lines (text_view, &newplace, ++count);
2391
2392       if (count != 0)
2393         gtk_text_layout_move_iter_to_line_end (text_view->layout, &newplace, count);
2394       break;
2395       
2396     case GTK_MOVEMENT_PARAGRAPHS:
2397       /* This should almost certainly instead be doing the parallel thing to WORD */
2398       /*       gtk_text_iter_down_lines (&newplace, count); */
2399       /* FIXME */
2400       break;
2401       
2402     case GTK_MOVEMENT_PARAGRAPH_ENDS:
2403       if (count > 0)
2404         gtk_text_iter_forward_to_newline (&newplace);
2405       else if (count < 0)
2406         gtk_text_iter_set_line_offset (&newplace, 0);
2407       break;
2408       
2409     case GTK_MOVEMENT_BUFFER_ENDS:
2410       if (count > 0)
2411         gtk_text_buffer_get_last_iter (text_view->buffer, &newplace);
2412       else if (count < 0)
2413         gtk_text_buffer_get_iter_at_offset (text_view->buffer, &newplace, 0);
2414       break;
2415       
2416     default:
2417       break;
2418     }
2419   
2420   if (!gtk_text_iter_equal (&insert, &newplace))
2421     {
2422       if (extend_selection)
2423         gtk_text_buffer_move_mark (text_view->buffer,
2424                                    gtk_text_buffer_get_mark (text_view->buffer,
2425                                                              "insert"),
2426                                    &newplace);
2427       else
2428         gtk_text_buffer_place_cursor (text_view->buffer, &newplace);
2429       
2430       gtk_text_view_scroll_to_mark (text_view,
2431                                     gtk_text_buffer_get_mark (text_view->buffer,
2432                                                               "insert"), 0);
2433
2434       if (step == GTK_MOVEMENT_DISPLAY_LINES)
2435         {
2436           gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
2437         }
2438     }
2439 }
2440
2441 static void
2442 gtk_text_view_set_anchor (GtkTextView *text_view)
2443 {
2444   GtkTextIter insert;
2445   
2446   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
2447                                     gtk_text_buffer_get_mark (text_view->buffer,
2448                                                               "insert"));
2449
2450   gtk_text_buffer_create_mark (text_view->buffer, "anchor", &insert, TRUE);
2451 }
2452
2453 static void
2454 gtk_text_view_scroll_pages (GtkTextView *text_view,
2455                             gint         count)
2456 {
2457   gfloat newval;
2458   GtkAdjustment *adj;
2459   gint cursor_x_pos, cursor_y_pos;
2460   GtkTextIter new_insert;
2461   GtkTextIter anchor;
2462   gint y0, y1;
2463   
2464   g_return_if_fail (text_view->vadjustment != NULL);
2465
2466   adj = text_view->vadjustment;
2467
2468   /* Validate the region that will be brought into view by the cursor motion
2469    */
2470   if (count < 0)
2471     {
2472       gtk_text_view_get_first_para_iter (text_view, &anchor);
2473       y0 = adj->page_size;
2474       y1 = adj->page_size + count * adj->page_increment;
2475     }
2476   else
2477     {
2478       gtk_text_view_get_first_para_iter (text_view, &anchor);
2479       y0 = count * adj->page_increment + adj->page_size;
2480       y1 = 0;
2481     }
2482   
2483   gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1);
2484
2485   gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
2486
2487   newval = adj->value;
2488
2489   newval += count * adj->page_increment;
2490
2491   cursor_y_pos += newval - adj->value;
2492   set_adjustment_clamped (adj, newval);
2493
2494   gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos);
2495   clamp_iter_onscreen (text_view, &new_insert);
2496   gtk_text_buffer_place_cursor (text_view->buffer, &new_insert);
2497
2498   gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
2499
2500   /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
2501    * only guarantees 1 pixel onscreen.
2502    */
2503   gtk_text_view_scroll_to_mark (text_view,
2504                                 gtk_text_buffer_get_mark (text_view->buffer,
2505                                                           "insert"),
2506                                 0);
2507 }
2508
2509 static gboolean
2510 whitespace (gunichar ch, gpointer user_data)
2511 {
2512   return (ch == ' ' || ch == '\t');
2513 }
2514
2515 static gboolean
2516 not_whitespace (gunichar ch, gpointer user_data)
2517 {
2518   return !whitespace (ch, user_data);
2519 }
2520
2521 static gboolean
2522 find_whitepace_region (const GtkTextIter *center,
2523                       GtkTextIter *start, GtkTextIter *end)
2524 {
2525   *start = *center;
2526   *end = *center;
2527
2528   if (gtk_text_iter_backward_find_char (start, not_whitespace, NULL))
2529     gtk_text_iter_next_char (start); /* we want the first whitespace... */
2530   if (whitespace (gtk_text_iter_get_char (end), NULL))
2531     gtk_text_iter_forward_find_char (end, not_whitespace, NULL);
2532   
2533   return !gtk_text_iter_equal (start, end);
2534 }
2535
2536 static void
2537 gtk_text_view_insert (GtkTextView *text_view,
2538                       const gchar *str)
2539 {
2540   gtk_text_buffer_insert_interactive_at_cursor (text_view->buffer, str, -1,
2541                                                 text_view->editable);
2542 }
2543
2544 static void
2545 gtk_text_view_delete (GtkTextView   *text_view,
2546                            GtkDeleteType  type,
2547                            gint           count)
2548 {
2549   GtkTextIter insert;
2550   GtkTextIter start;
2551   GtkTextIter end;
2552   gboolean leave_one = FALSE;
2553   
2554   if (type == GTK_DELETE_CHARS)
2555     {
2556       /* Char delete deletes the selection, if one exists */
2557       if (gtk_text_buffer_delete_selection (text_view->buffer, TRUE,
2558                                             text_view->editable))
2559         return;
2560     }
2561   
2562   gtk_text_buffer_get_iter_at_mark (text_view->buffer,
2563                                     &insert,
2564                                     gtk_text_buffer_get_mark (text_view->buffer,
2565                                                               "insert"));
2566
2567   start = insert;
2568   end = insert;
2569   
2570   switch (type)
2571     {
2572     case GTK_DELETE_CHARS:
2573       gtk_text_iter_forward_chars (&end, count);
2574       break;
2575       
2576     case GTK_DELETE_WORD_ENDS:
2577       if (count > 0)
2578         gtk_text_iter_forward_word_ends (&end, count);
2579       else if (count < 0)
2580         gtk_text_iter_backward_word_starts (&start, 0 - count);
2581       break;
2582       
2583     case GTK_DELETE_WORDS:
2584       break;
2585       
2586     case GTK_DELETE_DISPLAY_LINE_ENDS:
2587       break;
2588
2589     case GTK_DELETE_DISPLAY_LINES:
2590       break;
2591
2592     case GTK_DELETE_PARAGRAPH_ENDS:
2593       /* If we're already at a newline, we need to
2594        * simply delete that newline, instead of
2595        * moving to the next one.
2596        */
2597       if (gtk_text_iter_get_char (&end) == '\n')
2598         {
2599           gtk_text_iter_next_char (&end);
2600           --count;
2601         }
2602
2603       while (count > 0)
2604         {
2605           if (!gtk_text_iter_forward_to_newline (&end))
2606             break;
2607
2608           --count;
2609         }
2610
2611       /* FIXME figure out what a negative count means
2612          and support that */
2613       break;
2614       
2615     case GTK_DELETE_PARAGRAPHS:
2616       if (count > 0)
2617         {
2618           gtk_text_iter_set_line_offset (&start, 0);
2619           gtk_text_iter_forward_to_newline (&end);
2620
2621           /* Do the lines beyond the first. */
2622           while (count > 1)
2623             {
2624               gtk_text_iter_forward_to_newline (&end);
2625               
2626               --count;
2627             }
2628         }
2629
2630       /* FIXME negative count? */
2631       
2632       break;
2633
2634     case GTK_DELETE_WHITESPACE:
2635       {
2636         find_whitepace_region (&insert, &start, &end);
2637       }
2638       break;
2639       
2640     default:
2641       break;
2642     }
2643
2644   if (!gtk_text_iter_equal (&start, &end))
2645     {
2646       if (gtk_text_buffer_delete_interactive (text_view->buffer, &start, &end,
2647                                               text_view->editable))
2648         {
2649           if (leave_one)
2650             gtk_text_buffer_insert_interactive_at_cursor (text_view->buffer,
2651                                                           " ", 1,
2652                                                           text_view->editable);
2653         }
2654       
2655       gtk_text_view_scroll_to_mark (text_view,
2656                                     gtk_text_buffer_get_mark (text_view->buffer, "insert"),
2657                                     0);
2658     }
2659 }
2660
2661 static void
2662 gtk_text_view_cut_clipboard (GtkTextView *text_view)
2663 {
2664   gtk_text_buffer_cut_clipboard (text_view->buffer, text_view->editable);
2665   gtk_text_view_scroll_to_mark (text_view,
2666                                 gtk_text_buffer_get_mark (text_view->buffer,
2667                                                           "insert"),
2668                                 0);
2669 }
2670
2671 static void
2672 gtk_text_view_copy_clipboard (GtkTextView *text_view)
2673 {
2674   gtk_text_buffer_copy_clipboard (text_view->buffer);
2675   gtk_text_view_scroll_to_mark (text_view,
2676                                 gtk_text_buffer_get_mark (text_view->buffer,
2677                                                           "insert"),
2678                                 0);
2679 }
2680
2681 static void
2682 gtk_text_view_paste_clipboard (GtkTextView *text_view)
2683 {
2684   gtk_text_buffer_paste_clipboard (text_view->buffer, text_view->editable);
2685   gtk_text_view_scroll_to_mark (text_view,
2686                                 gtk_text_buffer_get_mark (text_view->buffer,
2687                                                           "insert"),
2688                                 0);
2689 }
2690
2691 static void
2692 gtk_text_view_toggle_overwrite (GtkTextView *text_view)
2693 {
2694   text_view->overwrite_mode = !text_view->overwrite_mode;
2695 }
2696
2697 /*
2698  * Selections
2699  */
2700
2701 static void
2702 gtk_text_view_unselect (GtkTextView *text_view)
2703 {
2704   GtkTextIter insert;
2705
2706   gtk_text_buffer_get_iter_at_mark (text_view->buffer,
2707                                     &insert,
2708                                     gtk_text_buffer_get_mark (text_view->buffer,
2709                                                               "insert"));
2710
2711   gtk_text_buffer_move_mark (text_view->buffer,
2712                              gtk_text_buffer_get_mark (text_view->buffer,
2713                                                        "selection_bound"),
2714                              &insert);
2715 }
2716
2717 static gboolean
2718 move_insert_to_pointer_and_scroll (GtkTextView *text_view, gboolean partial_scroll)
2719 {
2720   gint x, y;
2721   GdkModifierType state;
2722   GtkTextIter newplace;
2723   gint adjust = 0;
2724   gboolean in_threshold = FALSE;
2725   
2726   gdk_window_get_pointer (text_view->text_window->bin_window,
2727                           &x, &y, &state);
2728
2729   /* Adjust movement by how long we've been selecting, to
2730      get an acceleration effect. The exact numbers are
2731      pretty arbitrary. We have a threshold before we
2732      start to accelerate. */
2733   /* uncommenting this printf helps visualize how it works. */     
2734   /*   printf ("%d\n", text_view->scrolling_accel_factor); */
2735        
2736   if (text_view->scrolling_accel_factor > 10)
2737     adjust = (text_view->scrolling_accel_factor - 10) * 75;
2738   
2739   if (y < 0) /* scrolling upward */
2740     adjust = -adjust; 
2741
2742   /* No adjust if the pointer has moved back inside the window for sure.
2743      Also I'm adding a small threshold where no adjust is added,
2744      in case you want to do a continuous slow scroll. */
2745 #define SLOW_SCROLL_TH 7
2746   if (x >= (0 - SLOW_SCROLL_TH) &&
2747       x < (SCREEN_WIDTH (text_view) + SLOW_SCROLL_TH) &&
2748       y >= (0 - SLOW_SCROLL_TH) &&
2749       y < (SCREEN_HEIGHT (text_view) + SLOW_SCROLL_TH))
2750     {
2751       adjust = 0;
2752       in_threshold = TRUE;
2753     }
2754   
2755   gtk_text_layout_get_iter_at_pixel (text_view->layout,
2756                                      &newplace,
2757                                      x + text_view->xoffset,
2758                                      y + text_view->yoffset + adjust);
2759   
2760   {
2761       gboolean scrolled = FALSE;
2762       GtkTextMark *insert_mark =
2763         gtk_text_buffer_get_mark (text_view->buffer, "insert");
2764
2765       gtk_text_buffer_move_mark (text_view->buffer,
2766                                  insert_mark,
2767                                  &newplace);
2768       
2769       if (partial_scroll)
2770         scrolled = gtk_text_view_scroll_to_mark_adjusted (text_view, insert_mark, 0, 0.7);
2771       else
2772         scrolled = gtk_text_view_scroll_to_mark_adjusted (text_view, insert_mark, 0, 1.0);
2773
2774       if (scrolled)
2775         {
2776           /* We want to avoid rapid jump to super-accelerated when you
2777              leave the slow scroll threshold after scrolling for a
2778              while. So we slowly decrease accel when scrolling inside
2779              the threshold.
2780           */
2781           if (in_threshold)
2782             {
2783               if (text_view->scrolling_accel_factor > 1)
2784                 text_view->scrolling_accel_factor -= 2;
2785             }
2786           else
2787             text_view->scrolling_accel_factor += 1;
2788         }
2789       else
2790         {
2791           /* If we don't scroll we're probably inside the window, but
2792              potentially just a bit outside. We decrease acceleration
2793              while the user is fooling around inside the window.
2794              Acceleration decreases faster than it increases. */
2795           if (text_view->scrolling_accel_factor > 4)
2796             text_view->scrolling_accel_factor -= 5;
2797         }
2798       
2799       return scrolled;
2800   }
2801 }
2802
2803 static gint
2804 selection_scan_timeout (gpointer data)
2805 {
2806   GtkTextView *text_view;
2807
2808   text_view = GTK_TEXT_VIEW (data);
2809
2810   if (move_insert_to_pointer_and_scroll (text_view, TRUE))
2811     {
2812       return TRUE; /* remain installed. */
2813     }
2814   else
2815     {
2816       text_view->selection_drag_scan_timeout = 0;
2817       return FALSE; /* remove ourselves */
2818     }
2819 }
2820
2821 static gint
2822 selection_motion_event_handler (GtkTextView *text_view, GdkEventMotion *event, gpointer data)
2823 {
2824   if (move_insert_to_pointer_and_scroll (text_view, TRUE))
2825     {
2826       /* If we had to scroll offscreen, insert a timeout to do so
2827          again. Note that in the timeout, even if the mouse doesn't
2828          move, due to this scroll xoffset/yoffset will have changed
2829          and we'll need to scroll again. */
2830       if (text_view->selection_drag_scan_timeout != 0) /* reset on every motion event */
2831         gtk_timeout_remove (text_view->selection_drag_scan_timeout);
2832       
2833       text_view->selection_drag_scan_timeout =
2834         gtk_timeout_add (50, selection_scan_timeout, text_view);
2835     }
2836   
2837   return TRUE;
2838 }
2839
2840 static void
2841 gtk_text_view_start_selection_drag (GtkTextView       *text_view,
2842                                     const GtkTextIter *iter,
2843                                     GdkEventButton    *button)
2844 {
2845   GtkTextIter newplace;
2846
2847   g_return_if_fail (text_view->selection_drag_handler == 0);
2848   
2849   gtk_grab_add (GTK_WIDGET (text_view));
2850
2851   text_view->scrolling_accel_factor = 0;
2852
2853   newplace = *iter;
2854   
2855   gtk_text_buffer_place_cursor (text_view->buffer, &newplace);
2856
2857   text_view->selection_drag_handler = gtk_signal_connect (GTK_OBJECT (text_view),
2858                                                           "motion_notify_event",
2859                                                           GTK_SIGNAL_FUNC (selection_motion_event_handler),
2860                                                           NULL);
2861 }
2862
2863 /* returns whether we were really dragging */
2864 static gboolean
2865 gtk_text_view_end_selection_drag (GtkTextView *text_view, GdkEventButton *event)
2866 {
2867   if (text_view->selection_drag_handler == 0)
2868     return FALSE;
2869
2870   gtk_signal_disconnect (GTK_OBJECT (text_view), text_view->selection_drag_handler);
2871   text_view->selection_drag_handler = 0;
2872
2873   text_view->scrolling_accel_factor = 0;
2874   
2875   if (text_view->selection_drag_scan_timeout != 0)
2876     {
2877       gtk_timeout_remove (text_view->selection_drag_scan_timeout);
2878       text_view->selection_drag_scan_timeout = 0;
2879     }
2880
2881   /* one last update to current position */
2882   move_insert_to_pointer_and_scroll (text_view, FALSE);
2883   
2884   gtk_grab_remove (GTK_WIDGET (text_view));
2885   
2886   return TRUE;
2887 }
2888
2889 /*
2890  * Layout utils
2891  */
2892
2893 static void
2894 gtk_text_view_set_adjustment_upper (GtkAdjustment *adj, gfloat upper)
2895 {
2896   if (upper != adj->upper)
2897     {
2898       gfloat min = MAX (0., upper - adj->page_size);
2899       gboolean value_changed = FALSE;
2900       
2901       adj->upper = upper;
2902       
2903       if (adj->value > min)
2904         {
2905           adj->value = min;
2906           value_changed = TRUE;
2907         }
2908       
2909       gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed");
2910       if (value_changed)
2911         gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
2912     }
2913 }
2914
2915 static void
2916 gtk_text_view_scroll_calc_now (GtkTextView *text_view)
2917 {
2918   gint width = 0, height = 0;
2919   
2920   gtk_text_view_ensure_layout (text_view);
2921
2922       
2923   gtk_text_layout_set_screen_width (text_view->layout,
2924                                     SCREEN_WIDTH (text_view));
2925       
2926   gtk_text_layout_get_size (text_view->layout, &width, &height);
2927
2928 #if 0
2929   /* If the width is less than the screen width (likely
2930      if we have wrapping turned on for the whole widget),
2931      then we want to set the scroll region to the screen
2932      width. If the width is greater (wrapping off) then we
2933      probably want to set the scroll region to the width
2934      of the layout. I guess.
2935   */
2936
2937   width = MAX (text_view->layout->screen_width, width);
2938   height = height;
2939 #endif
2940
2941   if (text_view->width != width || text_view->height != height)
2942     {
2943 #if 0
2944       printf ("layout size set, widget width is %d\n",
2945               GTK_WIDGET (text_view)->allocation.width);
2946 #endif
2947       text_view->width = width;
2948       text_view->height = height;
2949       
2950       gtk_text_view_set_adjustment_upper (get_hadjustment (text_view),
2951                                           MAX (SCREEN_WIDTH (text_view), width));
2952       gtk_text_view_set_adjustment_upper (get_vadjustment (text_view), 
2953                                           MAX (SCREEN_HEIGHT (text_view), height));
2954
2955       /* hadj/vadj exist since we called get_hadjustment/get_vadjustment above */
2956       
2957       /* Set up the step sizes; we'll say that a page is
2958          our allocation minus one step, and a step is
2959          1/10 of our allocation. */
2960       text_view->hadjustment->step_increment =
2961         SCREEN_WIDTH (text_view) / 10.0;
2962       text_view->hadjustment->page_increment =
2963         SCREEN_WIDTH (text_view) * 0.9;
2964
2965       text_view->vadjustment->step_increment =
2966         SCREEN_HEIGHT (text_view) / 10.0;
2967       text_view->vadjustment->page_increment =
2968         SCREEN_HEIGHT (text_view) * 0.9;
2969     } 
2970 }
2971
2972 static void
2973 gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
2974                                          GtkTextAttributes *values,
2975                                          GtkStyle           *style)
2976 {
2977   values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
2978   values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];
2979       
2980   if (values->font_desc)
2981     pango_font_description_free (values->font_desc);
2982
2983   values->font_desc = pango_font_description_copy (style->font_desc);
2984 }
2985
2986 static void
2987 gtk_text_view_ensure_layout (GtkTextView *text_view)
2988 {
2989   GtkWidget *widget;
2990
2991   widget = GTK_WIDGET (text_view);
2992   
2993   if (text_view->layout == NULL)
2994     {
2995       GtkTextAttributes *style;
2996       PangoContext *ltr_context, *rtl_context;
2997       
2998       text_view->layout = gtk_text_layout_new ();
2999
3000       gtk_signal_connect (GTK_OBJECT (text_view->layout),
3001                           "invalidated",
3002                           GTK_SIGNAL_FUNC (invalidated_handler),
3003                           text_view);
3004
3005       gtk_signal_connect (GTK_OBJECT (text_view->layout),
3006                           "changed",
3007                           GTK_SIGNAL_FUNC (changed_handler),
3008                           text_view);
3009       
3010       if (text_view->buffer)
3011         gtk_text_layout_set_buffer (text_view->layout, text_view->buffer);
3012
3013       if ((GTK_WIDGET_HAS_FOCUS (text_view) && text_view->cursor_visible))
3014         gtk_text_view_start_cursor_blink (text_view);
3015       else
3016         gtk_text_layout_set_cursor_visible (text_view->layout, FALSE);
3017       
3018       ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
3019       pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
3020       rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
3021       pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
3022
3023       gtk_text_layout_set_contexts (text_view->layout, ltr_context, rtl_context);
3024
3025       g_object_unref (G_OBJECT (ltr_context));
3026       g_object_unref (G_OBJECT (rtl_context));
3027
3028       style = gtk_text_attributes_new ();
3029
3030       gtk_widget_ensure_style (widget);
3031       gtk_text_view_set_attributes_from_style (text_view,
3032                                                style, widget->style);
3033       
3034       style->pixels_above_lines = 2;
3035       style->pixels_below_lines = 2;
3036       style->pixels_inside_wrap = 1;
3037       
3038       style->wrap_mode = text_view->wrap_mode;
3039       style->justify = GTK_JUSTIFY_LEFT;
3040       style->direction = gtk_widget_get_direction (GTK_WIDGET (text_view));
3041       
3042       gtk_text_layout_set_default_style (text_view->layout, style);
3043       
3044       gtk_text_attributes_unref (style);
3045     }
3046 }
3047
3048 static void
3049 gtk_text_view_destroy_layout (GtkTextView *text_view)
3050 {
3051   if (text_view->layout)
3052     {
3053       gtk_text_view_stop_cursor_blink (text_view);
3054       gtk_text_view_end_selection_drag (text_view, NULL);
3055       
3056       gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->layout),
3057                                      invalidated_handler, text_view);
3058       gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->layout),
3059                                      changed_handler, text_view);
3060       gtk_object_unref (GTK_OBJECT (text_view->layout));
3061       text_view->layout = NULL;
3062     }
3063 }
3064
3065
3066 /*
3067  * DND feature
3068  */
3069
3070 static void
3071 gtk_text_view_start_selection_dnd (GtkTextView       *text_view,
3072                                    const GtkTextIter *iter,
3073                                    GdkEventMotion    *event)
3074 {
3075   GdkDragContext *context;
3076   GtkTargetList *target_list;
3077
3078   text_view->drag_start_x = -1;
3079   text_view->drag_start_y = -1;
3080   
3081   target_list = gtk_target_list_new (target_table, G_N_ELEMENTS (target_table));
3082
3083   context = gtk_drag_begin (GTK_WIDGET (text_view), target_list,
3084                             GDK_ACTION_COPY | GDK_ACTION_MOVE,
3085                             1, (GdkEvent*)event);
3086
3087   gtk_target_list_unref (target_list);
3088
3089   gtk_drag_set_icon_default (context);
3090
3091   /* We're inside the selection, so start without being able
3092    * to accept the drag.
3093    */
3094   gdk_drag_status (context, 0, event->time);
3095   gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
3096 }
3097
3098 static void
3099 gtk_text_view_drag_begin (GtkWidget        *widget,
3100                           GdkDragContext   *context)
3101 {
3102   
3103 }
3104
3105 static void
3106 gtk_text_view_drag_end (GtkWidget        *widget,
3107                         GdkDragContext   *context)
3108 {
3109   GtkTextView *text_view;
3110
3111   text_view = GTK_TEXT_VIEW (widget);
3112
3113   gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
3114 }
3115
3116 static void
3117 gtk_text_view_drag_data_get (GtkWidget        *widget,
3118                              GdkDragContext   *context,
3119                              GtkSelectionData *selection_data,
3120                              guint             info,
3121                              guint             time)
3122 {
3123   gchar *str;
3124   gint length;
3125   GtkTextIter start;
3126   GtkTextIter end;
3127   GtkTextView *text_view;
3128
3129   text_view = GTK_TEXT_VIEW (widget);
3130   
3131   str = NULL;
3132   length = 0;
3133   
3134   if (gtk_text_buffer_get_selection_bounds (text_view->buffer, &start, &end))
3135     {
3136       /* Extract the selected text */
3137       str = gtk_text_iter_get_visible_text (&start, &end);
3138       
3139       length = strlen (str);
3140     }
3141
3142   if (str)
3143     {
3144       gtk_selection_data_set_text (selection_data, str);
3145       g_free (str);
3146     }
3147 }
3148
3149 static void
3150 gtk_text_view_drag_data_delete (GtkWidget        *widget,
3151                                 GdkDragContext   *context)
3152 {
3153   GtkTextView *text_view;
3154
3155   text_view = GTK_TEXT_VIEW (widget);
3156   
3157   gtk_text_buffer_delete_selection (GTK_TEXT_VIEW (widget)->buffer,
3158                                     TRUE, GTK_TEXT_VIEW (widget)->editable);
3159 }
3160
3161 static void
3162 gtk_text_view_drag_leave (GtkWidget        *widget,
3163                           GdkDragContext   *context,
3164                           guint             time)
3165 {
3166
3167
3168 }
3169
3170 static gboolean
3171 gtk_text_view_drag_motion (GtkWidget        *widget,
3172                            GdkDragContext   *context,
3173                            gint              x,
3174                            gint              y,
3175                            guint             time)
3176 {
3177   GtkTextIter newplace;
3178   GtkTextView *text_view;
3179   GtkTextIter start;
3180   GtkTextIter end;
3181   GdkRectangle target_rect;
3182   gint bx, by;
3183   
3184   text_view = GTK_TEXT_VIEW (widget);
3185
3186   target_rect = text_view->text_window->allocation;
3187
3188   if (x < target_rect.x ||
3189       y < target_rect.y ||
3190       x > (target_rect.x + target_rect.width) ||
3191       y > (target_rect.y + target_rect.height))
3192     return FALSE; /* outside the text window */
3193
3194   gtk_text_view_window_to_buffer_coords (text_view,
3195                                          GTK_TEXT_WINDOW_WIDGET,
3196                                          x, y,
3197                                          &bx, &by);
3198   
3199   gtk_text_layout_get_iter_at_pixel (text_view->layout,
3200                                      &newplace, 
3201                                      bx, by);
3202
3203   if (gtk_text_buffer_get_selection_bounds (text_view->buffer,
3204                                             &start, &end) &&
3205       gtk_text_iter_in_range (&newplace, &start, &end))
3206     {
3207       /* We're inside the selection. */
3208       gdk_drag_status (context, 0, time);
3209       gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
3210     }
3211   else
3212     {
3213       if (gtk_text_iter_editable (&newplace, text_view->editable))
3214         {
3215           gtk_text_mark_set_visible (text_view->dnd_mark,
3216                                      text_view->cursor_visible);
3217           
3218           gdk_drag_status (context, context->suggested_action, time);
3219         }
3220       else
3221         {
3222           /* Can't drop here. */
3223           gdk_drag_status (context, 0, time);
3224           gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
3225         }
3226     }
3227
3228   gtk_text_buffer_move_mark (text_view->buffer,
3229                              gtk_text_buffer_get_mark (text_view->buffer,
3230                                                        "gtk_drag_target"),
3231                              &newplace);
3232
3233   {
3234     /* The effect of this is that the text scrolls if you're near
3235        the edge. We have to scroll whether or not we're inside
3236        the selection. */
3237     gint margin;
3238     
3239     margin = MIN (SCREEN_WIDTH (widget), SCREEN_HEIGHT (widget));
3240     margin /= 5;
3241     
3242     gtk_text_view_scroll_to_mark_adjusted (text_view,
3243                                            gtk_text_buffer_get_mark (text_view->buffer,
3244                                                                      "gtk_drag_target"),
3245                                            margin, 1.0);
3246   }
3247   
3248   return TRUE;
3249 }
3250
3251 static gboolean
3252 gtk_text_view_drag_drop (GtkWidget        *widget,
3253                          GdkDragContext   *context,
3254                          gint              x,
3255                          gint              y,
3256                          guint             time)
3257 {
3258 #if 0
3259   /* called automatically. */
3260   if (context->targets)
3261     {
3262       gtk_drag_get_data (widget, context, 
3263                          GPOINTER_TO_INT (context->targets->data), 
3264                          time);
3265       return TRUE;
3266     }
3267   else
3268     return FALSE;
3269 #endif
3270   return TRUE;
3271 }
3272
3273 static void
3274 gtk_text_view_drag_data_received (GtkWidget        *widget,
3275                                   GdkDragContext   *context,
3276                                   gint              x,
3277                                   gint              y,
3278                                   GtkSelectionData *selection_data,
3279                                   guint             info,
3280                                   guint             time)
3281 {
3282   GtkTextIter drop_point;
3283   GtkTextView *text_view;
3284   GtkTextMark *drag_target_mark;
3285   gchar *str;
3286   
3287   text_view = GTK_TEXT_VIEW (widget);
3288   
3289   drag_target_mark = gtk_text_buffer_get_mark (text_view->buffer,
3290                                                "gtk_drag_target");
3291   
3292   if (drag_target_mark == NULL)
3293     return;
3294
3295   gtk_text_buffer_get_iter_at_mark (text_view->buffer,
3296                                     &drop_point,
3297                                     drag_target_mark);
3298
3299   str = gtk_selection_data_get_text (selection_data);
3300
3301   if (str)
3302     {
3303       gtk_text_buffer_insert_interactive (text_view->buffer,
3304                                           &drop_point, str, -1,
3305                                           text_view->editable);
3306       g_free (str);
3307     }
3308 }
3309
3310 static GtkAdjustment*
3311 get_hadjustment (GtkTextView *text_view)
3312 {
3313   if (text_view->hadjustment == NULL)
3314     gtk_text_view_set_scroll_adjustments (text_view,
3315                                           (GtkAdjustment*)
3316                                           gtk_adjustment_new (0.0, 0.0, 0.0,
3317                                                               0.0, 0.0, 0.0),
3318                                           text_view->vadjustment);
3319
3320   return text_view->hadjustment;
3321 }
3322
3323 static GtkAdjustment*
3324 get_vadjustment (GtkTextView *text_view)
3325 {
3326   if (text_view->vadjustment == NULL)
3327     gtk_text_view_set_scroll_adjustments (text_view,
3328                                           text_view->hadjustment,
3329                                           (GtkAdjustment*)
3330                                           gtk_adjustment_new (0.0, 0.0, 0.0,
3331                                                               0.0, 0.0, 0.0));
3332
3333   return text_view->vadjustment;
3334 }
3335
3336
3337 static void
3338 gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
3339                                       GtkAdjustment *hadj,
3340                                       GtkAdjustment *vadj)
3341 {
3342   gboolean need_adjust = FALSE;
3343
3344   g_return_if_fail (text_view != NULL);
3345   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3346
3347   if (hadj)
3348     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
3349   else
3350     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3351   if (vadj)
3352     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
3353   else
3354     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3355   
3356   if (text_view->hadjustment && (text_view->hadjustment != hadj))
3357     {
3358       gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->hadjustment), text_view);
3359       gtk_object_unref (GTK_OBJECT (text_view->hadjustment));
3360     }
3361   
3362   if (text_view->vadjustment && (text_view->vadjustment != vadj))
3363     {
3364       gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->vadjustment), text_view);
3365       gtk_object_unref (GTK_OBJECT (text_view->vadjustment));
3366     }
3367   
3368   if (text_view->hadjustment != hadj)
3369     {
3370       text_view->hadjustment = hadj;
3371       gtk_object_ref (GTK_OBJECT (text_view->hadjustment));
3372       gtk_object_sink (GTK_OBJECT (text_view->hadjustment));
3373       
3374       gtk_signal_connect (GTK_OBJECT (text_view->hadjustment), "value_changed",
3375                           (GtkSignalFunc) gtk_text_view_value_changed,
3376                           text_view);
3377       need_adjust = TRUE;
3378     }
3379   
3380   if (text_view->vadjustment != vadj)
3381     {
3382       text_view->vadjustment = vadj;
3383       gtk_object_ref (GTK_OBJECT (text_view->vadjustment));
3384       gtk_object_sink (GTK_OBJECT (text_view->vadjustment));
3385       
3386       gtk_signal_connect (GTK_OBJECT (text_view->vadjustment), "value_changed",
3387                           (GtkSignalFunc) gtk_text_view_value_changed,
3388                           text_view);
3389       need_adjust = TRUE;
3390     }
3391
3392   if (need_adjust)
3393     gtk_text_view_value_changed (NULL, text_view);
3394 }
3395
3396 static void
3397 gtk_text_view_value_changed (GtkAdjustment *adj,
3398                              GtkTextView   *text_view)
3399 {
3400   GtkTextIter iter;
3401   gint line_top;
3402   gint dx = 0;
3403   gint dy = 0;
3404
3405   if (adj == text_view->hadjustment)
3406     {
3407       dx = text_view->xoffset - (gint)adj->value;
3408       text_view->xoffset = adj->value;
3409     }
3410   else if (adj == text_view->vadjustment)
3411     {
3412       dy = text_view->yoffset - (gint)adj->value;
3413       text_view->yoffset = adj->value;
3414
3415       if (text_view->layout)
3416         {
3417           gtk_text_layout_get_line_at_y (text_view->layout, &iter, adj->value, &line_top);
3418           
3419           gtk_text_buffer_move_mark (text_view->buffer, text_view->first_para_mark, &iter);
3420           
3421           text_view->first_para_pixels = adj->value - line_top;
3422         }
3423     }
3424
3425   if (dx != 0 || dy != 0)
3426     {
3427       if (dy != 0)
3428         {
3429           if (text_view->left_window)
3430             text_window_scroll (text_view->left_window, 0, dy);
3431           if (text_view->right_window)
3432             text_window_scroll (text_view->right_window, 0, dy);
3433         }
3434
3435       if (dx != 0)
3436         {
3437           if (text_view->top_window)
3438             text_window_scroll (text_view->top_window, dx, 0);
3439           if (text_view->bottom_window)
3440             text_window_scroll (text_view->bottom_window, dx, 0);
3441         }
3442
3443       /* It looks nicer to scroll the main area last, because
3444        * it takes a while, and making the side areas update
3445        * afterward emphasizes the slowness of scrolling the
3446        * main area.
3447        */
3448       text_window_scroll (text_view->text_window, dx, dy);
3449     }
3450 }
3451
3452 static void
3453 gtk_text_view_commit_handler (GtkIMContext  *context,
3454                               const gchar   *str,
3455                               GtkTextView   *text_view)
3456 {
3457   gtk_text_buffer_delete_selection (text_view->buffer, TRUE,
3458                                     text_view->editable);
3459
3460   if (!strcmp (str, "\n"))
3461     {
3462       gtk_text_buffer_insert_interactive_at_cursor (text_view->buffer, "\n", 1,
3463                                                     text_view->editable);
3464     }
3465   else
3466     {
3467       if (text_view->overwrite_mode)
3468         gtk_text_view_delete (text_view, GTK_DELETE_CHARS, 1);
3469       gtk_text_buffer_insert_interactive_at_cursor (text_view->buffer, str, -1,
3470                                                     text_view->editable);
3471     }
3472   
3473   gtk_text_view_scroll_to_mark (text_view,
3474                                 gtk_text_buffer_get_mark (text_view->buffer,
3475                                                           "insert"),
3476                                 0);
3477 }
3478
3479 static void
3480 gtk_text_view_mark_set_handler (GtkTextBuffer     *buffer,
3481                                 const GtkTextIter *location,
3482                                 GtkTextMark       *mark,
3483                                 gpointer           data)
3484 {
3485   GtkTextView *text_view = GTK_TEXT_VIEW (data);
3486
3487   if (mark == gtk_text_buffer_get_insert (buffer))
3488     {
3489       text_view->virtual_cursor_x = -1;
3490       text_view->virtual_cursor_y = -1;
3491     }
3492 }
3493
3494 static void
3495 gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
3496                                       gint        *x,
3497                                       gint        *y)
3498 {
3499   GdkRectangle strong_pos;
3500   GtkTextIter insert;
3501
3502   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
3503                                     gtk_text_buffer_get_mark (text_view->buffer,
3504                                                               "insert"));
3505   
3506   if ((x && text_view->virtual_cursor_x == -1) ||
3507       (y && text_view->virtual_cursor_y == -1))
3508     gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
3509
3510   if (x)
3511     {
3512       if (text_view->virtual_cursor_x != -1)
3513         *x = text_view->virtual_cursor_x;
3514       else
3515         *x = strong_pos.x;
3516     }
3517
3518   if (y)
3519     {
3520       if (text_view->virtual_cursor_x != -1)
3521         *y = text_view->virtual_cursor_y;
3522       else
3523         *y = strong_pos.y + strong_pos.height / 2;
3524     }
3525 }
3526
3527 static void
3528 gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
3529                                       gint         x,
3530                                       gint         y)
3531 {
3532   GdkRectangle strong_pos;
3533   GtkTextIter insert;
3534
3535   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
3536                                     gtk_text_buffer_get_mark (text_view->buffer,
3537                                                               "insert"));
3538   
3539   if (x == -1 || y == -1)
3540     gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
3541
3542   text_view->virtual_cursor_x = (x == -1) ? strong_pos.x : x;
3543   text_view->virtual_cursor_y = (y == -1) ? strong_pos.y + strong_pos.height / 2 : y;
3544 }
3545
3546
3547
3548 /* Child GdkWindows */
3549
3550
3551 static GtkTextWindow*
3552 text_window_new (GtkTextWindowType  type,
3553                  GtkWidget         *widget,
3554                  gint               width_request,
3555                  gint               height_request)
3556 {
3557   GtkTextWindow *win;
3558
3559   win = g_new (GtkTextWindow, 1);
3560
3561   win->type = type;
3562   win->widget = widget;
3563   win->window = NULL;
3564   win->bin_window = NULL;
3565   win->requisition.width = width_request;
3566   win->requisition.height = height_request;
3567   win->allocation.width = width_request;
3568   win->allocation.height = height_request;
3569   win->allocation.x = 0;
3570   win->allocation.y = 0;
3571   
3572   return win;
3573 }
3574
3575 static void
3576 text_window_free (GtkTextWindow *win)
3577 {
3578   if (win->window)
3579     text_window_unrealize (win);
3580
3581   g_free (win);
3582 }
3583
3584 static void
3585 text_window_realize (GtkTextWindow *win,
3586                      GdkWindow     *parent)
3587 {
3588   GdkWindowAttr attributes;
3589   gint attributes_mask;
3590   GdkCursor *cursor;
3591   
3592   attributes.window_type = GDK_WINDOW_CHILD;
3593   attributes.x = win->allocation.x;
3594   attributes.y = win->allocation.y;
3595   attributes.width = win->allocation.width;
3596   attributes.height = win->allocation.height;
3597   attributes.wclass = GDK_INPUT_OUTPUT;
3598   attributes.visual = gtk_widget_get_visual (win->widget);
3599   attributes.colormap = gtk_widget_get_colormap (win->widget);
3600   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
3601
3602   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3603
3604   win->window = gdk_window_new (parent,
3605                                 &attributes,
3606                                 attributes_mask);
3607
3608   gdk_window_show (win->window);
3609   gdk_window_set_user_data (win->window, win->widget);
3610
3611   attributes.x = 0;
3612   attributes.y = 0;
3613   attributes.width = win->allocation.width;
3614   attributes.height = win->allocation.height;
3615   attributes.event_mask = (GDK_EXPOSURE_MASK            |
3616                            GDK_SCROLL_MASK              |
3617                            GDK_KEY_PRESS_MASK           |
3618                            GDK_BUTTON_PRESS_MASK        |
3619                            GDK_BUTTON_RELEASE_MASK      |
3620                            GDK_POINTER_MOTION_MASK      |
3621                            GDK_POINTER_MOTION_HINT_MASK |
3622                            gtk_widget_get_events (win->widget));
3623
3624   win->bin_window = gdk_window_new (win->window,
3625                                     &attributes,
3626                                     attributes_mask);
3627
3628   gdk_window_show (win->bin_window);
3629   gdk_window_set_user_data (win->bin_window, win->widget);
3630
3631   if (win->type == GTK_TEXT_WINDOW_TEXT)
3632     {
3633       /* I-beam cursor */
3634       cursor = gdk_cursor_new (GDK_XTERM);
3635       gdk_window_set_cursor (win->bin_window, cursor);
3636       gdk_cursor_destroy (cursor);
3637       
3638       gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->im_context,
3639                                         win->window);
3640
3641
3642       gdk_window_set_background (win->bin_window,
3643                                  &win->widget->style->base[GTK_WIDGET_STATE (win->widget)]);
3644     }
3645   else
3646     {
3647       gdk_window_set_background (win->bin_window,
3648                                  &win->widget->style->bg[GTK_WIDGET_STATE (win->widget)]);
3649     }
3650   
3651   g_object_set_qdata (G_OBJECT (win->window),
3652                       g_quark_from_static_string ("gtk-text-view-text-window"),
3653                       win);
3654
3655   g_object_set_qdata (G_OBJECT (win->bin_window),
3656                       g_quark_from_static_string ("gtk-text-view-text-window"),
3657                       win);
3658 }
3659
3660 static void
3661 text_window_unrealize (GtkTextWindow *win)
3662 {
3663   if (win->type == GTK_TEXT_WINDOW_TEXT)
3664     {
3665       gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->im_context,
3666                                         NULL);
3667     }
3668
3669   gdk_window_set_user_data (win->window, NULL);
3670   gdk_window_set_user_data (win->bin_window, NULL);
3671   gdk_window_destroy (win->bin_window);
3672   gdk_window_destroy (win->window);
3673   win->window = NULL;
3674   win->bin_window = NULL;
3675 }
3676
3677 static void
3678 text_window_size_allocate (GtkTextWindow *win,
3679                            GdkRectangle  *rect)
3680 {
3681   win->allocation = *rect;
3682   
3683   if (win->window)
3684     {
3685       gdk_window_move_resize (win->window,
3686                               rect->x, rect->y,
3687                               rect->width, rect->height);
3688       
3689       gdk_window_resize (win->bin_window,
3690                          rect->width, rect->height);
3691     }
3692 }
3693
3694 static void
3695 text_window_scroll        (GtkTextWindow *win,
3696                            gint           dx,
3697                            gint           dy)
3698 {
3699   if (dx != 0 || dy != 0)
3700     {
3701       gdk_window_scroll (win->bin_window, dx, dy);
3702       gdk_window_process_updates (win->bin_window, TRUE);
3703     }
3704 }
3705
3706 static void
3707 text_window_invalidate_rect (GtkTextWindow *win,
3708                              GdkRectangle  *rect)
3709 {
3710   gdk_window_invalidate_rect (win->bin_window, rect, FALSE);
3711 }
3712
3713 static gint
3714 text_window_get_width (GtkTextWindow *win)
3715 {
3716   return win->allocation.width;
3717 }
3718
3719 static gint
3720 text_window_get_height (GtkTextWindow *win)
3721 {
3722   return win->allocation.height;
3723 }
3724
3725 static void
3726 text_window_get_allocation (GtkTextWindow *win,
3727                             GdkRectangle  *rect)
3728 {
3729   *rect = win->allocation;
3730 }
3731
3732 /* Windows */
3733
3734
3735 GdkWindow*
3736 gtk_text_view_get_window (GtkTextView *text_view,
3737                           GtkTextWindowType win)
3738 {
3739   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
3740
3741   switch (win)
3742     {
3743     case GTK_TEXT_WINDOW_WIDGET:
3744       return GTK_WIDGET (text_view)->window;
3745       break;
3746       
3747     case GTK_TEXT_WINDOW_TEXT:
3748       return text_view->text_window->bin_window;
3749       break;
3750       
3751     case GTK_TEXT_WINDOW_LEFT:
3752       if (text_view->left_window)
3753         return text_view->left_window->bin_window;
3754       else
3755         return NULL;
3756       break;
3757       
3758     case GTK_TEXT_WINDOW_RIGHT:
3759       if (text_view->right_window)
3760         return text_view->right_window->bin_window;
3761       else
3762         return NULL;
3763       break;
3764       
3765     case GTK_TEXT_WINDOW_TOP:
3766       if (text_view->top_window)
3767         return text_view->top_window->bin_window;
3768       else
3769         return NULL;
3770       break;
3771       
3772     case GTK_TEXT_WINDOW_BOTTOM:
3773       if (text_view->bottom_window)
3774         return text_view->bottom_window->bin_window;
3775       else
3776         return NULL;
3777       break;
3778
3779     default:
3780       g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC);
3781       return NULL;
3782       break;
3783     }
3784 }
3785
3786 GtkTextWindowType
3787 gtk_text_view_get_window_type (GtkTextView *text_view,
3788                                GdkWindow   *window)
3789 {
3790   GtkTextWindow *win;
3791
3792   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
3793   g_return_val_if_fail (GDK_IS_WINDOW (text_view), 0);
3794
3795   if (window == GTK_WIDGET (text_view)->window)
3796     return GTK_TEXT_WINDOW_WIDGET;
3797   
3798   win = g_object_get_qdata (G_OBJECT (window),
3799                             g_quark_try_string ("gtk-text-view-text-window"));
3800
3801   if (win)
3802     return win->type;
3803   else
3804     {
3805       g_warning ("%s: Window is not a text view window",
3806                  G_STRLOC);
3807       return 0;
3808     }
3809 }
3810
3811 static void
3812 buffer_to_widget (GtkTextView      *text_view,
3813                   gint              buffer_x,
3814                   gint              buffer_y,
3815                   gint             *window_x,
3816                   gint             *window_y)
3817 {
3818   if (window_x)
3819     {
3820       *window_x = buffer_x - text_view->xoffset + FOCUS_EDGE_WIDTH;
3821       if (text_view->left_window)
3822         *window_x += text_view->left_window->allocation.width;
3823     }
3824   
3825   if (window_y)
3826     {
3827       *window_y = buffer_y - text_view->yoffset + FOCUS_EDGE_WIDTH;
3828       if (text_view->top_window)
3829         *window_y += text_view->top_window->allocation.height;
3830     }
3831 }
3832
3833 static void
3834 widget_to_text_window (GtkTextWindow *win,
3835                        gint           widget_x,
3836                        gint           widget_y,
3837                        gint          *window_x,
3838                        gint          *window_y)
3839 {
3840   if (window_x)
3841     *window_x = widget_x - win->allocation.x;
3842
3843   if (window_y)
3844     *window_y = widget_y - win->allocation.y;
3845 }
3846
3847 static void
3848 buffer_to_text_window (GtkTextView   *text_view,
3849                        GtkTextWindow *win,
3850                        gint           buffer_x,
3851                        gint           buffer_y,
3852                        gint          *window_x,
3853                        gint          *window_y)
3854 {
3855   if (win == NULL)
3856     {
3857       g_warning ("Attempt to convert text buffer coordinates to coordinates "
3858                  "for a nonexistent child window of GtkTextView");
3859       return;
3860     }
3861   
3862   buffer_to_widget (text_view,
3863                     buffer_x, buffer_y,
3864                     window_x, window_y);
3865
3866   widget_to_text_window (win,
3867                          window_x ? *window_x : 0,
3868                          window_y ? *window_y : 0,
3869                          window_x,
3870                          window_y);
3871 }
3872
3873 void
3874 gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
3875                                        GtkTextWindowType win,
3876                                        gint              buffer_x,
3877                                        gint              buffer_y,
3878                                        gint             *window_x,
3879                                        gint             *window_y)
3880 {
3881   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
3882
3883   switch (win)
3884     {
3885     case GTK_TEXT_WINDOW_WIDGET:
3886       buffer_to_widget (text_view,
3887                         buffer_x, buffer_y,
3888                         window_x, window_y);
3889       break;
3890       
3891     case GTK_TEXT_WINDOW_TEXT:
3892       if (window_x)
3893         *window_x = buffer_x - text_view->xoffset;
3894       if (window_y)
3895         *window_y = buffer_y - text_view->yoffset;
3896       break;
3897       
3898     case GTK_TEXT_WINDOW_LEFT:
3899       buffer_to_text_window (text_view,
3900                              text_view->left_window,
3901                              buffer_x, buffer_y,
3902                              window_x, window_y);
3903       break;
3904       
3905     case GTK_TEXT_WINDOW_RIGHT:
3906       buffer_to_text_window (text_view,
3907                              text_view->right_window,
3908                              buffer_x, buffer_y,
3909                              window_x, window_y);
3910       break;
3911       
3912     case GTK_TEXT_WINDOW_TOP:
3913       buffer_to_text_window (text_view,
3914                              text_view->top_window,
3915                              buffer_x, buffer_y,
3916                              window_x, window_y);
3917       break;
3918       
3919     case GTK_TEXT_WINDOW_BOTTOM:
3920       buffer_to_text_window (text_view,
3921                              text_view->bottom_window,
3922                              buffer_x, buffer_y,
3923                              window_x, window_y);
3924       break;
3925
3926     default:
3927       g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC);
3928       break;
3929     }
3930 }
3931
3932 static void
3933 widget_to_buffer (GtkTextView *text_view,
3934                   gint         widget_x,
3935                   gint         widget_y,
3936                   gint        *buffer_x,
3937                   gint        *buffer_y)
3938 {
3939   if (buffer_x)
3940     {
3941       *buffer_x = widget_x - FOCUS_EDGE_WIDTH + text_view->xoffset;
3942       if (text_view->left_window)
3943         *buffer_x -= text_view->left_window->allocation.width;
3944     }
3945   
3946   if (buffer_y)
3947     {
3948       *buffer_y = widget_y - FOCUS_EDGE_WIDTH + text_view->yoffset;
3949       if (text_view->top_window)
3950         *buffer_y -= text_view->top_window->allocation.height;
3951     }
3952 }
3953
3954 static void
3955 text_window_to_widget (GtkTextWindow *win,
3956                        gint           window_x,
3957                        gint           window_y,
3958                        gint          *widget_x,
3959                        gint          *widget_y)
3960 {
3961   if (widget_x)
3962     *widget_x = window_x + win->allocation.x;
3963
3964   if (widget_y)
3965     *widget_y = window_y + win->allocation.y;
3966 }
3967
3968 static void
3969 text_window_to_buffer (GtkTextView   *text_view,
3970                        GtkTextWindow *win,
3971                        gint           window_x,
3972                        gint           window_y,
3973                        gint          *buffer_x,
3974                        gint          *buffer_y)
3975 {
3976   if (win == NULL)
3977     {
3978       g_warning ("Attempt to convert GtkTextView buffer coordinates into "
3979                  "coordinates for a nonexistent child window.");
3980       return;
3981     }
3982   
3983   text_window_to_widget (win,
3984                          window_x,
3985                          window_y,
3986                          buffer_x,
3987                          buffer_y);
3988
3989   widget_to_buffer (text_view,
3990                     buffer_x ? *buffer_x : 0,
3991                     buffer_y ? *buffer_y : 0,
3992                     buffer_x,
3993                     buffer_y);
3994 }
3995
3996 void
3997 gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
3998                                        GtkTextWindowType win,
3999                                        gint              window_x,
4000                                        gint              window_y,
4001                                        gint             *buffer_x,
4002                                        gint             *buffer_y)
4003 {
4004   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
4005
4006   switch (win)
4007     {
4008     case GTK_TEXT_WINDOW_WIDGET:
4009       widget_to_buffer (text_view,
4010                         window_x, window_y,
4011                         buffer_x, buffer_y);
4012       break;
4013       
4014     case GTK_TEXT_WINDOW_TEXT:
4015       if (buffer_x)
4016         *buffer_x = window_x + text_view->xoffset;
4017       if (buffer_y)
4018         *buffer_y = window_y + text_view->yoffset;
4019       break;
4020       
4021     case GTK_TEXT_WINDOW_LEFT:
4022       text_window_to_buffer (text_view,
4023                              text_view->left_window,
4024                              window_x, window_y,
4025                              buffer_x, buffer_y);
4026       break;
4027       
4028     case GTK_TEXT_WINDOW_RIGHT:
4029       text_window_to_buffer (text_view,
4030                              text_view->right_window,
4031                              window_x, window_y,
4032                              buffer_x, buffer_y);
4033       break;
4034       
4035     case GTK_TEXT_WINDOW_TOP:
4036       text_window_to_buffer (text_view,
4037                              text_view->top_window,
4038                              window_x, window_y,
4039                              buffer_x, buffer_y);
4040       break;
4041       
4042     case GTK_TEXT_WINDOW_BOTTOM:
4043       text_window_to_buffer (text_view,
4044                              text_view->bottom_window,
4045                              window_x, window_y,
4046                              buffer_x, buffer_y);
4047       break;
4048
4049     default:
4050       g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC);
4051       break;
4052     }
4053 }
4054
4055 static void
4056 set_window_width (GtkTextView      *text_view,
4057                   gint              width,
4058                   GtkTextWindowType type,
4059                   GtkTextWindow   **winp)
4060 {
4061   if (width == 0)
4062     {
4063       if (*winp)
4064         {
4065           text_window_free (*winp);
4066           *winp = NULL;
4067           gtk_widget_queue_resize (GTK_WIDGET (text_view));
4068         }
4069     }
4070   else
4071     {
4072       if (*winp == NULL)
4073         {
4074           *winp = text_window_new (type,
4075                                    GTK_WIDGET (text_view),
4076                                    width, 0);
4077         }
4078       else
4079         {
4080           if ((*winp)->requisition.width == width)
4081             return;
4082         }
4083
4084       gtk_widget_queue_resize (GTK_WIDGET (text_view));
4085     }
4086 }
4087
4088
4089 static void
4090 set_window_height (GtkTextView      *text_view,
4091                    gint              height,
4092                    GtkTextWindowType type,
4093                    GtkTextWindow   **winp)
4094 {
4095   if (height == 0)
4096     {
4097       if (*winp)
4098         {
4099           text_window_free (*winp);
4100           *winp = NULL;
4101           gtk_widget_queue_resize (GTK_WIDGET (text_view));
4102         }
4103     }
4104   else
4105     {
4106       if (*winp == NULL)
4107         {
4108           *winp = text_window_new (type,
4109                                    GTK_WIDGET (text_view),
4110                                    0, height);
4111         }
4112       else
4113         {
4114           if ((*winp)->requisition.height == height)
4115             return;
4116         }
4117
4118       gtk_widget_queue_resize (GTK_WIDGET (text_view));
4119     }
4120 }
4121
4122 void
4123 gtk_text_view_set_border_window_size (GtkTextView      *text_view,
4124                                       GtkTextWindowType type,
4125                                       gint              size)
4126
4127 {
4128   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
4129   g_return_if_fail (size >= 0);
4130   g_return_if_fail (type != GTK_TEXT_WINDOW_WIDGET);
4131   g_return_if_fail (type != GTK_TEXT_WINDOW_TEXT);
4132   
4133   switch (type)
4134     {
4135     case GTK_TEXT_WINDOW_LEFT:
4136       set_window_width (text_view, size, GTK_TEXT_WINDOW_LEFT,
4137                         &text_view->left_window);
4138       break;
4139
4140     case GTK_TEXT_WINDOW_RIGHT:
4141       set_window_width (text_view, size, GTK_TEXT_WINDOW_RIGHT,
4142                         &text_view->right_window);
4143       break;
4144
4145     case GTK_TEXT_WINDOW_TOP:
4146       set_window_height (text_view, size, GTK_TEXT_WINDOW_TOP,
4147                          &text_view->top_window);
4148       break;
4149
4150     case GTK_TEXT_WINDOW_BOTTOM:
4151       set_window_height (text_view, size, GTK_TEXT_WINDOW_BOTTOM,
4152                          &text_view->bottom_window);
4153       break;
4154
4155     default:
4156       g_warning ("Unknown GtkTextWindowType in %s", G_STRLOC);
4157       break;
4158     }
4159 }
4160
4161 void
4162 gtk_text_view_set_text_window_size (GtkTextView *text_view,
4163                                     gint         width,
4164                                     gint         height)
4165 {
4166   GtkTextWindow *win;
4167   
4168   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
4169   g_return_if_fail (width > 0);
4170   g_return_if_fail (height > 0);
4171
4172   win = text_view->text_window;
4173
4174   if (win->requisition.width == width &&
4175       win->requisition.height == height)
4176     return;
4177
4178   win->requisition.width = width;
4179   win->requisition.height = height;
4180
4181   gtk_widget_queue_resize (GTK_WIDGET (text_view));
4182 }
4183
4184 /*
4185  * Child widgets
4186  */
4187
4188 static GtkTextViewChild*
4189 text_view_child_new_anchored (GtkWidget          *child,
4190                               GtkTextChildAnchor *anchor)
4191 {
4192   GtkTextViewChild *vc;
4193
4194   vc = g_new (GtkTextViewChild, 1);
4195
4196   vc->widget = child;
4197   vc->anchor = anchor;
4198   
4199   g_object_ref (G_OBJECT (vc->widget));
4200   gtk_text_child_anchor_ref (vc->anchor);
4201
4202   gtk_object_set_data (GTK_OBJECT (child),
4203                        "gtk-text-view-child",
4204                        vc);
4205   
4206   return vc;
4207 }
4208
4209 static GtkTextViewChild*
4210 text_view_child_new_window (GtkWidget          *child,
4211                             GtkTextWindowType   type,
4212                             gint                x,
4213                             gint                y)
4214 {
4215   GtkTextViewChild *vc;
4216
4217   vc = g_new (GtkTextViewChild, 1);
4218
4219   vc->widget = child;
4220   vc->anchor = NULL;
4221   
4222   g_object_ref (G_OBJECT (vc->widget));
4223
4224   vc->type = type;
4225   vc->x = x;
4226   vc->y = y;
4227
4228   return vc;
4229 }
4230
4231 static void
4232 text_view_child_free (GtkTextViewChild *child)
4233 {
4234
4235   gtk_object_remove_data (GTK_OBJECT (child->widget),
4236                           "gtk-text-view-child");
4237   
4238   g_object_unref (G_OBJECT (child->widget));
4239   gtk_text_child_anchor_unref (child->anchor);
4240   
4241   g_free (child);
4242 }
4243
4244 static void
4245 text_view_child_realize (GtkTextView      *text_view,
4246                          GtkTextViewChild *vc)
4247 {
4248   if (vc->anchor)
4249     gtk_widget_set_parent_window (vc->widget,
4250                                   text_view->text_window->bin_window);
4251   else
4252     {
4253       GdkWindow *window;
4254       window = gtk_text_view_get_window (text_view,
4255                                          vc->type);
4256       gtk_widget_set_parent_window (vc->widget, window);                                    
4257     }
4258   
4259   gtk_widget_realize (vc->widget);
4260 }
4261
4262 static void
4263 text_view_child_unrealize (GtkTextViewChild *vc)
4264 {
4265   gtk_widget_unrealize (vc->widget);
4266 }
4267
4268 static void
4269 add_child (GtkTextView      *text_view,
4270            GtkTextViewChild *vc)
4271 {
4272   text_view->children = g_slist_prepend (text_view->children,
4273                                          vc);
4274
4275   gtk_widget_set_parent (vc->widget, GTK_WIDGET (text_view));
4276
4277   if (GTK_WIDGET_REALIZED (text_view))
4278     text_view_child_realize (text_view, vc);
4279     
4280   if (GTK_WIDGET_VISIBLE (text_view) && GTK_WIDGET_VISIBLE (vc->widget))
4281     {
4282       if (GTK_WIDGET_MAPPED (text_view))
4283         gtk_widget_map (vc->widget);
4284
4285       gtk_widget_queue_resize (vc->widget);
4286     }
4287 }
4288
4289 void
4290 gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
4291                                    GtkWidget            *child,
4292                                    GtkTextChildAnchor   *anchor)
4293 {
4294   GtkTextViewChild *vc;
4295   
4296   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
4297   g_return_if_fail (GTK_IS_WIDGET (child));
4298   g_return_if_fail (anchor != NULL);
4299   g_return_if_fail (child->parent == NULL);
4300   
4301   vc = text_view_child_new_anchored (child, anchor);
4302
4303   add_child (text_view, vc);
4304 }
4305
4306 void
4307 gtk_text_view_add_child_in_window (GtkTextView          *text_view,
4308                                    GtkWidget            *child,
4309                                    GtkTextWindowType     which_window,
4310                                    gint                  xpos,
4311                                    gint                  ypos)
4312 {
4313   GtkTextViewChild *vc;
4314   
4315   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
4316   g_return_if_fail (GTK_IS_WIDGET (child));
4317   g_return_if_fail (xpos >= 0);
4318   g_return_if_fail (ypos >= 0);
4319   g_return_if_fail (child->parent == NULL);
4320
4321   vc = text_view_child_new_window (child, which_window,
4322                                    xpos, ypos);
4323
4324   add_child (text_view, vc);
4325 }
4326
4327 void
4328 gtk_text_view_move_child          (GtkTextView          *text_view,
4329                                    GtkWidget            *child,
4330                                    gint                  xpos,
4331                                    gint                  ypos)
4332 {
4333   GtkTextViewChild *vc;
4334   
4335   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
4336   g_return_if_fail (GTK_IS_WIDGET (child));
4337   g_return_if_fail (xpos >= 0);
4338   g_return_if_fail (ypos >= 0);
4339   g_return_if_fail (child->parent == (GtkWidget*) text_view);
4340
4341   vc = gtk_object_get_data (GTK_OBJECT (child),
4342                             "gtk-text-view-child");
4343
4344   g_assert (vc != NULL);
4345
4346   vc->x = xpos;
4347   vc->y = ypos;
4348
4349   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (text_view))
4350     gtk_widget_queue_resize (child);
4351 }
4352
4353