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