]> Pileus Git - ~andy/gtk/blob - gtk/gtktextview.c
Remove all references to offscreen flag which was no longer used.
[~andy/gtk] / gtk / gtktextview.c
1 /* gtktext.c - A "view" widget for the GtkTextBuffer object
2  * 
3  * Copyright (c) 1992-1994 The Regents of the University of California.
4  * Copyright (c) 1994-1996 Sun Microsystems, Inc.
5  * Copyright (c) 1999 by Scriptics Corporation.
6  * Copyright (c) 2000      Red Hat, Inc.
7  * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
8  *
9  * This software is copyrighted by the Regents of the University of
10  * California, Sun Microsystems, Inc., and other parties.  The
11  * following terms apply to all files associated with the software
12  * unless explicitly disclaimed in individual files.
13  * 
14  * The authors hereby grant permission to use, copy, modify,
15  * distribute, and license this software and its documentation for any
16  * purpose, provided that existing copyright notices are retained in
17  * all copies and that this notice is included verbatim in any
18  * distributions. No written agreement, license, or royalty fee is
19  * required for any of the authorized uses.  Modifications to this
20  * software may be copyrighted by their authors and need not follow
21  * the licensing terms described here, provided that the new terms are
22  * clearly indicated on the first page of each file where they apply.
23  * 
24  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
25  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
26  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
27  * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  * 
30  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
31  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
32  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
33  * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
34  * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
35  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
36  *
37  * GOVERNMENT USE: If you are acquiring this software on behalf of the
38  * U.S. government, the Government shall have only "Restricted Rights"
39  * in the software and related documentation as defined in the Federal
40  * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
41  * are acquiring the software on behalf of the Department of Defense,
42  * the software shall be classified as "Commercial Computer Software"
43  * and the Government shall have only "Restricted Rights" as defined
44  * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
45  * foregoing, the authors grant the U.S. Government and others acting
46  * in its behalf permission to use and distribute the software in
47  * accordance with the terms specified in this license.
48  * 
49  */
50
51 #include "gtkbindings.h"
52 #include "gtkdnd.h"
53 #include "gtkmain.h"
54 #include "gtksignal.h"
55 #include "gtktext.h"
56 #include "gtktextdisplay.h"
57 #include "gtktextview.h"
58 #include "gtkimmulticontext.h"
59 #include "gdk/gdkkeysyms.h"
60
61 enum {
62   MOVE_INSERT,
63   SET_ANCHOR,
64   SCROLL_TEXT,
65   DELETE_TEXT,
66   CUT_TEXT,
67   COPY_TEXT,
68   PASTE_TEXT,
69   TOGGLE_OVERWRITE,
70   SET_SCROLL_ADJUSTMENTS,
71   LAST_SIGNAL
72 };
73
74 enum {
75   ARG_0,
76   ARG_HEIGHT_LINES,
77   ARG_WIDTH_COLUMNS,
78   ARG_PIXELS_ABOVE_LINES,
79   ARG_PIXELS_BELOW_LINES,
80   ARG_PIXELS_INSIDE_WRAP,
81   ARG_EDITABLE,
82   ARG_WRAP_MODE,
83   LAST_ARG
84 };
85
86 static void gtk_text_view_init                 (GtkTextView      *text_view);
87 static void gtk_text_view_class_init           (GtkTextViewClass *klass);
88 static void gtk_text_view_destroy              (GtkObject        *object);
89 static void gtk_text_view_finalize             (GObject          *object);
90 static void gtk_text_view_set_arg              (GtkObject        *object,
91                                                 GtkArg           *arg,
92                                                 guint             arg_id);
93 static void gtk_text_view_get_arg              (GtkObject        *object,
94                                                 GtkArg           *arg,
95                                                 guint             arg_id);
96 static void gtk_text_view_size_request         (GtkWidget        *widget,
97                                                 GtkRequisition   *requisition);
98 static void gtk_text_view_size_allocate        (GtkWidget        *widget,
99                                                 GtkAllocation    *allocation);
100 static void gtk_text_view_realize              (GtkWidget        *widget);
101 static void gtk_text_view_unrealize            (GtkWidget        *widget);
102 static void gtk_text_view_style_set            (GtkWidget        *widget,
103                                                 GtkStyle         *previous_style);
104 static gint gtk_text_view_event                (GtkWidget        *widget,
105                                                 GdkEvent         *event);
106 static gint gtk_text_view_key_press_event      (GtkWidget        *widget,
107                                                 GdkEventKey      *event);
108 static gint gtk_text_view_key_release_event    (GtkWidget        *widget,
109                                                 GdkEventKey      *event);
110 static gint gtk_text_view_button_press_event   (GtkWidget        *widget,
111                                                 GdkEventButton   *event);
112 static gint gtk_text_view_button_release_event (GtkWidget        *widget,
113                                                 GdkEventButton   *event);
114 static gint gtk_text_view_focus_in_event       (GtkWidget        *widget,
115                                                 GdkEventFocus    *event);
116 static gint gtk_text_view_focus_out_event      (GtkWidget        *widget,
117                                                 GdkEventFocus    *event);
118 static gint gtk_text_view_motion_event         (GtkWidget        *widget,
119                                                 GdkEventMotion   *event);
120 static void gtk_text_view_draw                 (GtkWidget        *widget,
121                                                 GdkRectangle     *area);
122 static gint gtk_text_view_expose_event         (GtkWidget        *widget,
123                                                 GdkEventExpose   *expose);
124
125
126 /* Source side drag signals */
127 static void gtk_text_view_drag_begin       (GtkWidget        *widget,
128                                             GdkDragContext   *context);
129 static void gtk_text_view_drag_end         (GtkWidget        *widget,
130                                             GdkDragContext   *context);
131 static void gtk_text_view_drag_data_get    (GtkWidget        *widget,
132                                             GdkDragContext   *context,
133                                             GtkSelectionData *selection_data,
134                                             guint             info,
135                                             guint             time);
136 static void gtk_text_view_drag_data_delete (GtkWidget        *widget,
137                                             GdkDragContext   *context);
138
139 /* Target side drag signals */
140 static void     gtk_text_view_drag_leave         (GtkWidget        *widget,
141                                                   GdkDragContext   *context,
142                                                   guint             time);
143 static gboolean gtk_text_view_drag_motion        (GtkWidget        *widget,
144                                                   GdkDragContext   *context,
145                                                   gint              x,
146                                                   gint              y,
147                                                   guint             time);
148 static gboolean gtk_text_view_drag_drop          (GtkWidget        *widget,
149                                                   GdkDragContext   *context,
150                                                   gint              x,
151                                                   gint              y,
152                                                   guint             time);
153 static void     gtk_text_view_drag_data_received (GtkWidget        *widget,
154                                                   GdkDragContext   *context,
155                                                   gint              x,
156                                                   gint              y,
157                                                   GtkSelectionData *selection_data,
158                                                   guint             info,
159                                                   guint             time);
160
161 static void gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
162                                                   GtkAdjustment *hadj,
163                                                   GtkAdjustment *vadj);
164
165 static void gtk_text_view_move_insert      (GtkTextView             *text_view,
166                                             GtkTextViewMovementStep  step,
167                                             gint                     count,
168                                             gboolean                 extend_selection);
169 static void gtk_text_view_set_anchor       (GtkTextView             *text_view);
170 static void gtk_text_view_scroll_text      (GtkTextView             *text_view,
171                                             GtkTextViewScrollType    type);
172 static void gtk_text_view_delete_text      (GtkTextView             *text_view,
173                                             GtkTextViewDeleteType    type,
174                                             gint                     count);
175 static void gtk_text_view_cut_text         (GtkTextView             *text_view);
176 static void gtk_text_view_copy_text        (GtkTextView             *text_view);
177 static void gtk_text_view_paste_text       (GtkTextView             *text_view);
178 static void gtk_text_view_toggle_overwrite (GtkTextView             *text_view);
179
180
181 static void     gtk_text_view_validate_onscreen     (GtkTextView        *text_view);
182 static void     gtk_text_view_get_first_para_iter   (GtkTextView        *text_view,
183                                                      GtkTextIter        *iter);
184 static void     gtk_text_view_scroll_calc_now       (GtkTextView        *text_view);
185 static void     gtk_text_view_set_values_from_style (GtkTextView        *text_view,
186                                                      GtkTextStyleValues *values,
187                                                      GtkStyle           *style);
188 static void     gtk_text_view_ensure_layout         (GtkTextView        *text_view);
189 static void     gtk_text_view_destroy_layout        (GtkTextView        *text_view);
190 static void     gtk_text_view_start_selection_drag  (GtkTextView        *text_view,
191                                                      const GtkTextIter  *iter,
192                                                      GdkEventButton     *event);
193 static gboolean gtk_text_view_end_selection_drag    (GtkTextView        *text_view,
194                                                      GdkEventButton     *event);
195 static void     gtk_text_view_start_selection_dnd   (GtkTextView        *text_view,
196                                                      const GtkTextIter  *iter,
197                                                      GdkEventButton     *event);
198 static void     gtk_text_view_start_cursor_blink    (GtkTextView        *text_view);
199 static void     gtk_text_view_stop_cursor_blink     (GtkTextView        *text_view);
200
201 static void gtk_text_view_value_changed (GtkAdjustment *adj,
202                                          GtkTextView   *view);
203 static void gtk_text_view_commit_handler            (GtkIMContext  *context,
204                                                      const gchar   *str,
205                                                      GtkTextView   *text_view);
206
207 static void gtk_text_view_mark_set_handler       (GtkTextBuffer     *buffer,
208                                                   const GtkTextIter *location,
209                                                   const char        *mark_name,
210                                                   gpointer           data);
211 static void gtk_text_view_get_virtual_cursor_pos (GtkTextView       *text_view,
212                                                   gint              *x,
213                                                   gint              *y);
214 static void gtk_text_view_set_virtual_cursor_pos (GtkTextView       *text_view,
215                                                   gint               x,
216                                                   gint               y);
217
218 enum {
219   TARGET_STRING,
220   TARGET_TEXT,
221   TARGET_COMPOUND_TEXT,
222   TARGET_UTF8_STRING
223 };
224
225 static GdkAtom clipboard_atom = GDK_NONE;
226 static GdkAtom text_atom = GDK_NONE;
227 static GdkAtom ctext_atom = GDK_NONE;
228 static GdkAtom utf8_atom = GDK_NONE;
229
230
231 static GtkTargetEntry target_table[] = {
232   { "UTF8_STRING", 0, TARGET_UTF8_STRING },
233   { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
234   { "TEXT", 0, TARGET_TEXT },
235   { "text/plain", 0, TARGET_STRING },
236   { "STRING",     0, TARGET_STRING }
237 };
238
239 static guint n_targets = sizeof (target_table) / sizeof (target_table[0]);
240
241 static GtkContainerClass *parent_class = NULL;
242 static guint signals[LAST_SIGNAL] = { 0 };
243
244 GtkType
245 gtk_text_view_get_type (void)
246 {
247   static GtkType our_type = 0;
248
249   if (our_type == 0)
250     {
251       static const GtkTypeInfo our_info =
252       {
253         "GtkTextView",
254         sizeof (GtkTextView),
255         sizeof (GtkTextViewClass),
256         (GtkClassInitFunc) gtk_text_view_class_init,
257         (GtkObjectInitFunc) gtk_text_view_init,
258         /* reserved_1 */ NULL,
259         /* reserved_2 */ NULL,
260         (GtkClassInitFunc) NULL
261       };
262
263     our_type = gtk_type_unique (GTK_TYPE_CONTAINER, &our_info);
264   }
265
266   return our_type;
267 }
268
269 static void
270 add_move_insert_binding (GtkBindingSet *binding_set,
271                          guint keyval,
272                          guint modmask,
273                          GtkTextViewMovementStep step,
274                          gint count)
275 {
276   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
277   
278   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
279                                 "move_insert", 3,
280                                 GTK_TYPE_ENUM, step,
281                                 GTK_TYPE_INT, count,
282                                 GTK_TYPE_BOOL, FALSE);
283
284   /* Selection-extending version */
285   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
286                                 "move_insert", 3,
287                                 GTK_TYPE_ENUM, step,
288                                 GTK_TYPE_INT, count,
289                                 GTK_TYPE_BOOL, TRUE);
290 }
291
292 static void
293 gtk_text_view_class_init (GtkTextViewClass *klass)
294 {
295   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
296   GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
297   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
298   GtkBindingSet *binding_set;
299   
300   parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
301
302   /*
303    * Arguments
304    */
305   gtk_object_add_arg_type ("GtkTextView::height_lines", GTK_TYPE_INT,
306                            GTK_ARG_READWRITE, ARG_HEIGHT_LINES);
307   gtk_object_add_arg_type ("GtkTextView::width_columns", GTK_TYPE_INT,
308                            GTK_ARG_READWRITE, ARG_WIDTH_COLUMNS);
309   gtk_object_add_arg_type ("GtkTextView::pixels_above_lines", GTK_TYPE_INT,
310                            GTK_ARG_READWRITE, ARG_PIXELS_ABOVE_LINES);
311   gtk_object_add_arg_type ("GtkTextView::pixels_below_lines", GTK_TYPE_INT,
312                            GTK_ARG_READWRITE, ARG_PIXELS_BELOW_LINES);
313   gtk_object_add_arg_type ("GtkTextView::pixels_inside_wrap", GTK_TYPE_INT,
314                            GTK_ARG_READWRITE, ARG_PIXELS_INSIDE_WRAP);
315   gtk_object_add_arg_type ("GtkTextView::editable", GTK_TYPE_BOOL,
316                            GTK_ARG_READWRITE, ARG_EDITABLE);
317   gtk_object_add_arg_type ("GtkTextView::wrap_mode", GTK_TYPE_ENUM,
318                            GTK_ARG_READWRITE, ARG_WRAP_MODE);
319
320
321   /*
322    * Signals
323    */
324
325   signals[MOVE_INSERT] = 
326       gtk_signal_new ("move_insert",
327                       GTK_RUN_LAST | GTK_RUN_ACTION,
328                       GTK_CLASS_TYPE (object_class),
329                       GTK_SIGNAL_OFFSET (GtkTextViewClass, move_insert),
330                       gtk_marshal_NONE__INT_INT_INT,
331                       GTK_TYPE_NONE, 3, GTK_TYPE_ENUM, GTK_TYPE_INT, GTK_TYPE_BOOL);
332
333   signals[SET_ANCHOR] = 
334       gtk_signal_new ("set_anchor",
335                       GTK_RUN_LAST | GTK_RUN_ACTION,
336                       GTK_CLASS_TYPE (object_class),
337                       GTK_SIGNAL_OFFSET (GtkTextViewClass, set_anchor),
338                       gtk_marshal_NONE__NONE,
339                       GTK_TYPE_NONE, 0);
340
341   signals[SCROLL_TEXT] = 
342       gtk_signal_new ("scroll_text",
343                       GTK_RUN_LAST | GTK_RUN_ACTION,
344                       GTK_CLASS_TYPE (object_class),
345                       GTK_SIGNAL_OFFSET (GtkTextViewClass, scroll_text),
346                       gtk_marshal_NONE__INT,
347                       GTK_TYPE_NONE, 1, GTK_TYPE_ENUM);
348
349   signals[DELETE_TEXT] = 
350       gtk_signal_new ("delete_text",
351                       GTK_RUN_LAST | GTK_RUN_ACTION,
352                       GTK_CLASS_TYPE (object_class),
353                       GTK_SIGNAL_OFFSET (GtkTextViewClass, delete_text),
354                       gtk_marshal_NONE__INT_INT,
355                       GTK_TYPE_NONE, 2, GTK_TYPE_ENUM, GTK_TYPE_INT);
356
357   signals[CUT_TEXT] =
358     gtk_signal_new ("cut_text",
359                     GTK_RUN_LAST | GTK_RUN_ACTION,
360                     GTK_CLASS_TYPE (object_class),
361                     GTK_SIGNAL_OFFSET (GtkTextViewClass, cut_text),
362                     gtk_marshal_NONE__NONE,
363                     GTK_TYPE_NONE, 0);
364
365   signals[COPY_TEXT] =
366     gtk_signal_new ("copy_text",
367                     GTK_RUN_LAST | GTK_RUN_ACTION,
368                     GTK_CLASS_TYPE (object_class),
369                     GTK_SIGNAL_OFFSET (GtkTextViewClass, copy_text),
370                     gtk_marshal_NONE__NONE,
371                     GTK_TYPE_NONE, 0);
372
373   signals[PASTE_TEXT] =
374     gtk_signal_new ("paste_text",
375                     GTK_RUN_LAST | GTK_RUN_ACTION,
376                     GTK_CLASS_TYPE (object_class),
377                     GTK_SIGNAL_OFFSET (GtkTextViewClass, paste_text),
378                     gtk_marshal_NONE__NONE,
379                     GTK_TYPE_NONE, 0);
380
381   signals[TOGGLE_OVERWRITE] =
382     gtk_signal_new ("toggle_overwrite",
383                     GTK_RUN_LAST | GTK_RUN_ACTION,
384                     GTK_CLASS_TYPE (object_class),
385                     GTK_SIGNAL_OFFSET (GtkTextViewClass, toggle_overwrite),
386                     gtk_marshal_NONE__NONE,
387                     GTK_TYPE_NONE, 0);
388
389   signals[SET_SCROLL_ADJUSTMENTS] = widget_class->set_scroll_adjustments_signal =
390     gtk_signal_new ("set_scroll_adjustments",
391                     GTK_RUN_LAST,
392                     GTK_CLASS_TYPE (object_class),
393                     GTK_SIGNAL_OFFSET (GtkTextViewClass, set_scroll_adjustments),
394                     gtk_marshal_NONE__POINTER_POINTER,
395                     GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
396   
397   gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
398
399   /*
400    * Key bindings
401    */
402
403   binding_set = gtk_binding_set_by_class (klass);
404
405   /* Moving the insertion point */
406   add_move_insert_binding (binding_set, GDK_Right, 0,
407                           GTK_TEXT_MOVEMENT_POSITIONS, 1);
408   
409   add_move_insert_binding (binding_set, GDK_Left, 0,
410                           GTK_TEXT_MOVEMENT_POSITIONS, -1);
411
412   add_move_insert_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
413                           GTK_TEXT_MOVEMENT_CHAR, 1);
414   
415   add_move_insert_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
416                           GTK_TEXT_MOVEMENT_CHAR, -1);
417   
418   add_move_insert_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
419                           GTK_TEXT_MOVEMENT_WORD, 1);
420
421   add_move_insert_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
422                           GTK_TEXT_MOVEMENT_WORD, -1);
423   
424   /* Eventually we want to move by display lines, not paragraphs */
425   add_move_insert_binding (binding_set, GDK_Up, 0,
426                           GTK_TEXT_MOVEMENT_LINE, -1);
427   
428   add_move_insert_binding (binding_set, GDK_Down, 0,
429                           GTK_TEXT_MOVEMENT_LINE, 1);
430
431   add_move_insert_binding (binding_set, GDK_p, GDK_CONTROL_MASK,
432                           GTK_TEXT_MOVEMENT_LINE, -1);
433   
434   add_move_insert_binding (binding_set, GDK_n, GDK_CONTROL_MASK,
435                           GTK_TEXT_MOVEMENT_LINE, 1);
436   
437   add_move_insert_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
438                           GTK_TEXT_MOVEMENT_PARAGRAPH_ENDS, -1);
439
440   add_move_insert_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
441                           GTK_TEXT_MOVEMENT_PARAGRAPH_ENDS, 1);
442
443   add_move_insert_binding (binding_set, GDK_f, GDK_MOD1_MASK,
444                           GTK_TEXT_MOVEMENT_WORD, 1);
445
446   add_move_insert_binding (binding_set, GDK_b, GDK_MOD1_MASK,
447                           GTK_TEXT_MOVEMENT_WORD, -1);
448
449   add_move_insert_binding (binding_set, GDK_Home, 0,
450                           GTK_TEXT_MOVEMENT_BUFFER_ENDS, -1);
451
452   add_move_insert_binding (binding_set, GDK_End, 0,
453                           GTK_TEXT_MOVEMENT_BUFFER_ENDS, 1);
454   
455   /* Setting the cut/paste/copy anchor */
456   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK,
457                                 "set_anchor", 0);
458
459   
460   /* Scrolling around */
461   gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
462                                 "scroll_text", 1,
463                                 GTK_TYPE_ENUM, GTK_TEXT_SCROLL_PAGE_UP);
464
465   gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
466                                 "scroll_text", 1,
467                                 GTK_TYPE_ENUM, GTK_TEXT_SCROLL_PAGE_DOWN);
468
469   /* Deleting text */
470   gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
471                                 "delete_text", 2,
472                                 GTK_TYPE_ENUM, GTK_TEXT_DELETE_CHAR,
473                                 GTK_TYPE_INT, 1);
474
475   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
476                                 "delete_text", 2,
477                                 GTK_TYPE_ENUM, GTK_TEXT_DELETE_CHAR,
478                                 GTK_TYPE_INT, -1);
479
480   gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
481                                 "delete_text", 2,
482                                 GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_WORD,
483                                 GTK_TYPE_INT, 1);
484
485   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
486                                 "delete_text", 2,
487                                 GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_WORD,
488                                 GTK_TYPE_INT, -1);
489
490   gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
491                                 "delete_text", 2,
492                                 GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_PARAGRAPH,
493                                 GTK_TYPE_INT, 1);
494
495   gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
496                                 "delete_text", 2,
497                                 GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHOLE_PARAGRAPH,
498                                 GTK_TYPE_INT, 1);
499
500   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
501                                 "delete_text", 2,
502                                 GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE,
503                                 GTK_TYPE_INT, 1);
504
505   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
506                                 "delete_text", 2,
507                                 GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHITESPACE,
508                                 GTK_TYPE_INT, 1);
509   
510   /* Cut/copy/paste */
511
512   gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
513                                 "cut_text", 0);
514
515   gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
516                                 "cut_text", 0);
517   
518   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
519                                 "copy_text", 0);
520   
521   gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
522                                 "paste_text", 0);
523
524   /* Overwrite */
525   gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
526                                 "toggle_overwrite", 0);
527   
528   /*
529    * Default handlers and virtual methods
530    */
531   object_class->set_arg = gtk_text_view_set_arg;
532   object_class->get_arg = gtk_text_view_get_arg;
533
534   object_class->destroy = gtk_text_view_destroy;
535   gobject_class->finalize = gtk_text_view_finalize;
536
537   widget_class->realize = gtk_text_view_realize;
538   widget_class->unrealize = gtk_text_view_unrealize;
539   widget_class->style_set = gtk_text_view_style_set;
540   widget_class->size_request = gtk_text_view_size_request;
541   widget_class->size_allocate = gtk_text_view_size_allocate;
542   widget_class->event = gtk_text_view_event;
543   widget_class->key_press_event = gtk_text_view_key_press_event;
544   widget_class->key_release_event = gtk_text_view_key_release_event;
545   widget_class->button_press_event = gtk_text_view_button_press_event;
546   widget_class->button_release_event = gtk_text_view_button_release_event;
547   widget_class->focus_in_event = gtk_text_view_focus_in_event;
548   widget_class->focus_out_event = gtk_text_view_focus_out_event;
549   widget_class->motion_notify_event = gtk_text_view_motion_event;
550   widget_class->expose_event = gtk_text_view_expose_event;
551   widget_class->draw = gtk_text_view_draw;
552
553   widget_class->drag_begin = gtk_text_view_drag_begin;
554   widget_class->drag_end = gtk_text_view_drag_end;
555   widget_class->drag_data_get = gtk_text_view_drag_data_get;
556   widget_class->drag_data_delete = gtk_text_view_drag_data_delete;
557
558   widget_class->drag_leave = gtk_text_view_drag_leave;
559   widget_class->drag_motion = gtk_text_view_drag_motion;
560   widget_class->drag_drop = gtk_text_view_drag_drop;
561   widget_class->drag_data_received = gtk_text_view_drag_data_received;
562
563   klass->move_insert = gtk_text_view_move_insert;
564   klass->set_anchor = gtk_text_view_set_anchor;
565   klass->scroll_text = gtk_text_view_scroll_text;
566   klass->delete_text = gtk_text_view_delete_text;
567   klass->cut_text = gtk_text_view_cut_text;
568   klass->copy_text = gtk_text_view_copy_text;
569   klass->paste_text = gtk_text_view_paste_text;
570   klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
571   klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments;
572 }
573
574 void
575 gtk_text_view_init (GtkTextView *text_view)
576 {
577   GtkWidget *widget;
578   
579   widget = GTK_WIDGET (text_view);
580
581   GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
582
583   text_view->wrap_mode = GTK_WRAPMODE_NONE;
584
585   if (!clipboard_atom)
586     clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
587
588   if (!text_atom)
589     text_atom = gdk_atom_intern ("TEXT", FALSE);
590
591   if (!ctext_atom)
592     ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
593
594   if (!utf8_atom)
595     utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
596   
597   gtk_drag_dest_set (widget,
598                     GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION,
599                     target_table, n_targets,
600                     GDK_ACTION_COPY | GDK_ACTION_MOVE);
601
602   text_view->virtual_cursor_x = -1;
603   text_view->virtual_cursor_y = -1;
604
605   /* This object is completely private. No external entity can gain a reference
606    * to it; so we create it here and destroy it in finalize().
607    */
608   text_view->im_context = gtk_im_multicontext_new ();
609   
610   gtk_signal_connect (GTK_OBJECT (text_view->im_context), "commit",
611                       GTK_SIGNAL_FUNC (gtk_text_view_commit_handler), text_view);
612 }
613
614 GtkWidget*
615 gtk_text_view_new (void)
616 {
617   return GTK_WIDGET (gtk_type_new (gtk_text_view_get_type ()));
618 }
619
620 GtkWidget*
621 gtk_text_view_new_with_buffer (GtkTextBuffer *buffer)
622 {
623   GtkTextView *text_view;
624
625   text_view = (GtkTextView*)gtk_text_view_new ();
626
627   gtk_text_view_set_buffer (text_view, buffer);
628
629   return GTK_WIDGET (text_view);
630 }
631
632 void
633 gtk_text_view_set_buffer (GtkTextView *text_view,
634                           GtkTextBuffer *buffer)
635 {
636   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
637   g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
638   
639   if (text_view->buffer == buffer)
640     return;
641
642   if (text_view->buffer != NULL)
643     {
644       gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->buffer),
645                                      gtk_text_view_mark_set_handler, text_view);
646       gtk_object_unref (GTK_OBJECT (text_view->buffer));
647       text_view->dnd_mark = NULL;
648     }
649
650   text_view->buffer = buffer;
651   
652   if (buffer != NULL)
653     {
654       char *mark_name;
655       
656       GtkTextIter start;
657       
658       gtk_object_ref (GTK_OBJECT (buffer));
659       gtk_object_sink (GTK_OBJECT (buffer));
660
661       if (text_view->layout)
662         gtk_text_layout_set_buffer (text_view->layout, buffer);
663
664       gtk_text_buffer_get_iter_at_char (text_view->buffer, &start, 0);
665       
666       text_view->dnd_mark = gtk_text_buffer_create_mark (text_view->buffer,
667                                                          "__drag_target",
668                                                          &start, FALSE);
669
670       /* Initialize. FIXME: Allow anonymous marks and use one here
671        */
672       mark_name = g_strdup_printf ("__first_para_%p", text_view);
673       text_view->first_para_mark = gtk_text_buffer_create_mark (text_view->buffer,
674                                                                 mark_name,
675                                                                 &start, TRUE);
676       g_free (mark_name);
677       text_view->first_para_pixels = 0;
678       
679       gtk_signal_connect (GTK_OBJECT (text_view->buffer), "mark_set",
680                           gtk_text_view_mark_set_handler, text_view);
681     }
682
683   if (GTK_WIDGET_VISIBLE (text_view))    
684     gtk_widget_queue_draw (GTK_WIDGET (text_view));
685 }
686
687 GtkTextBuffer*
688 gtk_text_view_get_buffer (GtkTextView *text_view)
689 {
690   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
691   
692   return text_view->buffer;
693 }
694
695 void
696 gtk_text_view_get_iter_at_pixel (GtkTextView *text_view,
697                                  GtkTextIter *iter,
698                                  gint x, gint y)
699 {
700   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
701   g_return_if_fail (iter != NULL);
702   g_return_if_fail (text_view->layout != NULL);
703
704   gtk_text_layout_get_iter_at_pixel (text_view->layout,
705                                      iter,
706                                      x + text_view->xoffset,
707                                      y + text_view->yoffset);
708 }
709
710
711 static void
712 set_adjustment_clamped (GtkAdjustment *adj, gfloat val)
713 {
714   /* We don't really want to clamp to upper; we want to clamp to
715      upper - page_size which is the highest value the scrollbar
716      will let us reach. */
717   if (val > (adj->upper - adj->page_size))
718     val = adj->upper - adj->page_size;
719
720   if (val < adj->lower)
721     val = adj->lower;
722   
723   gtk_adjustment_set_value (adj, val);
724 }
725
726 gboolean
727 gtk_text_view_scroll_to_mark_adjusted (GtkTextView *text_view,
728                                        const gchar *mark_name,
729                                        gint margin,
730                                        gfloat percentage)
731 {
732   GtkTextIter iter;
733   GdkRectangle rect;
734   GdkRectangle screen;
735   gint screen_bottom;
736   gint screen_right;
737   GtkWidget *widget;
738   gboolean retval = FALSE;
739   gint scroll_inc;
740
741   gint current_x_scroll, current_y_scroll;
742   
743   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
744   g_return_val_if_fail (mark_name != NULL, FALSE);
745
746   widget = GTK_WIDGET (text_view);
747   
748   if (!GTK_WIDGET_MAPPED (widget))
749     {
750       g_warning ("FIXME need to implement scroll_to_mark for unmapped GtkTextView?");
751       return FALSE;
752     }
753   
754   if (!gtk_text_buffer_get_iter_at_mark (text_view->buffer, &iter, mark_name))
755     {
756       g_warning ("Mark %s does not exist! can't scroll to it.", mark_name);
757       return FALSE;
758     }
759   
760   gtk_text_layout_get_iter_location (text_view->layout,
761                                      &iter,
762                                      &rect);
763   
764   /* Be sure the scroll region is up-to-date */
765   gtk_text_view_scroll_calc_now (text_view);
766   
767   current_x_scroll = text_view->xoffset;
768   current_y_scroll = text_view->yoffset;
769
770   screen.x = current_x_scroll;
771   screen.y = current_y_scroll;
772   screen.width = widget->allocation.width;
773   screen.height = widget->allocation.height;
774
775   {
776     /* Clamp margin so it's not too large. */
777     gint small_dimension = MIN (screen.width, screen.height);
778     gint max_rect_dim;
779     
780     if (margin > (small_dimension/2 - 5)) /* 5 is arbitrary */
781       margin = (small_dimension/2 - 5);
782
783     if (margin < 0)
784       margin = 0;
785     
786     /* make sure rectangle fits in the leftover space */
787
788     max_rect_dim = MAX (rect.width, rect.height);
789     
790     if (max_rect_dim > (small_dimension - margin*2))
791       margin -= max_rect_dim - (small_dimension - margin*2);
792                  
793     if (margin < 0)
794       margin = 0;
795   }
796
797   g_assert (margin >= 0);
798   
799   screen.x += margin;
800   screen.y += margin;
801
802   screen.width -= margin*2;
803   screen.height -= margin*2;
804
805   screen_bottom = screen.y + screen.height;
806   screen_right = screen.x + screen.width;
807   
808   /* Vertical scroll (only vertical gets adjusted) */
809
810   scroll_inc = 0;
811   if (rect.y < screen.y)
812     {
813       gint scroll_dest = rect.y;
814       scroll_inc = (scroll_dest - screen.y) * percentage;
815     }
816   else if ((rect.y + rect.height) > screen_bottom)
817     {
818       gint scroll_dest = rect.y + rect.height;
819       scroll_inc = (scroll_dest - screen_bottom) * percentage;
820     }
821
822   if (scroll_inc != 0)
823     {
824       set_adjustment_clamped (text_view->vadjustment,
825                               current_y_scroll + scroll_inc);
826       retval = TRUE;
827     }
828   
829   /* Horizontal scroll */
830
831   scroll_inc = 0;
832   if (rect.x < screen.x)
833     {
834       gint scroll_dest = rect.x;
835       scroll_inc = scroll_dest - screen.x;
836     }
837   else if ((rect.x + rect.width) > screen_right)
838     {
839       gint scroll_dest = rect.x + rect.width;
840       scroll_inc = scroll_dest - screen_right;
841     }
842
843   if (scroll_inc != 0)
844     {
845       set_adjustment_clamped (text_view->hadjustment,
846                               current_x_scroll + scroll_inc);
847       retval = TRUE;
848     }
849
850   return retval;
851 }
852
853 gboolean
854 gtk_text_view_scroll_to_mark (GtkTextView *text_view,
855                           const gchar *mark_name,
856                           gint mark_within_margin)
857 {
858   g_return_val_if_fail (mark_within_margin >= 0, FALSE);
859   
860   return gtk_text_view_scroll_to_mark_adjusted (text_view, mark_name,
861                                                 mark_within_margin, 1.0);
862 }
863
864 static gboolean
865 clamp_iter_onscreen (GtkTextView *text_view, GtkTextIter *iter)
866 {
867   GdkRectangle visible_rect;
868   gtk_text_view_get_visible_rect (text_view, &visible_rect);
869
870   return gtk_text_layout_clamp_iter_to_vrange (text_view->layout, iter,
871                                                visible_rect.y,
872                                                visible_rect.y + visible_rect.height);
873 }
874
875 gboolean
876 gtk_text_view_move_mark_onscreen (GtkTextView *text_view,
877                                   const gchar *mark_name)
878 {
879   GtkTextIter mark;
880   
881   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
882   g_return_val_if_fail (mark_name != NULL, FALSE);
883   
884   if (!gtk_text_buffer_get_iter_at_mark (text_view->buffer, &mark, mark_name))
885     return FALSE;
886   
887   if (clamp_iter_onscreen (text_view, &mark))
888     {
889       gtk_text_buffer_move_mark (text_view->buffer, mark_name, &mark);
890       return TRUE;
891     }
892   else
893     return FALSE;
894 }
895
896 void
897 gtk_text_view_get_visible_rect (GtkTextView  *text_view,
898                                 GdkRectangle *visible_rect)
899 {
900   GtkWidget *widget;
901
902   g_return_if_fail (text_view != NULL);
903   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
904
905   widget = GTK_WIDGET (text_view);
906   
907   if (visible_rect)
908     {
909       visible_rect->x = text_view->xoffset;
910       visible_rect->y = text_view->yoffset;
911       visible_rect->width = widget->allocation.width;
912       visible_rect->height = widget->allocation.height;
913     }
914 }
915
916 void
917 gtk_text_view_set_wrap_mode (GtkTextView *text_view,
918                              GtkWrapMode  wrap_mode)
919 {
920   g_return_if_fail (text_view != NULL);
921   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
922
923   if (text_view->wrap_mode != wrap_mode)
924     {
925       text_view->wrap_mode = wrap_mode;
926
927       if (text_view->layout)
928         {
929           text_view->layout->default_style->wrap_mode = wrap_mode;
930           gtk_text_layout_default_style_changed (text_view->layout);
931         }
932     }
933 }
934
935 GtkWrapMode
936 gtk_text_view_get_wrap_mode (GtkTextView *text_view)
937 {
938   g_return_val_if_fail (text_view != NULL, GTK_WRAPMODE_NONE);
939   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_WRAPMODE_NONE);
940
941   return text_view->wrap_mode;
942 }
943
944 gboolean
945 gtk_text_view_place_cursor_onscreen (GtkTextView *text_view)
946 {
947   GtkTextIter insert;
948   
949   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
950   
951   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, "insert");
952   
953   if (clamp_iter_onscreen (text_view, &insert))
954     {
955       gtk_text_buffer_place_cursor (text_view->buffer, &insert);
956       return TRUE;
957     }
958   else
959     return FALSE;
960 }
961
962 static void
963 gtk_text_view_destroy (GtkObject *object)
964 {
965   GtkTextView *text_view;
966
967   text_view = GTK_TEXT_VIEW (object);
968
969   gtk_text_view_destroy_layout (text_view);
970   gtk_text_view_set_buffer (text_view, NULL);
971   
972   (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
973 }
974
975 static void
976 gtk_text_view_finalize (GObject *object)
977 {
978   GtkTextView *text_view;
979
980   text_view = GTK_TEXT_VIEW (object);  
981
982   gtk_object_unref (GTK_OBJECT (text_view->hadjustment));
983   gtk_object_unref (GTK_OBJECT (text_view->vadjustment));
984   gtk_object_unref (GTK_OBJECT (text_view->im_context));
985
986   (* G_OBJECT_CLASS (parent_class)->finalize) (object);
987 }
988
989 static void
990 gtk_text_view_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
991 {
992   GtkTextView *text_view;
993
994   text_view = GTK_TEXT_VIEW (object);
995
996   switch (arg_id)
997     {
998     case ARG_HEIGHT_LINES:
999       break;
1000
1001     case ARG_WIDTH_COLUMNS:
1002       break;
1003
1004     case ARG_PIXELS_ABOVE_LINES:
1005       break;
1006
1007     case ARG_PIXELS_BELOW_LINES:
1008       break;
1009
1010     case ARG_PIXELS_INSIDE_WRAP:
1011       break;
1012
1013     case ARG_EDITABLE:
1014       break;
1015
1016     case ARG_WRAP_MODE:
1017       break;
1018
1019     default:
1020       g_assert_not_reached ();
1021       break;
1022     }
1023 }
1024
1025 static void
1026 gtk_text_view_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
1027 {
1028   GtkTextView *text_view;
1029
1030   text_view = GTK_TEXT_VIEW (object);
1031
1032   switch (arg_id)
1033     {
1034     case ARG_HEIGHT_LINES:
1035       break;
1036
1037     case ARG_WIDTH_COLUMNS:
1038       break;
1039
1040     case ARG_PIXELS_ABOVE_LINES:
1041       break;
1042
1043     case ARG_PIXELS_BELOW_LINES:
1044       break;
1045
1046     case ARG_PIXELS_INSIDE_WRAP:
1047       break;
1048
1049     case ARG_EDITABLE:
1050       break;
1051
1052     case ARG_WRAP_MODE:
1053       break;
1054
1055     default:
1056       arg->type = GTK_TYPE_INVALID;
1057       break;
1058     }
1059 }
1060
1061 static void
1062 gtk_text_view_size_request (GtkWidget      *widget,
1063                             GtkRequisition *requisition)
1064 {
1065   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
1066   
1067   /* Hrm */
1068   requisition->width = 1;
1069   requisition->height = 1;
1070
1071   /* Check to see if the widget direction has changed */
1072
1073   if (text_view->layout)
1074     {
1075       GtkTextDirection direction = gtk_widget_get_direction (widget);
1076       if (direction != text_view->layout->default_style->direction)
1077         {
1078           text_view->layout->default_style->direction = direction;
1079           gtk_text_layout_default_style_changed (text_view->layout);      
1080         }
1081     }
1082 }
1083
1084 static void
1085 gtk_text_view_size_allocate (GtkWidget *widget,
1086                              GtkAllocation *allocation)
1087 {
1088   GtkTextView *text_view;
1089   GtkTextIter first_para;
1090   gint y;
1091   GtkAdjustment *vadj;
1092   gboolean yoffset_changed = FALSE;
1093
1094   text_view = GTK_TEXT_VIEW (widget);
1095   
1096   widget->allocation = *allocation;
1097   
1098   if (GTK_WIDGET_REALIZED (widget))
1099     {
1100       gdk_window_move_resize (widget->window,
1101                               allocation->x, allocation->y,
1102                               allocation->width, allocation->height);
1103
1104       gdk_window_resize (text_view->bin_window,
1105                          allocation->width, allocation->height);
1106     }
1107
1108   gtk_text_view_ensure_layout (text_view);
1109   gtk_text_layout_set_screen_width (text_view->layout,
1110                                     GTK_WIDGET (text_view)->allocation.width);
1111       
1112   gtk_text_view_validate_onscreen (text_view);
1113   gtk_text_view_scroll_calc_now (text_view);
1114
1115   /* Now adjust the value of the adjustment to keep the cursor at the same place in
1116    * the buffer 
1117   */
1118   gtk_text_view_get_first_para_iter (text_view, &first_para);
1119   y = gtk_text_layout_get_line_y (text_view->layout, &first_para) + text_view->first_para_pixels;
1120
1121   vadj = text_view->vadjustment;
1122   if (y > vadj->upper - vadj->page_size)
1123     y = MAX (0, vadj->upper - vadj->page_size);
1124
1125   if (y != text_view->yoffset)
1126     {
1127       vadj->value = text_view->yoffset = y;
1128       yoffset_changed = TRUE;
1129     }
1130
1131   text_view->hadjustment->page_size = allocation->width;
1132   text_view->hadjustment->page_increment = allocation->width / 2;
1133   text_view->hadjustment->lower = 0;
1134   text_view->hadjustment->upper = MAX (allocation->width, text_view->width);
1135   gtk_signal_emit_by_name (GTK_OBJECT (text_view->hadjustment), "changed");
1136
1137   text_view->vadjustment->page_size = allocation->height;
1138   text_view->vadjustment->page_increment = allocation->height / 2;
1139   text_view->vadjustment->lower = 0;
1140   text_view->vadjustment->upper = MAX (allocation->height, text_view->height);
1141   gtk_signal_emit_by_name (GTK_OBJECT (text_view->vadjustment), "changed");
1142
1143   if (yoffset_changed)
1144     gtk_adjustment_value_changed (vadj);
1145 }
1146
1147 static void
1148 gtk_text_view_get_first_para_iter (GtkTextView *text_view,
1149                                    GtkTextIter *iter)
1150 {
1151   gchar *mark_name = gtk_text_mark_get_name (text_view->first_para_mark);
1152   gtk_text_buffer_get_iter_at_mark (text_view->buffer, iter, mark_name);
1153   g_free (mark_name);
1154 }
1155
1156 static void
1157 gtk_text_view_validate_onscreen (GtkTextView *text_view)
1158 {
1159   GtkWidget *widget = GTK_WIDGET (text_view);
1160   
1161   if (widget->allocation.height > 0)
1162     {
1163       GtkTextIter first_para;
1164       gtk_text_view_get_first_para_iter (text_view, &first_para);
1165       gtk_text_layout_validate_yrange (text_view->layout,
1166                                        &first_para,
1167                                        0, text_view->first_para_pixels + widget->allocation.height);
1168     }
1169 }
1170
1171 static gboolean
1172 first_validate_callback (gpointer data)
1173 {
1174   GtkTextView *text_view = data;
1175
1176   gtk_text_view_validate_onscreen (text_view);
1177   
1178   text_view->first_validate_idle = 0;
1179   return FALSE;
1180 }
1181
1182 static gboolean
1183 incremental_validate_callback (gpointer data)
1184 {
1185   GtkTextView *text_view = data;
1186
1187   gtk_text_layout_validate (text_view->layout, 2000);
1188   if (gtk_text_layout_is_valid (text_view->layout))
1189     {
1190       text_view->incremental_validate_idle = 0;
1191       return FALSE;
1192     }
1193   else
1194     return TRUE;
1195 }
1196
1197 static void
1198 invalidated_handler (GtkTextLayout *layout,
1199                      gpointer       data)
1200 {
1201   GtkTextView *text_view;
1202
1203   text_view = GTK_TEXT_VIEW (data);
1204
1205   if (!text_view->first_validate_idle)
1206     text_view->first_validate_idle = g_idle_add_full (GTK_PRIORITY_RESIZE - 1, first_validate_callback, text_view, NULL);
1207
1208   if (!text_view->incremental_validate_idle)
1209     text_view->incremental_validate_idle = g_idle_add_full (GDK_PRIORITY_REDRAW + 1, incremental_validate_callback, text_view, NULL);
1210 }
1211
1212 static void
1213 changed_handler (GtkTextLayout *layout,
1214                  gint           start_y,
1215                  gint           old_height,
1216                  gint           new_height,
1217                  gpointer       data)
1218 {
1219   GtkTextView *text_view;
1220   GtkWidget *widget;
1221   GdkRectangle visible_rect;
1222   GdkRectangle redraw_rect;
1223   
1224   text_view = GTK_TEXT_VIEW (data);
1225   widget = GTK_WIDGET (data);
1226
1227   if (GTK_WIDGET_REALIZED (text_view))
1228     {
1229       gtk_text_view_get_visible_rect (text_view, &visible_rect);
1230       
1231       redraw_rect.x = visible_rect.x;
1232       redraw_rect.width = visible_rect.width;
1233       redraw_rect.y = start_y;
1234       redraw_rect.height = MAX (old_height, new_height);
1235       
1236       if (gdk_rectangle_intersect (&redraw_rect, &visible_rect, &redraw_rect))
1237         {
1238           redraw_rect.y -= text_view->yoffset;
1239           gdk_window_invalidate_rect (text_view->bin_window, &redraw_rect, FALSE);
1240         }
1241     }
1242
1243   if (old_height != new_height)
1244     {
1245       gboolean yoffset_changed = FALSE;
1246
1247       if (start_y + old_height <= text_view->yoffset - text_view->first_para_pixels)
1248         {
1249           text_view->yoffset += new_height - old_height;
1250           text_view->vadjustment->value = text_view->yoffset;
1251           yoffset_changed = TRUE;
1252         }
1253
1254       gtk_text_view_scroll_calc_now (text_view);
1255
1256       if (yoffset_changed)
1257         gtk_adjustment_value_changed (text_view->vadjustment);
1258         
1259     }
1260 }
1261
1262 static void
1263 gtk_text_view_realize (GtkWidget *widget)
1264 {
1265   GtkTextView *text_view;
1266   GdkCursor *cursor;
1267   GdkWindowAttr attributes;
1268   gint attributes_mask;
1269     
1270   text_view = GTK_TEXT_VIEW (widget);
1271   GTK_WIDGET_SET_FLAGS (text_view, GTK_REALIZED);
1272   
1273   attributes.window_type = GDK_WINDOW_CHILD;
1274   attributes.x = widget->allocation.x;
1275   attributes.y = widget->allocation.y;
1276   attributes.width = widget->allocation.width;
1277   attributes.height = widget->allocation.height;
1278   attributes.wclass = GDK_INPUT_OUTPUT;
1279   attributes.visual = gtk_widget_get_visual (widget);
1280   attributes.colormap = gtk_widget_get_colormap (widget);
1281   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1282
1283   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1284
1285   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1286                                    &attributes, attributes_mask);
1287   gdk_window_set_user_data (widget->window, widget);
1288
1289   attributes.x = 0;
1290   attributes.y = 0;
1291   attributes.width = widget->allocation.width;
1292   attributes.height = widget->allocation.height;
1293   attributes.event_mask = (GDK_EXPOSURE_MASK            |
1294                            GDK_SCROLL_MASK              |
1295                            GDK_KEY_PRESS_MASK           |
1296                            GDK_BUTTON_PRESS_MASK        |
1297                            GDK_BUTTON_RELEASE_MASK      |
1298                            GDK_POINTER_MOTION_MASK      |
1299                            GDK_POINTER_MOTION_HINT_MASK |
1300                            gtk_widget_get_events (widget));
1301
1302   text_view->bin_window = gdk_window_new (widget->window,
1303                                         &attributes, attributes_mask);
1304   gdk_window_show (text_view->bin_window);
1305   gdk_window_set_user_data (text_view->bin_window, widget);
1306
1307   widget->style = gtk_style_attach (widget->style, widget->window);
1308
1309   gdk_window_set_background (text_view->bin_window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1310
1311   gtk_text_view_ensure_layout (text_view);
1312
1313   /* I-beam cursor */
1314   cursor = gdk_cursor_new (GDK_XTERM);
1315   gdk_window_set_cursor (text_view->bin_window, cursor);
1316   gdk_cursor_destroy (cursor);
1317
1318   gtk_im_context_set_client_window (text_view->im_context, widget->window);
1319 }
1320
1321 static void
1322 gtk_text_view_unrealize (GtkWidget *widget)
1323 {
1324   GtkTextView *text_view;
1325
1326   text_view = GTK_TEXT_VIEW (widget);
1327
1328   if (text_view->first_validate_idle)
1329     {
1330       g_source_remove (text_view->first_validate_idle);
1331       text_view->first_validate_idle = 0;
1332     }
1333     
1334   if (text_view->incremental_validate_idle)
1335     {
1336       g_source_remove (text_view->incremental_validate_idle);
1337       text_view->incremental_validate_idle = 0;
1338     }
1339     
1340   gtk_text_view_destroy_layout (text_view);
1341   
1342   gtk_im_context_set_client_window (text_view->im_context, NULL);
1343
1344   gdk_window_set_user_data (text_view->bin_window, NULL);
1345   gdk_window_destroy (text_view->bin_window);
1346   text_view->bin_window = NULL;
1347
1348   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1349 }
1350
1351 static void 
1352 gtk_text_view_style_set (GtkWidget *widget,
1353                          GtkStyle  *previous_style)
1354 {
1355   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
1356
1357   if (GTK_WIDGET_REALIZED (widget))
1358     {
1359       gdk_window_set_background (text_view->bin_window,
1360                                  &widget->style->base[GTK_WIDGET_STATE (widget)]);
1361
1362       gtk_text_view_set_values_from_style (text_view, text_view->layout->default_style, widget->style);
1363       gtk_text_layout_default_style_changed (text_view->layout);
1364     }
1365 }
1366
1367 /*
1368  * Events
1369  */
1370
1371 static gboolean
1372 get_event_coordinates (GdkEvent *event, gint *x, gint *y)
1373 {
1374   if (event)
1375     switch (event->type)
1376       {
1377       case GDK_MOTION_NOTIFY:
1378         *x = event->motion.x;
1379         *y = event->motion.y;
1380         return TRUE;
1381         break;
1382         
1383       case GDK_BUTTON_PRESS:
1384       case GDK_2BUTTON_PRESS:
1385       case GDK_3BUTTON_PRESS:
1386       case GDK_BUTTON_RELEASE:
1387         *x = event->button.x;
1388         *y = event->button.y;
1389         return TRUE;
1390         break;
1391         
1392       case GDK_KEY_PRESS:
1393       case GDK_KEY_RELEASE:
1394       case GDK_ENTER_NOTIFY:
1395       case GDK_LEAVE_NOTIFY:
1396       case GDK_PROPERTY_NOTIFY:
1397       case GDK_SELECTION_CLEAR:
1398       case GDK_SELECTION_REQUEST:
1399       case GDK_SELECTION_NOTIFY:
1400       case GDK_PROXIMITY_IN:
1401       case GDK_PROXIMITY_OUT:
1402       case GDK_DRAG_ENTER:
1403       case GDK_DRAG_LEAVE:
1404       case GDK_DRAG_MOTION:
1405       case GDK_DRAG_STATUS:
1406       case GDK_DROP_START:
1407       case GDK_DROP_FINISHED:
1408       default:
1409         return FALSE;
1410         break;
1411       }
1412
1413   return FALSE;
1414 }
1415
1416 static gint
1417 gtk_text_view_event (GtkWidget *widget, GdkEvent *event)
1418 {
1419   GtkTextView *text_view;
1420   gint x = 0, y = 0;
1421   
1422   text_view = GTK_TEXT_VIEW (widget);
1423   
1424   if (text_view->layout == NULL ||
1425       text_view->buffer == NULL)
1426     return FALSE;
1427
1428   /* FIXME eventually we really want to synthesize enter/leave
1429      events here as the canvas does for canvas items */
1430   
1431   if (get_event_coordinates (event, &x, &y))
1432     {
1433       GtkTextIter iter;
1434       gint retval = FALSE;
1435
1436       x += text_view->xoffset;
1437       y += text_view->yoffset;
1438
1439       /* FIXME this is slow and we do it twice per event.
1440          My favorite solution is to have GtkTextLayout cache
1441          the last couple lookups. */
1442       gtk_text_layout_get_iter_at_pixel (text_view->layout,
1443                                          &iter,
1444                                          x, y);
1445
1446         {
1447           GSList *tags;
1448           GSList *tmp;
1449           
1450           tags = gtk_text_buffer_get_tags (text_view->buffer, &iter);
1451           
1452           tmp = tags;
1453           while (tmp != NULL)
1454             {
1455               GtkTextTag *tag = tmp->data;
1456
1457               if (gtk_text_tag_event (tag, GTK_OBJECT (widget), event, &iter))
1458                 {
1459                   retval = TRUE;
1460                   break;
1461                 }
1462
1463               tmp = g_slist_next (tmp);
1464             }
1465
1466           g_slist_free (tags);
1467         }
1468         
1469       return retval;
1470     }
1471
1472   return FALSE;
1473 }
1474
1475 static gint
1476 gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
1477 {
1478   GtkTextView *text_view;
1479
1480   text_view = GTK_TEXT_VIEW (widget);
1481
1482   if (text_view->layout == NULL ||
1483       text_view->buffer == NULL)
1484     return FALSE;
1485
1486   if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
1487       GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1488     return TRUE;
1489
1490   if (gtk_im_context_filter_keypress (text_view->im_context, event))
1491     return TRUE;
1492   else if (event->keyval == GDK_Return)
1493     {
1494       gtk_text_buffer_insert_at_cursor (text_view->buffer, "\n", 1);
1495       gtk_text_view_scroll_to_mark (text_view, "insert", 0);
1496       return TRUE;
1497     }
1498   else
1499     return FALSE;
1500 }
1501
1502 static gint
1503 gtk_text_view_key_release_event (GtkWidget *widget, GdkEventKey *event)
1504 {
1505   return FALSE;
1506 }
1507
1508 static gint
1509 gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
1510 {
1511   GtkTextView *text_view;
1512
1513   text_view = GTK_TEXT_VIEW (widget);
1514   
1515   gtk_widget_grab_focus (widget);
1516
1517   /* debug hack */
1518   if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0)
1519     gtk_text_buffer_spew (GTK_TEXT_VIEW (widget)->buffer);
1520   else if (event->button == 3)
1521     gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout);
1522
1523   if (event->type == GDK_BUTTON_PRESS)
1524     {
1525       if (event->button == 1)
1526         {
1527           /* If we're in the selection, start a drag copy/move of the
1528              selection; otherwise, start creating a new selection. */
1529           GtkTextIter iter;
1530           GtkTextIter start, end;
1531
1532           gtk_text_layout_get_iter_at_pixel (text_view->layout,
1533                                              &iter,
1534                                              event->x + text_view->xoffset,
1535                                              event->y + text_view->yoffset);
1536           
1537           if (gtk_text_buffer_get_selection_bounds (text_view->buffer,
1538                                                     &start, &end) &&
1539               gtk_text_iter_in_region (&iter, &start, &end))
1540             {
1541               gtk_text_view_start_selection_dnd (text_view, &iter, event);
1542             }
1543           else
1544             {
1545               gtk_text_view_start_selection_drag (text_view, &iter, event);
1546             }
1547           
1548           return TRUE;
1549         }
1550       else if (event->button == 2)
1551         {
1552           GtkTextIter iter;
1553
1554           gtk_text_layout_get_iter_at_pixel (text_view->layout,
1555                                              &iter,
1556                                              event->x + text_view->xoffset,
1557                                              event->y + text_view->yoffset);
1558           
1559           gtk_text_buffer_paste_primary_selection (text_view->buffer,
1560                                                    &iter,
1561                                                    event->time);
1562           return TRUE;
1563         }
1564       else if (event->button == 3)
1565         {
1566           if (gtk_text_view_end_selection_drag (text_view, event))
1567             return TRUE;
1568           else
1569             return FALSE;
1570         }
1571     }
1572   
1573   return FALSE;
1574 }
1575
1576 static gint
1577 gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
1578 {
1579   if (event->button == 1)
1580     {
1581       gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget), event);
1582       return TRUE;
1583     }
1584
1585   return FALSE;
1586 }
1587
1588 static gint
1589 gtk_text_view_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
1590 {
1591   GtkTextMark *insert;
1592   
1593   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1594
1595   insert = gtk_text_buffer_get_mark (GTK_TEXT_VIEW (widget)->buffer,
1596                                      "insert");
1597   gtk_text_mark_set_visible (insert, TRUE);
1598
1599   gtk_text_view_start_cursor_blink (GTK_TEXT_VIEW (widget));
1600
1601   gtk_im_context_focus_in (GTK_TEXT_VIEW (widget)->im_context);
1602   
1603   return FALSE;
1604 }
1605
1606 static gint
1607 gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
1608 {
1609   GtkTextMark *insert;
1610   
1611   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1612
1613   insert = gtk_text_buffer_get_mark (GTK_TEXT_VIEW (widget)->buffer,
1614                                      "insert");
1615   gtk_text_mark_set_visible (insert, FALSE);
1616
1617   gtk_text_view_stop_cursor_blink (GTK_TEXT_VIEW (widget));
1618
1619   gtk_im_context_focus_out (GTK_TEXT_VIEW (widget)->im_context);
1620   
1621   return FALSE;
1622 }
1623
1624 static gint
1625 gtk_text_view_motion_event (GtkWidget *widget, GdkEventMotion *event)
1626 {
1627   
1628   return FALSE;
1629 }
1630
1631 static void
1632 gtk_text_view_paint (GtkWidget *widget, GdkRectangle *area)
1633 {
1634   GtkTextView *text_view;
1635   
1636   text_view = GTK_TEXT_VIEW (widget);
1637
1638   g_return_if_fail (text_view->layout != NULL);
1639   g_return_if_fail (text_view->xoffset >= 0);
1640   g_return_if_fail (text_view->yoffset >= 0);
1641
1642   gtk_text_view_validate_onscreen (text_view);
1643
1644 #if 0
1645   printf ("painting %d,%d  %d x %d\n",
1646          area->x, area->y,
1647          area->width, area->height);
1648 #endif  
1649
1650   gtk_text_layout_draw (text_view->layout,
1651                         widget,
1652                         text_view->bin_window,
1653                         text_view->xoffset, text_view->yoffset,
1654                         area->x, area->y,
1655                         area->width, area->height);
1656 }
1657
1658 static void
1659 gtk_text_view_draw (GtkWidget *widget, GdkRectangle *area)
1660 {  
1661   gtk_text_view_paint (widget, area);
1662 }
1663
1664 static gint
1665 gtk_text_view_expose_event (GtkWidget *widget, GdkEventExpose *event)
1666 {
1667   if (event->window == GTK_TEXT_VIEW (widget)->bin_window)
1668     gtk_text_view_paint (widget, &event->area);
1669   
1670   return TRUE;
1671 }
1672
1673 /*
1674  * Blink!
1675  */
1676
1677 static gint
1678 blink_cb (gpointer data)
1679 {
1680   GtkTextView *text_view;
1681   GtkTextMark *insert;
1682   
1683   text_view = GTK_TEXT_VIEW (data);
1684
1685   insert = gtk_text_buffer_get_mark (text_view->buffer,
1686                                      "insert");
1687   
1688   if (!GTK_WIDGET_HAS_FOCUS (text_view))
1689     {
1690       /* paranoia, in case the user somehow mangles our
1691          focus_in/focus_out pairing. */
1692       gtk_text_mark_set_visible (insert, FALSE);
1693       text_view->blink_timeout = 0;
1694       return FALSE;
1695     }
1696   else
1697     {
1698       gtk_text_mark_set_visible (insert,
1699                                  !gtk_text_mark_is_visible (insert));
1700       return TRUE;
1701     }
1702 }
1703
1704 static void
1705 gtk_text_view_start_cursor_blink (GtkTextView *text_view)
1706 {
1707   return;
1708   if (text_view->blink_timeout != 0)
1709     return;
1710
1711   text_view->blink_timeout = gtk_timeout_add (500, blink_cb, text_view);
1712 }
1713
1714 static void
1715 gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
1716 {
1717   return;
1718   if (text_view->blink_timeout == 0)
1719     return;
1720
1721   gtk_timeout_remove (text_view->blink_timeout);
1722   text_view->blink_timeout = 0;
1723 }
1724
1725 /*
1726  * Key binding handlers
1727  */
1728
1729 static void
1730 gtk_text_view_move_iter_by_lines (GtkTextView *text_view,
1731                                   GtkTextIter *newplace,
1732                                   gint         count)
1733 {
1734   while (count < 0)
1735     {
1736       gtk_text_layout_move_iter_to_previous_line (text_view->layout, newplace);
1737       count++;
1738     }
1739
1740   while (count > 0)
1741     {
1742       gtk_text_layout_move_iter_to_next_line (text_view->layout, newplace);
1743       count--;
1744     }
1745 }
1746
1747 static void
1748 gtk_text_view_move_insert (GtkTextView *text_view,
1749                            GtkTextViewMovementStep step,
1750                            gint count,
1751                            gboolean extend_selection)
1752 {
1753   GtkTextIter insert;
1754   GtkTextIter newplace;
1755   
1756   gint cursor_x_pos = 0;
1757
1758   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, "insert");
1759   newplace = insert;
1760
1761   if (step == GTK_TEXT_MOVEMENT_LINE)
1762     gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, NULL);
1763
1764   switch (step)
1765     {
1766     case GTK_TEXT_MOVEMENT_CHAR:
1767       gtk_text_iter_forward_chars (&newplace, count);
1768       break;
1769
1770     case GTK_TEXT_MOVEMENT_POSITIONS:
1771       gtk_text_layout_move_iter_visually (text_view->layout,
1772                                           &newplace, count);
1773       break;
1774
1775     case GTK_TEXT_MOVEMENT_WORD:
1776       if (count < 0)
1777         gtk_text_iter_backward_word_starts (&newplace, -count);
1778       else if (count > 0)
1779         gtk_text_iter_forward_word_ends (&newplace, count);
1780       break;
1781
1782     case GTK_TEXT_MOVEMENT_LINE:
1783       gtk_text_view_move_iter_by_lines (text_view, &newplace, count);
1784       gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
1785       break;
1786       
1787     case GTK_TEXT_MOVEMENT_PARAGRAPH:
1788       /* This should almost certainly instead be doing the parallel thing to WORD */
1789       gtk_text_iter_down_lines (&newplace, count);
1790       break;
1791       
1792     case GTK_TEXT_MOVEMENT_PARAGRAPH_ENDS:
1793       if (count > 0)
1794         gtk_text_iter_forward_to_newline (&newplace);
1795       else if (count < 0)
1796         gtk_text_iter_set_line_char (&newplace, 0);
1797       break;
1798       
1799     case GTK_TEXT_MOVEMENT_BUFFER_ENDS:
1800       if (count > 0)
1801         gtk_text_buffer_get_last_iter (text_view->buffer, &newplace);
1802       else if (count < 0)
1803         gtk_text_buffer_get_iter_at_char (text_view->buffer, &newplace, 0);
1804       break;
1805       
1806     default:
1807       break;
1808     }
1809   
1810   if (!gtk_text_iter_equal (&insert, &newplace))
1811     {
1812       if (extend_selection)
1813         gtk_text_buffer_move_mark (text_view->buffer, "insert", &newplace);
1814       else
1815         gtk_text_buffer_place_cursor (text_view->buffer, &newplace);
1816       
1817       gtk_text_view_scroll_to_mark (text_view, "insert", 0);
1818
1819       if (step == GTK_TEXT_MOVEMENT_LINE)
1820         {
1821           gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
1822         }
1823     }
1824 }
1825
1826 static void
1827 gtk_text_view_set_anchor (GtkTextView *text_view)
1828 {
1829   GtkTextIter insert;
1830   
1831   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, "insert");
1832
1833   gtk_text_buffer_create_mark (text_view->buffer, "anchor", &insert, TRUE);
1834 }
1835
1836 static void
1837 gtk_text_view_scroll_text (GtkTextView *text_view,
1838                            GtkTextViewScrollType type)
1839 {
1840   gfloat newval;
1841   GtkAdjustment *adj;
1842   gint cursor_x_pos, cursor_y_pos;
1843   GtkTextIter new_insert;
1844   GtkTextIter anchor;
1845   gint y0, y1;
1846   
1847   g_return_if_fail (text_view->vadjustment != NULL);
1848
1849   adj = text_view->vadjustment;
1850
1851   /* Validate the region that will be brought into view by the cursor motion
1852    */
1853   switch (type)
1854     {
1855     default:
1856     case GTK_TEXT_SCROLL_TO_TOP:
1857       gtk_text_buffer_get_iter_at_char (text_view->buffer, &anchor, 0);
1858       y0 = 0;
1859       y1 = adj->page_size;
1860       break;
1861
1862     case GTK_TEXT_SCROLL_TO_BOTTOM:
1863       gtk_text_buffer_get_last_iter (text_view->buffer, &anchor);
1864       y0 = -adj->page_size;
1865       y1 = adj->page_size;
1866       break;
1867
1868     case GTK_TEXT_SCROLL_PAGE_DOWN:
1869       gtk_text_view_get_first_para_iter (text_view, &anchor);
1870       y0 = adj->page_size;
1871       y1 = adj->page_size + adj->page_increment;
1872       break;
1873
1874     case GTK_TEXT_SCROLL_PAGE_UP:
1875       gtk_text_view_get_first_para_iter (text_view, &anchor);
1876       y0 = - adj->page_increment + adj->page_size;
1877       y1 = 0;
1878       break;
1879     }
1880   gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1);
1881
1882
1883   gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
1884
1885   newval = adj->value;
1886   switch (type)
1887     {
1888     case GTK_TEXT_SCROLL_TO_TOP:
1889       newval = adj->lower;
1890       break;
1891
1892     case GTK_TEXT_SCROLL_TO_BOTTOM:
1893       newval = adj->upper;
1894       break;
1895
1896     case GTK_TEXT_SCROLL_PAGE_DOWN:
1897       newval += adj->page_increment;
1898       break;
1899
1900     case GTK_TEXT_SCROLL_PAGE_UP:
1901       newval -= adj->page_increment;
1902       break;
1903       
1904     default:
1905       break;
1906     }
1907
1908   cursor_y_pos += newval - adj->value;
1909   set_adjustment_clamped (adj, newval);
1910
1911   gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos);
1912   clamp_iter_onscreen (text_view, &new_insert);
1913   gtk_text_buffer_place_cursor (text_view->buffer, &new_insert);
1914
1915   gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
1916
1917   /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
1918    * only guarantees 1 pixel onscreen.
1919    */
1920   gtk_text_view_scroll_to_mark (text_view, "insert", 0);
1921 }
1922
1923 static gboolean
1924 whitespace (GtkTextUniChar ch, gpointer user_data)
1925 {
1926   return (ch == ' ' || ch == '\t');
1927 }
1928
1929 static gboolean
1930 not_whitespace (GtkTextUniChar ch, gpointer user_data)
1931 {
1932   return !whitespace (ch, user_data);
1933 }
1934
1935 static gboolean
1936 find_whitepace_region (const GtkTextIter *center,
1937                       GtkTextIter *start, GtkTextIter *end)
1938 {
1939   *start = *center;
1940   *end = *center;
1941
1942   if (gtk_text_iter_backward_find_char (start, not_whitespace, NULL))
1943     gtk_text_iter_forward_char (start); /* we want the first whitespace... */
1944   if (whitespace (gtk_text_iter_get_char (end), NULL))
1945     gtk_text_iter_forward_find_char (end, not_whitespace, NULL);
1946   
1947   return !gtk_text_iter_equal (start, end);
1948 }
1949
1950 static void
1951 gtk_text_view_delete_text (GtkTextView *text_view,
1952                        GtkTextViewDeleteType type,
1953                        gint count)
1954 {
1955   GtkTextIter insert;
1956   GtkTextIter start;
1957   GtkTextIter end;
1958   gboolean leave_one = FALSE;
1959   
1960   if (type == GTK_TEXT_DELETE_CHAR)
1961     {
1962       /* Char delete deletes the selection, if one exists */
1963       if (gtk_text_buffer_delete_selection (text_view->buffer))
1964         return;
1965     }
1966   
1967   gtk_text_buffer_get_iter_at_mark (text_view->buffer,
1968                                     &insert,
1969                                     "insert");
1970
1971   start = insert;
1972   end = insert;
1973   
1974   switch (type)
1975     {
1976     case GTK_TEXT_DELETE_CHAR:
1977       gtk_text_iter_forward_chars (&end, count);
1978       break;
1979       
1980     case GTK_TEXT_DELETE_HALF_WORD:
1981       if (count > 0)
1982         gtk_text_iter_forward_word_ends (&end, count);
1983       else if (count < 0)
1984         gtk_text_iter_backward_word_starts (&start, 0 - count);
1985       break;
1986       
1987     case GTK_TEXT_DELETE_WHOLE_WORD:
1988       break;
1989       
1990     case GTK_TEXT_DELETE_HALF_LINE:
1991       break;
1992
1993     case GTK_TEXT_DELETE_WHOLE_LINE:
1994       break;
1995
1996     case GTK_TEXT_DELETE_HALF_PARAGRAPH:
1997       while (count > 0)
1998         {
1999           if (!gtk_text_iter_forward_to_newline (&end))
2000             break;
2001
2002           --count;
2003         }
2004
2005       /* FIXME figure out what a negative count means
2006          and support that */
2007       break;
2008       
2009     case GTK_TEXT_DELETE_WHOLE_PARAGRAPH:
2010       if (count > 0)
2011         {
2012           gtk_text_iter_set_line_char (&start, 0);
2013           gtk_text_iter_forward_to_newline (&end);
2014
2015           /* Do the lines beyond the first. */
2016           while (count > 1)
2017             {
2018               gtk_text_iter_forward_to_newline (&end);
2019               
2020               --count;
2021             }
2022         }
2023
2024       /* FIXME negative count? */
2025       
2026       break;
2027
2028     case GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE:
2029       leave_one = TRUE; /* FALL THRU */
2030     case GTK_TEXT_DELETE_WHITESPACE:
2031       {
2032         find_whitepace_region (&insert, &start, &end);
2033       }
2034       break;
2035       
2036     default:
2037       break;
2038     }
2039
2040   if (!gtk_text_iter_equal (&start, &end))
2041     {
2042       gtk_text_buffer_delete (text_view->buffer, &start, &end);
2043       
2044       if (leave_one)
2045         gtk_text_buffer_insert_at_cursor (text_view->buffer, " ", 1);
2046       
2047       gtk_text_view_scroll_to_mark (text_view, "insert", 0);
2048     }
2049 }
2050
2051 static void
2052 gtk_text_view_cut_text (GtkTextView *text_view)
2053 {
2054   gtk_text_buffer_cut (text_view->buffer, GDK_CURRENT_TIME);
2055   gtk_text_view_scroll_to_mark (text_view, "insert", 0);
2056 }
2057
2058 static void
2059 gtk_text_view_copy_text (GtkTextView *text_view)
2060 {
2061   gtk_text_buffer_copy (text_view->buffer, GDK_CURRENT_TIME);
2062   gtk_text_view_scroll_to_mark (text_view, "insert", 0);
2063 }
2064
2065 static void
2066 gtk_text_view_paste_text (GtkTextView *text_view)
2067 {
2068   gtk_text_buffer_paste_clipboard (text_view->buffer, GDK_CURRENT_TIME);
2069   gtk_text_view_scroll_to_mark (text_view, "insert", 0);
2070 }
2071
2072 static void
2073 gtk_text_view_toggle_overwrite (GtkTextView *text_view)
2074 {
2075   text_view->overwrite_mode = !text_view->overwrite_mode;
2076 }
2077
2078 /*
2079  * Selections
2080  */
2081
2082 static gboolean
2083 move_insert_to_pointer_and_scroll (GtkTextView *text_view, gboolean partial_scroll)
2084 {
2085   gint x, y;
2086   GdkModifierType state;
2087   GtkTextIter newplace;
2088   gint adjust = 0;
2089   gboolean in_threshold = FALSE;
2090   
2091   gdk_window_get_pointer (text_view->bin_window, &x, &y, &state);
2092
2093   /* Adjust movement by how long we've been selecting, to
2094      get an acceleration effect. The exact numbers are
2095      pretty arbitrary. We have a threshold before we
2096      start to accelerate. */
2097   /* uncommenting this printf helps visualize how it works. */     
2098   /*   printf ("%d\n", text_view->scrolling_accel_factor); */
2099        
2100   if (text_view->scrolling_accel_factor > 10)
2101     adjust = (text_view->scrolling_accel_factor - 10) * 75;
2102   
2103   if (y < 0) /* scrolling upward */
2104     adjust = -adjust; 
2105
2106   /* No adjust if the pointer has moved back inside the window for sure.
2107      Also I'm adding a small threshold where no adjust is added,
2108      in case you want to do a continuous slow scroll. */
2109 #define SLOW_SCROLL_TH 7
2110   if (x >= (0 - SLOW_SCROLL_TH) &&
2111       x < (GTK_WIDGET (text_view)->allocation.width + SLOW_SCROLL_TH) &&
2112       y >= (0 - SLOW_SCROLL_TH) &&
2113       y < (GTK_WIDGET (text_view)->allocation.height + SLOW_SCROLL_TH))
2114     {
2115       adjust = 0;
2116       in_threshold = TRUE;
2117     }
2118   
2119   gtk_text_layout_get_iter_at_pixel (text_view->layout,
2120                                      &newplace,
2121                                      x + text_view->xoffset,
2122                                      y + text_view->yoffset + adjust);
2123   
2124   {
2125       gboolean scrolled = FALSE;
2126
2127       gtk_text_buffer_move_mark (text_view->buffer,
2128                                  "insert",
2129                                  &newplace);
2130       
2131       if (partial_scroll)
2132         scrolled = gtk_text_view_scroll_to_mark_adjusted (text_view, "insert", 0, 0.7);
2133       else
2134         scrolled = gtk_text_view_scroll_to_mark_adjusted (text_view, "insert", 0, 1.0);
2135
2136       if (scrolled)
2137         {
2138           /* We want to avoid rapid jump to super-accelerated when you
2139              leave the slow scroll threshold after scrolling for a
2140              while. So we slowly decrease accel when scrolling inside
2141              the threshold.
2142           */
2143           if (in_threshold)
2144             {
2145               if (text_view->scrolling_accel_factor > 1)
2146                 text_view->scrolling_accel_factor -= 2;
2147             }
2148           else
2149             text_view->scrolling_accel_factor += 1;
2150         }
2151       else
2152         {
2153           /* If we don't scroll we're probably inside the window, but
2154              potentially just a bit outside. We decrease acceleration
2155              while the user is fooling around inside the window.
2156              Acceleration decreases faster than it increases. */
2157           if (text_view->scrolling_accel_factor > 4)
2158             text_view->scrolling_accel_factor -= 5;
2159         }
2160       
2161       return scrolled;
2162   }
2163 }
2164
2165 static gint
2166 selection_scan_timeout (gpointer data)
2167 {
2168   GtkTextView *text_view;
2169
2170   text_view = GTK_TEXT_VIEW (data);
2171
2172   if (move_insert_to_pointer_and_scroll (text_view, TRUE))
2173     {
2174       return TRUE; /* remain installed. */
2175     }
2176   else
2177     {
2178       text_view->selection_drag_scan_timeout = 0;
2179       return FALSE; /* remove ourselves */
2180     }
2181 }
2182
2183 static gint
2184 selection_motion_event_handler (GtkTextView *text_view, GdkEventMotion *event, gpointer data)
2185 {
2186   if (move_insert_to_pointer_and_scroll (text_view, TRUE))
2187     {
2188       /* If we had to scroll offscreen, insert a timeout to do so
2189          again. Note that in the timeout, even if the mouse doesn't
2190          move, due to this scroll xoffset/yoffset will have changed
2191          and we'll need to scroll again. */
2192       if (text_view->selection_drag_scan_timeout != 0) /* reset on every motion event */
2193         gtk_timeout_remove (text_view->selection_drag_scan_timeout);
2194       
2195       text_view->selection_drag_scan_timeout =
2196         gtk_timeout_add (50, selection_scan_timeout, text_view);
2197     }
2198   
2199   return TRUE;
2200 }
2201
2202 static void
2203 gtk_text_view_start_selection_drag (GtkTextView *text_view,
2204                                const GtkTextIter *iter,
2205                                GdkEventButton *event)
2206 {
2207   GtkTextIter newplace;
2208
2209   g_return_if_fail (text_view->selection_drag_handler == 0);
2210   
2211   gtk_grab_add (GTK_WIDGET (text_view));
2212
2213   text_view->scrolling_accel_factor = 0;
2214
2215   newplace = *iter;
2216   
2217   gtk_text_buffer_place_cursor (text_view->buffer, &newplace);
2218
2219   text_view->selection_drag_handler = gtk_signal_connect (GTK_OBJECT (text_view),
2220                                                           "motion_notify_event",
2221                                                           GTK_SIGNAL_FUNC (selection_motion_event_handler),
2222                                                           NULL);
2223 }
2224
2225 /* returns whether we were really dragging */
2226 static gboolean
2227 gtk_text_view_end_selection_drag (GtkTextView *text_view, GdkEventButton *event)
2228 {
2229   if (text_view->selection_drag_handler == 0)
2230     return FALSE;
2231
2232   gtk_signal_disconnect (GTK_OBJECT (text_view), text_view->selection_drag_handler);
2233   text_view->selection_drag_handler = 0;
2234
2235   text_view->scrolling_accel_factor = 0;
2236   
2237   if (text_view->selection_drag_scan_timeout != 0)
2238     {
2239       gtk_timeout_remove (text_view->selection_drag_scan_timeout);
2240       text_view->selection_drag_scan_timeout = 0;
2241     }
2242
2243   /* one last update to current position */
2244   move_insert_to_pointer_and_scroll (text_view, FALSE);
2245   
2246   gtk_grab_remove (GTK_WIDGET (text_view));
2247   
2248   return TRUE;
2249 }
2250
2251 /*
2252  * Layout utils
2253  */
2254
2255 static void
2256 gtk_text_view_set_adjustment_upper (GtkAdjustment *adj, gfloat upper)
2257 {
2258   if (upper != adj->upper)
2259     {
2260       gfloat min = MAX (0., upper - adj->page_size);
2261       gboolean value_changed = FALSE;
2262       
2263       adj->upper = upper;
2264       
2265       if (adj->value > min)
2266         {
2267           adj->value = min;
2268           value_changed = TRUE;
2269         }
2270       
2271       gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed");
2272       if (value_changed)
2273         gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
2274     }
2275 }
2276
2277 static void
2278 gtk_text_view_scroll_calc_now (GtkTextView *text_view)
2279 {
2280   gint width = 0, height = 0;
2281   GtkWidget *widget = GTK_WIDGET (text_view);
2282   
2283   gtk_text_view_ensure_layout (text_view);
2284
2285       
2286   gtk_text_layout_set_screen_width (text_view->layout,
2287                                     widget->allocation.width);
2288       
2289   gtk_text_layout_get_size (text_view->layout, &width, &height);
2290
2291 #if 0
2292   /* If the width is less than the screen width (likely
2293      if we have wrapping turned on for the whole widget),
2294      then we want to set the scroll region to the screen
2295      width. If the width is greater (wrapping off) then we
2296      probably want to set the scroll region to the width
2297      of the layout. I guess.
2298   */
2299
2300   width = MAX (text_view->layout->screen_width, width);
2301   height = height;
2302 #endif
2303
2304   if (text_view->width != width || text_view->height != height)
2305     {
2306 #if 0
2307       printf ("layout size set, widget width is %d\n",
2308               GTK_WIDGET (text_view)->allocation.width);
2309 #endif
2310       text_view->width = width;
2311       text_view->height = height;
2312       
2313       gtk_text_view_set_adjustment_upper (text_view->hadjustment,
2314                                           MAX (widget->allocation.width, width));
2315       gtk_text_view_set_adjustment_upper (text_view->vadjustment, 
2316                                           MAX (widget->allocation.height, height));
2317
2318       /* Set up the step sizes; we'll say that a page is
2319          our allocation minus one step, and a step is
2320          1/10 of our allocation. */
2321       text_view->hadjustment->step_increment =
2322         GTK_WIDGET (text_view)->allocation.width/10.0;
2323       text_view->hadjustment->page_increment =
2324         GTK_WIDGET (text_view)->allocation.width  *0.9;
2325
2326       text_view->vadjustment->step_increment =
2327         GTK_WIDGET (text_view)->allocation.height/10.0;
2328       text_view->vadjustment->page_increment =
2329         GTK_WIDGET (text_view)->allocation.height  *0.9;
2330     } 
2331 }
2332
2333 static void
2334 gtk_text_view_set_values_from_style (GtkTextView        *text_view,
2335                                      GtkTextStyleValues *values,
2336                                      GtkStyle           *style)
2337 {
2338   values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
2339   values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];
2340       
2341   if (values->font_desc)
2342     pango_font_description_free (values->font_desc);
2343
2344   values->font_desc = pango_font_description_copy (style->font_desc);
2345 }
2346
2347 static void
2348 gtk_text_view_ensure_layout (GtkTextView *text_view)
2349 {
2350   GtkWidget *widget;
2351
2352   widget = GTK_WIDGET (text_view);
2353   
2354   if (text_view->layout == NULL)
2355     {
2356       GtkTextStyleValues *style;
2357       PangoContext *ltr_context, *rtl_context;
2358       
2359       text_view->layout = gtk_text_layout_new ();
2360
2361       gtk_signal_connect (GTK_OBJECT (text_view->layout),
2362                           "invalidated",
2363                           GTK_SIGNAL_FUNC (invalidated_handler),
2364                           text_view);
2365
2366       gtk_signal_connect (GTK_OBJECT (text_view->layout),
2367                           "changed",
2368                           GTK_SIGNAL_FUNC (changed_handler),
2369                           text_view);
2370       
2371       if (text_view->buffer)
2372         gtk_text_layout_set_buffer (text_view->layout, text_view->buffer);
2373
2374       ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
2375       pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
2376       rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
2377       pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
2378
2379       gtk_text_layout_set_contexts (text_view->layout, ltr_context, rtl_context);
2380
2381       pango_context_unref (ltr_context);
2382       pango_context_unref (rtl_context);
2383
2384       style = gtk_text_view_style_values_new ();
2385
2386       gtk_widget_ensure_style (widget);
2387       gtk_text_view_set_values_from_style (text_view, style, widget->style);
2388       
2389       style->pixels_above_lines = 2;
2390       style->pixels_below_lines = 2;
2391       style->pixels_inside_wrap = 1;
2392       
2393       style->wrap_mode = text_view->wrap_mode;
2394       style->justify = GTK_JUSTIFY_LEFT;
2395       style->direction = gtk_widget_get_direction (GTK_WIDGET (text_view));
2396       
2397       gtk_text_layout_set_default_style (text_view->layout, style);
2398       
2399       gtk_text_view_style_values_unref (style);
2400     }
2401 }
2402
2403 static void
2404 gtk_text_view_destroy_layout (GtkTextView *text_view)
2405 {
2406   if (text_view->layout)
2407     {
2408       gtk_text_view_end_selection_drag (text_view, NULL);
2409       
2410       gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->layout),
2411                                      invalidated_handler, text_view);
2412       gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->layout),
2413                                      changed_handler, text_view);
2414       gtk_object_unref (GTK_OBJECT (text_view->layout));
2415       text_view->layout = NULL;
2416     }
2417 }
2418
2419
2420 /*
2421  * DND feature
2422  */
2423
2424 static void
2425 gtk_text_view_start_selection_dnd (GtkTextView *text_view,
2426                               const GtkTextIter *iter,
2427                               GdkEventButton *event)
2428 {
2429   GdkDragContext *context;
2430   GtkTargetList *target_list;
2431   
2432   /* FIXME we have to handle more formats for the selection,
2433      and do the conversions to/from UTF8 */
2434   
2435   /* FIXME not sure how this is memory-managed. */
2436   target_list = gtk_target_list_new (target_table, n_targets);
2437   
2438   context = gtk_drag_begin (GTK_WIDGET (text_view), target_list,
2439                            GDK_ACTION_COPY | GDK_ACTION_MOVE,
2440                            1, (GdkEvent*)event);
2441
2442   gtk_drag_set_icon_default (context);
2443
2444   /* We're inside the selection, so start without being able
2445      to accept the drag. */
2446   gdk_drag_status (context, 0, event->time);
2447   gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
2448 }
2449
2450 static void
2451 gtk_text_view_drag_begin (GtkWidget        *widget,
2452                       GdkDragContext   *context)
2453 {
2454   
2455 }
2456
2457 static void
2458 gtk_text_view_drag_end (GtkWidget        *widget,
2459                     GdkDragContext   *context)
2460 {
2461   GtkTextView *text_view;
2462
2463   text_view = GTK_TEXT_VIEW (widget);
2464
2465   gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
2466 }
2467
2468 static void
2469 gtk_text_view_drag_data_get (GtkWidget        *widget,
2470                          GdkDragContext   *context,
2471                          GtkSelectionData *selection_data,
2472                          guint             info,
2473                          guint             time)
2474 {
2475   gchar *str;
2476   gint length;
2477   GtkTextIter start;
2478   GtkTextIter end;
2479   GtkTextView *text_view;
2480
2481   text_view = GTK_TEXT_VIEW (widget);
2482   
2483   str = NULL;
2484   length = 0;
2485   
2486   if (gtk_text_buffer_get_selection_bounds (text_view->buffer, &start, &end))
2487     {
2488       /* Extract the selected text */
2489       str = gtk_text_iter_get_visible_text (&start, &end);
2490       
2491       length = strlen (str);
2492     }
2493
2494   if (str)
2495     {
2496       if (info == TARGET_UTF8_STRING)
2497         {
2498           /* Pass raw UTF8 */
2499           gtk_selection_data_set (selection_data,
2500                                   utf8_atom,
2501                                   8*sizeof (gchar), (guchar *)str, length);
2502
2503         }
2504       else if (info == TARGET_STRING ||
2505                info == TARGET_TEXT)
2506         {
2507           gchar *latin1;
2508
2509           latin1 = gtk_text_utf_to_latin1(str, length);
2510           
2511           gtk_selection_data_set (selection_data,
2512                                   GDK_SELECTION_TYPE_STRING,
2513                                   8*sizeof (gchar), latin1, strlen (latin1));
2514           g_free (latin1);
2515         }
2516       else if (info == TARGET_COMPOUND_TEXT)
2517         {
2518           /* FIXME convert UTF8 directly to current locale, not via
2519              latin1 */
2520           
2521           guchar *text;
2522           GdkAtom encoding;
2523           gint format;
2524           gint new_length;
2525           gchar *latin1;
2526
2527           latin1 = gtk_text_utf_to_latin1(str, length);
2528           
2529           gdk_string_to_compound_text (latin1, &encoding, &format, &text, &new_length);
2530           gtk_selection_data_set (selection_data, encoding, format, text, new_length);
2531           gdk_free_compound_text (text);
2532
2533           g_free (latin1);
2534         }
2535
2536       g_free (str);
2537     }
2538 }
2539
2540 static void
2541 gtk_text_view_drag_data_delete (GtkWidget        *widget,
2542                             GdkDragContext   *context)
2543 {
2544   gtk_text_buffer_delete_selection (GTK_TEXT_VIEW (widget)->buffer);
2545 }
2546
2547 static void
2548 gtk_text_view_drag_leave (GtkWidget        *widget,
2549                       GdkDragContext   *context,
2550                       guint             time)
2551 {
2552
2553
2554 }
2555
2556 static gboolean
2557 gtk_text_view_drag_motion (GtkWidget        *widget,
2558                            GdkDragContext   *context,
2559                            gint              x,
2560                            gint              y,
2561                            guint             time)
2562 {
2563   GtkTextIter newplace;
2564   GtkTextView *text_view;
2565   GtkTextIter start;
2566   GtkTextIter end;
2567   
2568   text_view = GTK_TEXT_VIEW (widget);
2569
2570   gtk_text_layout_get_iter_at_pixel (text_view->layout,
2571                                      &newplace, 
2572                                      x + text_view->xoffset,
2573                                      y + text_view->yoffset);
2574
2575   if (gtk_text_buffer_get_selection_bounds (text_view->buffer,
2576                                             &start, &end) &&
2577       gtk_text_iter_in_region (&newplace, &start, &end))
2578     {
2579       /* We're inside the selection. */
2580       gdk_drag_status (context, 0, time);
2581       gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
2582     }
2583   else
2584     {
2585       gtk_text_mark_set_visible (text_view->dnd_mark, TRUE);
2586       
2587       gdk_drag_status (context, context->suggested_action, time);
2588     }
2589
2590   gtk_text_buffer_move_mark (text_view->buffer,
2591                              "__drag_target",
2592                              &newplace);
2593
2594   {
2595     /* The effect of this is that the text scrolls if you're near
2596        the edge. We have to scroll whether or not we're inside
2597        the selection. */
2598     gint margin;
2599     
2600     margin = MIN (widget->allocation.width, widget->allocation.height);
2601     margin /= 5;
2602     
2603     gtk_text_view_scroll_to_mark_adjusted (text_view, "__drag_target", margin, 1.0);
2604   }
2605   
2606   return TRUE;
2607 }
2608
2609 static gboolean
2610 gtk_text_view_drag_drop (GtkWidget        *widget,
2611                          GdkDragContext   *context,
2612                          gint              x,
2613                          gint              y,
2614                          guint             time)
2615 {
2616 #if 0
2617   /* called automatically. */
2618   if (context->targets)
2619     {
2620       gtk_drag_get_data (widget, context, 
2621                          GPOINTER_TO_INT (context->targets->data), 
2622                          time);
2623       return TRUE;
2624     }
2625   else
2626     return FALSE;
2627 #endif
2628   return TRUE;
2629 }
2630
2631 static void
2632 gtk_text_view_drag_data_received (GtkWidget        *widget,
2633                                   GdkDragContext   *context,
2634                                   gint              x,
2635                                   gint              y,
2636                                   GtkSelectionData *selection_data,
2637                                   guint             info,
2638                                   guint             time)
2639 {
2640   GtkTextIter drop_point;
2641   GtkTextView *text_view;
2642   enum {INVALID, STRING, CTEXT, UTF8} type;
2643
2644   text_view = GTK_TEXT_VIEW (widget);
2645   
2646   if (selection_data->type == GDK_TARGET_STRING)
2647     type = STRING;
2648   else if (selection_data->type == ctext_atom)
2649     type = CTEXT;
2650   else if (selection_data->type == utf8_atom)
2651     type = UTF8;
2652   else
2653     type = INVALID;
2654
2655   if (type == INVALID || selection_data->length < 0)
2656     {
2657       /* I think the DND code automatically tries asking
2658          for the various formats. */
2659       return;
2660     }
2661
2662   if (!gtk_text_buffer_get_iter_at_mark (text_view->buffer, &drop_point, "__drag_target"))
2663     return;
2664   
2665   switch (type)
2666     {
2667     case STRING:
2668       {
2669         gchar *utf;
2670
2671         utf = gtk_text_latin1_to_utf ((const gchar*)selection_data->data,
2672                                       selection_data->length);
2673         gtk_text_buffer_insert (text_view->buffer, &drop_point,
2674                                  utf, -1);
2675         g_free (utf);
2676       }
2677       break;
2678       
2679     case UTF8:
2680       gtk_text_buffer_insert (text_view->buffer, &drop_point,
2681                                (const gchar *)selection_data->data,
2682                                selection_data->length);
2683       break;
2684       
2685     case CTEXT:
2686       {
2687         gchar **list;
2688         gint count;
2689         gint i;
2690
2691         count = gdk_text_property_to_text_list (selection_data->type,
2692                                                 selection_data->format, 
2693                                                 selection_data->data,
2694                                                 selection_data->length,
2695                                                 &list);
2696         for (i=0; i<count; i++)
2697           {
2698             /* FIXME this is broken, it assumes the CTEXT is latin1
2699                when it probably isn't. */
2700             gchar *utf;
2701
2702             utf = gtk_text_latin1_to_utf (list[i], strlen (list[i]));
2703             
2704             gtk_text_buffer_insert (text_view->buffer, &drop_point, utf, -1);
2705
2706             g_free (utf);
2707           }
2708
2709         if (count > 0)
2710           gdk_free_text_list (list);
2711       }
2712       break;
2713       
2714     case INVALID:               /* quiet compiler */
2715       break;
2716     }
2717 }
2718
2719 static void
2720 gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
2721                                       GtkAdjustment *hadj,
2722                                       GtkAdjustment *vadj)
2723 {
2724   gboolean need_adjust = FALSE;
2725
2726   g_return_if_fail (text_view != NULL);
2727   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2728
2729   if (hadj)
2730     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
2731   else
2732     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
2733   if (vadj)
2734     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
2735   else
2736     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
2737   
2738   if (text_view->hadjustment && (text_view->hadjustment != hadj))
2739     {
2740       gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->hadjustment), text_view);
2741       gtk_object_unref (GTK_OBJECT (text_view->hadjustment));
2742     }
2743   
2744   if (text_view->vadjustment && (text_view->vadjustment != vadj))
2745     {
2746       gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->vadjustment), text_view);
2747       gtk_object_unref (GTK_OBJECT (text_view->vadjustment));
2748     }
2749   
2750   if (text_view->hadjustment != hadj)
2751     {
2752       text_view->hadjustment = hadj;
2753       gtk_object_ref (GTK_OBJECT (text_view->hadjustment));
2754       gtk_object_sink (GTK_OBJECT (text_view->hadjustment));
2755       
2756       gtk_signal_connect (GTK_OBJECT (text_view->hadjustment), "value_changed",
2757                           (GtkSignalFunc) gtk_text_view_value_changed,
2758                           text_view);
2759       need_adjust = TRUE;
2760     }
2761   
2762   if (text_view->vadjustment != vadj)
2763     {
2764       text_view->vadjustment = vadj;
2765       gtk_object_ref (GTK_OBJECT (text_view->vadjustment));
2766       gtk_object_sink (GTK_OBJECT (text_view->vadjustment));
2767       
2768       gtk_signal_connect (GTK_OBJECT (text_view->vadjustment), "value_changed",
2769                           (GtkSignalFunc) gtk_text_view_value_changed,
2770                           text_view);
2771       need_adjust = TRUE;
2772     }
2773
2774   if (need_adjust)
2775     gtk_text_view_value_changed (NULL, text_view);
2776 }
2777
2778 static void
2779 gtk_text_view_value_changed (GtkAdjustment *adj,
2780                              GtkTextView   *text_view)
2781 {
2782   GtkTextIter iter;
2783   gchar *mark_name;
2784   gint line_top;
2785   gint dx = 0;
2786   gint dy = 0;
2787
2788   if (adj == text_view->hadjustment)
2789     {
2790       dx = text_view->xoffset - (gint)adj->value;
2791       text_view->xoffset = adj->value;
2792     }
2793   else if (adj == text_view->vadjustment)
2794     {
2795       dy = text_view->yoffset - (gint)adj->value;
2796       text_view->yoffset = adj->value;
2797
2798       if (text_view->layout)
2799         {
2800           gtk_text_layout_get_line_at_y (text_view->layout, &iter, adj->value, &line_top);
2801           
2802           mark_name = gtk_text_mark_get_name (text_view->first_para_mark);
2803           gtk_text_buffer_move_mark (text_view->buffer, mark_name, &iter);
2804           g_free (mark_name);
2805           
2806           text_view->first_para_pixels = adj->value - line_top;
2807         }
2808     }
2809
2810   if (dx != 0 || dy != 0)
2811     {
2812       gdk_window_scroll (text_view->bin_window, dx, dy);
2813       gdk_window_process_updates (text_view->bin_window, TRUE);
2814     }
2815 }
2816
2817 static void
2818 gtk_text_view_commit_handler (GtkIMContext  *context,
2819                               const gchar   *str,
2820                               GtkTextView   *text_view)
2821 {
2822   gtk_text_buffer_delete_selection (text_view->buffer);
2823
2824   if (!strcmp (str, "\n"))
2825     {
2826       gtk_text_buffer_insert_at_cursor (text_view->buffer, "\n", 1);
2827     }
2828   else
2829     {
2830       if (text_view->overwrite_mode)
2831         gtk_text_view_delete_text (text_view, GTK_TEXT_DELETE_CHAR, 1);
2832       gtk_text_buffer_insert_at_cursor (text_view->buffer, str, strlen (str));
2833     }
2834   
2835   gtk_text_view_scroll_to_mark (text_view, "insert", 0);
2836 }
2837
2838 static void
2839 gtk_text_view_mark_set_handler (GtkTextBuffer     *buffer,
2840                                 const GtkTextIter *location,
2841                                 const char        *mark_name,
2842                                 gpointer           data)
2843 {
2844   GtkTextView *text_view = GTK_TEXT_VIEW (data);
2845   
2846   if (!strcmp (mark_name, "insert"))
2847     {
2848       text_view->virtual_cursor_x = -1;
2849       text_view->virtual_cursor_y = -1;
2850     }
2851 }
2852
2853 static void
2854 gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
2855                                       gint        *x,
2856                                       gint        *y)
2857 {
2858   GdkRectangle strong_pos;
2859   GtkTextIter insert;
2860
2861   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, "insert");
2862   
2863   if ((x && text_view->virtual_cursor_x == -1) ||
2864       (y && text_view->virtual_cursor_y == -1))
2865     gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
2866
2867   if (x)
2868     {
2869       if (text_view->virtual_cursor_x != -1)
2870         *x = text_view->virtual_cursor_x;
2871       else
2872         *x = strong_pos.x;
2873     }
2874
2875   if (y)
2876     {
2877       if (text_view->virtual_cursor_x != -1)
2878         *y = text_view->virtual_cursor_y;
2879       else
2880         *y = strong_pos.y + strong_pos.height / 2;
2881     }
2882 }
2883
2884 static void
2885 gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
2886                                       gint         x,
2887                                       gint         y)
2888 {
2889   GdkRectangle strong_pos;
2890   GtkTextIter insert;
2891
2892   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, "insert");
2893   
2894   if (x == -1 || y == -1)
2895     gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
2896
2897   text_view->virtual_cursor_x = (x == -1) ? strong_pos.x : x;
2898   text_view->virtual_cursor_y = (y == -1) ? strong_pos.y + strong_pos.height / 2 : y;
2899 }