]> Pileus Git - ~andy/gtk/blob - gtk/gtktextview.c
Fix for #84668, reported by Torbjörn Andersson:
[~andy/gtk] / gtk / gtktextview.c
1 /* GTK - The GIMP Toolkit
2  * gtktextview.c Copyright (C) 2000 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include <string.h>
28
29 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
30 #include "gtkbindings.h"
31 #include "gtkdnd.h"
32 #include "gtkimagemenuitem.h"
33 #include "gtkintl.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkmenu.h"
37 #include "gtkmenuitem.h"
38 #include "gtkseparatormenuitem.h"
39 #include "gtksettings.h"
40 #include "gtkstock.h"
41 #include "gtktextdisplay.h"
42 #include "gtktextview.h"
43 #include "gtkimmulticontext.h"
44 #include "gdk/gdkkeysyms.h"
45 #include "gtksizegroup.h"          /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=72258 */
46 #include "gtktextutil.h"
47 #include "gtkwindow.h"
48
49 /* How scrolling, validation, exposes, etc. work.
50  *
51  * The expose_event handler has the invariant that the onscreen lines
52  * have been validated.
53  *
54  * There are two ways that onscreen lines can become invalid. The first
55  * is to change which lines are onscreen. This happens when the value
56  * of a scroll adjustment changes. So the code path begins in
57  * gtk_text_view_value_changed() and goes like this:
58  *   - gdk_window_scroll() to reflect the new adjustment value
59  *   - validate the lines that were moved onscreen
60  *   - gdk_window_process_updates() to handle the exposes immediately
61  *
62  * The second way is that you get the "invalidated" signal from the layout,
63  * indicating that lines have become invalid. This code path begins in
64  * invalidated_handler() and goes like this:
65  *   - install high-priority idle which does the rest of the steps
66  *   - if a scroll is pending from scroll_to_mark(), do the scroll,
67  *     jumping to the gtk_text_view_value_changed() code path
68  *   - otherwise, validate the onscreen lines
69  *   - DO NOT process updates
70  *
71  * In both cases, validating the onscreen lines can trigger a scroll
72  * due to maintaining the first_para on the top of the screen.
73  * If validation triggers a scroll, we jump to the top of the code path
74  * for value_changed, and bail out of the current code path.
75  *
76  * Also, in size_allocate, if we invalidate some lines from changing
77  * the layout width, we need to go ahead and run the high-priority idle,
78  * because GTK sends exposes right after doing the size allocates without
79  * returning to the main loop. This is also why the high-priority idle
80  * is at a higher priority than resizing.
81  *
82  */
83
84 #if 0
85 #define DEBUG_VALIDATION_AND_SCROLLING
86 #endif
87
88 #ifdef DEBUG_VALIDATION_AND_SCROLLING
89 #define DV(x) (x)
90 #else
91 #define DV(x)
92 #endif
93
94 #define SCREEN_WIDTH(widget) text_window_get_width (GTK_TEXT_VIEW (widget)->text_window)
95 #define SCREEN_HEIGHT(widget) text_window_get_height (GTK_TEXT_VIEW (widget)->text_window)
96
97 struct _GtkTextPendingScroll
98 {
99   GtkTextMark   *mark;
100   gdouble        within_margin;
101   gboolean       use_align;
102   gdouble        xalign;
103   gdouble        yalign;
104 };
105   
106 enum
107 {
108   SET_SCROLL_ADJUSTMENTS,
109   POPULATE_POPUP,
110   MOVE_CURSOR,
111   PAGE_HORIZONTALLY,
112   SET_ANCHOR,
113   INSERT_AT_CURSOR,
114   DELETE_FROM_CURSOR,
115   CUT_CLIPBOARD,
116   COPY_CLIPBOARD,
117   PASTE_CLIPBOARD,
118   TOGGLE_OVERWRITE,
119   MOVE_FOCUS,
120   SELECT_ALL,
121   LAST_SIGNAL
122 };
123
124 enum
125 {
126   PROP_0,
127   PROP_PIXELS_ABOVE_LINES,
128   PROP_PIXELS_BELOW_LINES,
129   PROP_PIXELS_INSIDE_WRAP,
130   PROP_EDITABLE,
131   PROP_WRAP_MODE,
132   PROP_JUSTIFICATION,
133   PROP_LEFT_MARGIN,
134   PROP_RIGHT_MARGIN,
135   PROP_INDENT,
136   PROP_TABS,
137   PROP_CURSOR_VISIBLE,
138   PROP_BUFFER,
139   LAST_PROP
140 };
141
142 static void gtk_text_view_init                 (GtkTextView      *text_view);
143 static void gtk_text_view_class_init           (GtkTextViewClass *klass);
144 static void gtk_text_view_destroy              (GtkObject        *object);
145 static void gtk_text_view_finalize             (GObject          *object);
146 static void gtk_text_view_set_property         (GObject         *object,
147                                                 guint            prop_id,
148                                                 const GValue    *value,
149                                                 GParamSpec      *pspec);
150 static void gtk_text_view_get_property         (GObject         *object,
151                                                 guint            prop_id,
152                                                 GValue          *value,
153                                                 GParamSpec      *pspec);
154 static void gtk_text_view_size_request         (GtkWidget        *widget,
155                                                 GtkRequisition   *requisition);
156 static void gtk_text_view_size_allocate        (GtkWidget        *widget,
157                                                 GtkAllocation    *allocation);
158 static void gtk_text_view_realize              (GtkWidget        *widget);
159 static void gtk_text_view_unrealize            (GtkWidget        *widget);
160 static void gtk_text_view_style_set            (GtkWidget        *widget,
161                                                 GtkStyle         *previous_style);
162 static void gtk_text_view_direction_changed    (GtkWidget        *widget,
163                                                 GtkTextDirection  previous_direction);
164 static void gtk_text_view_grab_notify          (GtkWidget        *widget,
165                                                 gboolean         was_grabbed);
166 static void gtk_text_view_state_changed        (GtkWidget        *widget,
167                                                 GtkStateType      previous_state);
168
169 static gint gtk_text_view_event                (GtkWidget        *widget,
170                                                 GdkEvent         *event);
171 static gint gtk_text_view_key_press_event      (GtkWidget        *widget,
172                                                 GdkEventKey      *event);
173 static gint gtk_text_view_key_release_event    (GtkWidget        *widget,
174                                                 GdkEventKey      *event);
175 static gint gtk_text_view_button_press_event   (GtkWidget        *widget,
176                                                 GdkEventButton   *event);
177 static gint gtk_text_view_button_release_event (GtkWidget        *widget,
178                                                 GdkEventButton   *event);
179 static gint gtk_text_view_focus_in_event       (GtkWidget        *widget,
180                                                 GdkEventFocus    *event);
181 static gint gtk_text_view_focus_out_event      (GtkWidget        *widget,
182                                                 GdkEventFocus    *event);
183 static gint gtk_text_view_motion_event         (GtkWidget        *widget,
184                                                 GdkEventMotion   *event);
185 static gint gtk_text_view_expose_event         (GtkWidget        *widget,
186                                                 GdkEventExpose   *expose);
187 static void gtk_text_view_draw_focus           (GtkWidget        *widget);
188 static void gtk_text_view_grab_focus           (GtkWidget        *widget);
189 static gboolean gtk_text_view_focus            (GtkWidget        *widget,
190                                                 GtkDirectionType  direction);
191 static void gtk_text_view_select_all           (GtkWidget        *widget,
192                                                 gboolean          select);
193
194
195 /* Source side drag signals */
196 static void gtk_text_view_drag_begin       (GtkWidget        *widget,
197                                             GdkDragContext   *context);
198 static void gtk_text_view_drag_end         (GtkWidget        *widget,
199                                             GdkDragContext   *context);
200 static void gtk_text_view_drag_data_get    (GtkWidget        *widget,
201                                             GdkDragContext   *context,
202                                             GtkSelectionData *selection_data,
203                                             guint             info,
204                                             guint             time);
205 static void gtk_text_view_drag_data_delete (GtkWidget        *widget,
206                                             GdkDragContext   *context);
207
208 /* Target side drag signals */
209 static void     gtk_text_view_drag_leave         (GtkWidget        *widget,
210                                                   GdkDragContext   *context,
211                                                   guint             time);
212 static gboolean gtk_text_view_drag_motion        (GtkWidget        *widget,
213                                                   GdkDragContext   *context,
214                                                   gint              x,
215                                                   gint              y,
216                                                   guint             time);
217 static gboolean gtk_text_view_drag_drop          (GtkWidget        *widget,
218                                                   GdkDragContext   *context,
219                                                   gint              x,
220                                                   gint              y,
221                                                   guint             time);
222 static void     gtk_text_view_drag_data_received (GtkWidget        *widget,
223                                                   GdkDragContext   *context,
224                                                   gint              x,
225                                                   gint              y,
226                                                   GtkSelectionData *selection_data,
227                                                   guint             info,
228                                                   guint             time);
229
230 static void gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
231                                                   GtkAdjustment *hadj,
232                                                   GtkAdjustment *vadj);
233 static gboolean gtk_text_view_popup_menu         (GtkWidget     *widget);
234
235 static void gtk_text_view_move_cursor       (GtkTextView           *text_view,
236                                              GtkMovementStep        step,
237                                              gint                   count,
238                                              gboolean               extend_selection);
239 static void gtk_text_view_page_horizontally (GtkTextView          *text_view,
240                                              gint                  count,
241                                              gboolean              extend_selection);
242 static void gtk_text_view_set_anchor       (GtkTextView           *text_view);
243 static void gtk_text_view_scroll_pages     (GtkTextView           *text_view,
244                                             gint                   count,
245                                             gboolean               extend_selection);
246 static void gtk_text_view_scroll_hpages    (GtkTextView           *text_view,
247                                             gint                   count,
248                                             gboolean               extend_selection);
249 static void gtk_text_view_insert_at_cursor (GtkTextView           *text_view,
250                                             const gchar           *str);
251 static void gtk_text_view_delete_from_cursor (GtkTextView           *text_view,
252                                               GtkDeleteType          type,
253                                               gint                   count);
254 static void gtk_text_view_cut_clipboard    (GtkTextView           *text_view);
255 static void gtk_text_view_copy_clipboard   (GtkTextView           *text_view);
256 static void gtk_text_view_paste_clipboard  (GtkTextView           *text_view);
257 static void gtk_text_view_toggle_overwrite (GtkTextView           *text_view);
258 static void gtk_text_view_move_focus       (GtkTextView           *text_view,
259                                             GtkDirectionType       direction_type);
260 static void gtk_text_view_unselect         (GtkTextView           *text_view);
261
262 static void     gtk_text_view_validate_onscreen     (GtkTextView        *text_view);
263 static void     gtk_text_view_get_first_para_iter   (GtkTextView        *text_view,
264                                                      GtkTextIter        *iter);
265 static void     gtk_text_view_update_layout_width       (GtkTextView        *text_view);
266 static void     gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
267                                                          GtkTextAttributes *values,
268                                                          GtkStyle           *style);
269 static void     gtk_text_view_ensure_layout          (GtkTextView        *text_view);
270 static void     gtk_text_view_destroy_layout         (GtkTextView        *text_view);
271 static void     gtk_text_view_check_keymap_direction (GtkTextView        *text_view);
272 static void     gtk_text_view_reset_im_context       (GtkTextView        *text_view);
273 static void     gtk_text_view_start_selection_drag   (GtkTextView        *text_view,
274                                                       const GtkTextIter  *iter,
275                                                       GdkEventButton     *event);
276 static gboolean gtk_text_view_end_selection_drag     (GtkTextView        *text_view,
277                                                       GdkEventButton     *event);
278 static void     gtk_text_view_start_selection_dnd    (GtkTextView        *text_view,
279                                                       const GtkTextIter  *iter,
280                                                       GdkEventMotion     *event);
281 static void     gtk_text_view_check_cursor_blink     (GtkTextView        *text_view);
282 static void     gtk_text_view_pend_cursor_blink      (GtkTextView        *text_view);
283 static void     gtk_text_view_stop_cursor_blink      (GtkTextView        *text_view);
284
285 static void     gtk_text_view_value_changed                (GtkAdjustment *adj,
286                                                             GtkTextView   *view);
287 static void     gtk_text_view_commit_handler               (GtkIMContext  *context,
288                                                             const gchar   *str,
289                                                             GtkTextView   *text_view);
290 static void     gtk_text_view_commit_text                  (GtkTextView   *text_view,
291                                                             const gchar   *text);
292 static void     gtk_text_view_preedit_changed_handler      (GtkIMContext  *context,
293                                                             GtkTextView   *text_view);
294 static gboolean gtk_text_view_retrieve_surrounding_handler (GtkIMContext  *context,
295                                                             GtkTextView   *text_view);
296 static gboolean gtk_text_view_delete_surrounding_handler   (GtkIMContext  *context,
297                                                             gint           offset,
298                                                             gint           n_chars,
299                                                             GtkTextView   *text_view);
300
301 static void gtk_text_view_mark_set_handler       (GtkTextBuffer     *buffer,
302                                                   const GtkTextIter *location,
303                                                   GtkTextMark       *mark,
304                                                   gpointer           data);
305 static void gtk_text_view_get_virtual_cursor_pos (GtkTextView       *text_view,
306                                                   gint              *x,
307                                                   gint              *y);
308 static void gtk_text_view_set_virtual_cursor_pos (GtkTextView       *text_view,
309                                                   gint               x,
310                                                   gint               y);
311
312 static GtkAdjustment* get_hadjustment            (GtkTextView       *text_view);
313 static GtkAdjustment* get_vadjustment            (GtkTextView       *text_view);
314
315 static void gtk_text_view_do_popup               (GtkTextView       *text_view,
316                                                   GdkEventButton    *event);
317
318 static void gtk_text_view_queue_scroll           (GtkTextView   *text_view,
319                                                   GtkTextMark   *mark,
320                                                   gdouble        within_margin,
321                                                   gboolean       use_align,
322                                                   gdouble        xalign,
323                                                   gdouble        yalign);
324
325 static gboolean gtk_text_view_flush_scroll         (GtkTextView *text_view);
326 static void     gtk_text_view_update_adjustments   (GtkTextView *text_view);
327 static void     gtk_text_view_invalidate           (GtkTextView *text_view);
328 static void     gtk_text_view_flush_first_validate (GtkTextView *text_view);
329
330 static void gtk_text_view_update_im_spot_location (GtkTextView *text_view);
331
332 /* Container methods */
333 static void gtk_text_view_add    (GtkContainer *container,
334                                   GtkWidget    *child);
335 static void gtk_text_view_remove (GtkContainer *container,
336                                   GtkWidget    *child);
337 static void gtk_text_view_forall (GtkContainer *container,
338                                   gboolean      include_internals,
339                                  GtkCallback   callback,
340                                   gpointer      callback_data);
341
342 /* FIXME probably need the focus methods. */
343
344 typedef struct _GtkTextViewChild GtkTextViewChild;
345
346 struct _GtkTextViewChild
347 {
348   GtkWidget *widget;
349
350   GtkTextChildAnchor *anchor;
351
352   gint from_top_of_line;
353   gint from_left_of_buffer;
354   
355   /* These are ignored if anchor != NULL */
356   GtkTextWindowType type;
357   gint x;
358   gint y;
359 };
360
361 static GtkTextViewChild* text_view_child_new_anchored      (GtkWidget          *child,
362                                                             GtkTextChildAnchor *anchor,
363                                                             GtkTextLayout      *layout);
364 static GtkTextViewChild* text_view_child_new_window        (GtkWidget          *child,
365                                                             GtkTextWindowType   type,
366                                                             gint                x,
367                                                             gint                y);
368 static void              text_view_child_free              (GtkTextViewChild   *child);
369 static void              text_view_child_set_parent_window (GtkTextView        *text_view,
370                                                             GtkTextViewChild   *child);
371
372 struct _GtkTextWindow
373 {
374   GtkTextWindowType type;
375   GtkWidget *widget;
376   GdkWindow *window;
377   GdkWindow *bin_window;
378   GtkRequisition requisition;
379   GdkRectangle allocation;
380 };
381
382 static GtkTextWindow *text_window_new             (GtkTextWindowType  type,
383                                                    GtkWidget         *widget,
384                                                    gint               width_request,
385                                                    gint               height_request);
386 static void           text_window_free            (GtkTextWindow     *win);
387 static void           text_window_realize         (GtkTextWindow     *win,
388                                                    GdkWindow         *parent);
389 static void           text_window_unrealize       (GtkTextWindow     *win);
390 static void           text_window_size_allocate   (GtkTextWindow     *win,
391                                                    GdkRectangle      *rect);
392 static void           text_window_scroll          (GtkTextWindow     *win,
393                                                    gint               dx,
394                                                    gint               dy);
395 static void           text_window_invalidate_rect (GtkTextWindow     *win,
396                                                    GdkRectangle      *rect);
397
398 static gint           text_window_get_width       (GtkTextWindow     *win);
399 static gint           text_window_get_height      (GtkTextWindow     *win);
400
401
402 enum
403 {
404   TARGET_STRING,
405   TARGET_TEXT,
406   TARGET_COMPOUND_TEXT,
407   TARGET_UTF8_STRING,
408   TARGET_TEXT_BUFFER_CONTENTS
409 };
410
411 static const GtkTargetEntry target_table[] = {
412   { "GTK_TEXT_BUFFER_CONTENTS", GTK_TARGET_SAME_APP,
413     TARGET_TEXT_BUFFER_CONTENTS },
414   { "UTF8_STRING", 0, TARGET_UTF8_STRING },
415   { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
416   { "TEXT", 0, TARGET_TEXT },
417   { "STRING",     0, TARGET_STRING }
418 };
419
420 static GtkContainerClass *parent_class = NULL;
421 static guint signals[LAST_SIGNAL] = { 0 };
422
423 GType
424 gtk_text_view_get_type (void)
425 {
426   static GType our_type = 0;
427
428   if (our_type == 0)
429     {
430       static const GTypeInfo our_info =
431       {
432         sizeof (GtkTextViewClass),
433         NULL,           /* base_init */
434         NULL,           /* base_finalize */
435         (GClassInitFunc) gtk_text_view_class_init,
436         NULL,           /* class_finalize */
437         NULL,           /* class_data */
438         sizeof (GtkTextView),
439         0,              /* n_preallocs */
440         (GInstanceInitFunc) gtk_text_view_init,
441       };
442
443       our_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkTextView",
444                                          &our_info, 0);
445     }
446
447   return our_type;
448 }
449
450 static void
451 add_move_binding (GtkBindingSet  *binding_set,
452                   guint           keyval,
453                   guint           modmask,
454                   GtkMovementStep step,
455                   gint            count)
456 {
457   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
458
459   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
460                                 "move_cursor", 3,
461                                 G_TYPE_ENUM, step,
462                                 G_TYPE_INT, count,
463                                 G_TYPE_BOOLEAN, FALSE);
464
465   /* Selection-extending version */
466   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
467                                 "move_cursor", 3,
468                                 G_TYPE_ENUM, step,
469                                 G_TYPE_INT, count,
470                                 G_TYPE_BOOLEAN, TRUE);
471 }
472
473 static void
474 gtk_text_view_class_init (GtkTextViewClass *klass)
475 {
476   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
477   GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
478   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
479   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
480   GtkBindingSet *binding_set;
481
482   parent_class = g_type_class_peek_parent (klass);
483
484   /* Default handlers and virtual methods
485    */
486   gobject_class->set_property = gtk_text_view_set_property;
487   gobject_class->get_property = gtk_text_view_get_property;
488
489   object_class->destroy = gtk_text_view_destroy;
490   gobject_class->finalize = gtk_text_view_finalize;
491
492   widget_class->realize = gtk_text_view_realize;
493   widget_class->unrealize = gtk_text_view_unrealize;
494   widget_class->style_set = gtk_text_view_style_set;
495   widget_class->direction_changed = gtk_text_view_direction_changed;
496   widget_class->grab_notify = gtk_text_view_grab_notify;
497   widget_class->state_changed = gtk_text_view_state_changed;
498   widget_class->size_request = gtk_text_view_size_request;
499   widget_class->size_allocate = gtk_text_view_size_allocate;
500   widget_class->event = gtk_text_view_event;
501   widget_class->key_press_event = gtk_text_view_key_press_event;
502   widget_class->key_release_event = gtk_text_view_key_release_event;
503   widget_class->button_press_event = gtk_text_view_button_press_event;
504   widget_class->button_release_event = gtk_text_view_button_release_event;
505   widget_class->focus_in_event = gtk_text_view_focus_in_event;
506   widget_class->focus_out_event = gtk_text_view_focus_out_event;
507   widget_class->motion_notify_event = gtk_text_view_motion_event;
508   widget_class->expose_event = gtk_text_view_expose_event;
509   widget_class->grab_focus = gtk_text_view_grab_focus;
510   widget_class->focus = gtk_text_view_focus;
511   
512   widget_class->drag_begin = gtk_text_view_drag_begin;
513   widget_class->drag_end = gtk_text_view_drag_end;
514   widget_class->drag_data_get = gtk_text_view_drag_data_get;
515   widget_class->drag_data_delete = gtk_text_view_drag_data_delete;
516
517   widget_class->drag_leave = gtk_text_view_drag_leave;
518   widget_class->drag_motion = gtk_text_view_drag_motion;
519   widget_class->drag_drop = gtk_text_view_drag_drop;
520   widget_class->drag_data_received = gtk_text_view_drag_data_received;
521
522   widget_class->popup_menu = gtk_text_view_popup_menu;
523   
524   container_class->add = gtk_text_view_add;
525   container_class->remove = gtk_text_view_remove;
526   container_class->forall = gtk_text_view_forall;
527
528   klass->move_cursor = gtk_text_view_move_cursor;
529   klass->page_horizontally = gtk_text_view_page_horizontally;
530   klass->set_anchor = gtk_text_view_set_anchor;
531   klass->insert_at_cursor = gtk_text_view_insert_at_cursor;
532   klass->delete_from_cursor = gtk_text_view_delete_from_cursor;
533   klass->cut_clipboard = gtk_text_view_cut_clipboard;
534   klass->copy_clipboard = gtk_text_view_copy_clipboard;
535   klass->paste_clipboard = gtk_text_view_paste_clipboard;
536   klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
537   klass->move_focus = gtk_text_view_move_focus;
538   klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments;
539
540   /*
541    * Properties
542    */
543  
544   g_object_class_install_property (gobject_class,
545                                    PROP_PIXELS_ABOVE_LINES,
546                                    g_param_spec_int ("pixels_above_lines",
547                                                      _("Pixels Above Lines"),
548                                                      _("Pixels of blank space above paragraphs"),
549                                                      0,
550                                                      G_MAXINT,
551                                                      0,
552                                                      G_PARAM_READWRITE));
553  
554   g_object_class_install_property (gobject_class,
555                                    PROP_PIXELS_BELOW_LINES,
556                                    g_param_spec_int ("pixels_below_lines",
557                                                      _("Pixels Below Lines"),
558                                                      _("Pixels of blank space below paragraphs"),
559                                                      0,
560                                                      G_MAXINT,
561                                                      0,
562                                                      G_PARAM_READWRITE));
563  
564   g_object_class_install_property (gobject_class,
565                                    PROP_PIXELS_INSIDE_WRAP,
566                                    g_param_spec_int ("pixels_inside_wrap",
567                                                      _("Pixels Inside Wrap"),
568                                                      _("Pixels of blank space between wrapped lines in a paragraph"),
569                                                      0,
570                                                      G_MAXINT,
571                                                      0,
572                                                      G_PARAM_READWRITE));
573
574   g_object_class_install_property (gobject_class,
575                                    PROP_EDITABLE,
576                                    g_param_spec_boolean ("editable",
577                                                          _("Editable"),
578                                                          _("Whether the text can be modified by the user"),
579                                                          TRUE,
580                                                          G_PARAM_READWRITE));
581
582   g_object_class_install_property (gobject_class,
583                                    PROP_WRAP_MODE,
584                                    g_param_spec_enum ("wrap_mode",
585                                                       _("Wrap Mode"),
586                                                       _("Whether to wrap lines never, at word boundaries, or at character boundaries"),
587                                                       GTK_TYPE_WRAP_MODE,
588                                                       GTK_WRAP_NONE,
589                                                       G_PARAM_READWRITE));
590  
591   g_object_class_install_property (gobject_class,
592                                    PROP_JUSTIFICATION,
593                                    g_param_spec_enum ("justification",
594                                                       _("Justification"),
595                                                       _("Left, right, or center justification"),
596                                                       GTK_TYPE_JUSTIFICATION,
597                                                       GTK_JUSTIFY_LEFT,
598                                                       G_PARAM_READWRITE));
599  
600   g_object_class_install_property (gobject_class,
601                                    PROP_LEFT_MARGIN,
602                                    g_param_spec_int ("left_margin",
603                                                      _("Left Margin"),
604                                                      _("Width of the left margin in pixels"),
605                                                      0,
606                                                      G_MAXINT,
607                                                      0,
608                                                      G_PARAM_READWRITE));
609
610   g_object_class_install_property (gobject_class,
611                                    PROP_RIGHT_MARGIN,
612                                    g_param_spec_int ("right_margin",
613                                                      _("Right Margin"),
614                                                      _("Width of the right margin in pixels"),
615                                                      0,
616                                                      G_MAXINT,
617                                                      0,
618                                                      G_PARAM_READWRITE));
619
620   g_object_class_install_property (gobject_class,
621                                    PROP_INDENT,
622                                    g_param_spec_int ("indent",
623                                                      _("Indent"),
624                                                      _("Amount to indent the paragraph, in pixels"),
625                                                      0,
626                                                      G_MAXINT,
627                                                      0,
628                                                      G_PARAM_READWRITE));
629
630   g_object_class_install_property (gobject_class,
631                                    PROP_TABS,
632                                    g_param_spec_boxed ("tabs",
633                                                        _("Tabs"),
634                                                        _("Custom tabs for this text"),
635                                                        PANGO_TYPE_TAB_ARRAY,
636                                                        G_PARAM_READWRITE));
637
638   g_object_class_install_property (gobject_class,
639                                    PROP_CURSOR_VISIBLE,
640                                    g_param_spec_boolean ("cursor_visible",
641                                                          _("Cursor Visible"),
642                                                          _("If the insertion cursor is shown"),
643                                                          TRUE,
644                                                          G_PARAM_READWRITE));
645
646   g_object_class_install_property (gobject_class,
647                                    PROP_BUFFER,
648                                    g_param_spec_object ("buffer",
649                                                         _("Buffer"),
650                                                         _("The buffer which is displayed"),
651                                                         GTK_TYPE_TEXT_BUFFER,
652                                                         G_PARAM_READWRITE));
653
654   
655   /*
656    * Signals
657    */
658
659   signals[MOVE_CURSOR] =
660     g_signal_new ("move_cursor",
661                   G_OBJECT_CLASS_TYPE (gobject_class),
662                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
663                   G_STRUCT_OFFSET (GtkTextViewClass, move_cursor),
664                   NULL, NULL,
665                   _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
666                   G_TYPE_NONE, 3,
667                   GTK_TYPE_MOVEMENT_STEP,
668                   G_TYPE_INT,
669                   G_TYPE_BOOLEAN);
670
671   signals[PAGE_HORIZONTALLY] =
672     g_signal_new ("page_horizontally",
673                   G_OBJECT_CLASS_TYPE (gobject_class),
674                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
675                   G_STRUCT_OFFSET (GtkTextViewClass, page_horizontally),
676                   NULL, NULL,
677                   _gtk_marshal_VOID__INT_BOOLEAN,
678                   G_TYPE_NONE, 2,
679                   G_TYPE_INT,
680                   G_TYPE_BOOLEAN);
681   
682   signals[SET_ANCHOR] =
683     g_signal_new ("set_anchor",
684                   G_OBJECT_CLASS_TYPE (gobject_class),
685                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
686                   G_STRUCT_OFFSET (GtkTextViewClass, set_anchor),
687                   NULL, NULL,
688                   _gtk_marshal_VOID__VOID,
689                   G_TYPE_NONE, 0);
690
691   signals[INSERT_AT_CURSOR] =
692     g_signal_new ("insert_at_cursor",
693                   G_OBJECT_CLASS_TYPE (gobject_class),
694                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
695                   G_STRUCT_OFFSET (GtkTextViewClass, insert_at_cursor),
696                   NULL, NULL,
697                   _gtk_marshal_VOID__STRING,
698                   G_TYPE_NONE, 1,
699                   G_TYPE_STRING);
700
701   signals[DELETE_FROM_CURSOR] =
702     g_signal_new ("delete_from_cursor",
703                   G_OBJECT_CLASS_TYPE (gobject_class),
704                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
705                   G_STRUCT_OFFSET (GtkTextViewClass, delete_from_cursor),
706                   NULL, NULL,
707                   _gtk_marshal_VOID__ENUM_INT,
708                   G_TYPE_NONE, 2,
709                   GTK_TYPE_DELETE_TYPE,
710                   G_TYPE_INT);
711
712   signals[CUT_CLIPBOARD] =
713     g_signal_new ("cut_clipboard",
714                   G_OBJECT_CLASS_TYPE (gobject_class),
715                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
716                   G_STRUCT_OFFSET (GtkTextViewClass, cut_clipboard),
717                   NULL, NULL,
718                   _gtk_marshal_VOID__VOID,
719                   G_TYPE_NONE, 0);
720
721   signals[COPY_CLIPBOARD] =
722     g_signal_new ("copy_clipboard",
723                   G_OBJECT_CLASS_TYPE (gobject_class),
724                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
725                   G_STRUCT_OFFSET (GtkTextViewClass, copy_clipboard),
726                   NULL, NULL,
727                   _gtk_marshal_VOID__VOID,
728                   G_TYPE_NONE, 0);
729
730   signals[PASTE_CLIPBOARD] =
731     g_signal_new ("paste_clipboard",
732                   G_OBJECT_CLASS_TYPE (gobject_class),
733                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
734                   G_STRUCT_OFFSET (GtkTextViewClass, paste_clipboard),
735                   NULL, NULL,
736                   _gtk_marshal_VOID__VOID,
737                   G_TYPE_NONE, 0);
738
739   signals[TOGGLE_OVERWRITE] =
740     g_signal_new ("toggle_overwrite",
741                   G_OBJECT_CLASS_TYPE (gobject_class),
742                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
743                   G_STRUCT_OFFSET (GtkTextViewClass, toggle_overwrite),
744                   NULL, NULL,
745                   _gtk_marshal_VOID__VOID,
746                   G_TYPE_NONE, 0);
747
748   signals[MOVE_FOCUS] =
749     g_signal_new ("move_focus",
750                   G_OBJECT_CLASS_TYPE (gobject_class),
751                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
752                   G_STRUCT_OFFSET (GtkTextViewClass, move_focus),
753                   NULL, NULL,
754                   _gtk_marshal_VOID__ENUM,
755                   G_TYPE_NONE, 1,
756                   GTK_TYPE_DIRECTION_TYPE);
757   
758   signals[SET_SCROLL_ADJUSTMENTS] =
759     g_signal_new ("set_scroll_adjustments",
760                   G_OBJECT_CLASS_TYPE (gobject_class),
761                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
762                   G_STRUCT_OFFSET (GtkTextViewClass, set_scroll_adjustments),
763                   NULL, NULL,
764                   _gtk_marshal_VOID__OBJECT_OBJECT,
765                   G_TYPE_NONE, 2,
766                   GTK_TYPE_ADJUSTMENT,
767                   GTK_TYPE_ADJUSTMENT);
768   widget_class->set_scroll_adjustments_signal = signals[SET_SCROLL_ADJUSTMENTS];
769
770   signals[POPULATE_POPUP] =
771     g_signal_new ("populate_popup",
772                   G_OBJECT_CLASS_TYPE (gobject_class),
773                   G_SIGNAL_RUN_LAST,
774                   G_STRUCT_OFFSET (GtkTextViewClass, populate_popup),
775                   NULL, NULL,
776                   _gtk_marshal_VOID__OBJECT,
777                   G_TYPE_NONE, 1,
778                   GTK_TYPE_MENU);
779   
780   signals[SELECT_ALL] =
781     _gtk_binding_signal_new ("select_all",
782                              G_OBJECT_CLASS_TYPE (object_class),
783                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
784                              G_CALLBACK (gtk_text_view_select_all),
785                              NULL, NULL,
786                              _gtk_marshal_VOID__BOOLEAN,
787                              G_TYPE_NONE, 1,
788                              G_TYPE_BOOLEAN, TRUE);
789
790   
791   /*
792    * Key bindings
793    */
794
795   binding_set = gtk_binding_set_by_class (klass);
796   
797   /* Moving the insertion point */
798   add_move_binding (binding_set, GDK_Right, 0,
799                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
800
801   add_move_binding (binding_set, GDK_KP_Right, 0,
802                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
803   
804   add_move_binding (binding_set, GDK_Left, 0,
805                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
806
807   add_move_binding (binding_set, GDK_KP_Left, 0,
808                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
809   
810   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
811                     GTK_MOVEMENT_WORDS, 1);
812
813   add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
814                     GTK_MOVEMENT_WORDS, 1);
815   
816   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
817                     GTK_MOVEMENT_WORDS, -1);
818
819   add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
820                     GTK_MOVEMENT_WORDS, 1);
821   
822   add_move_binding (binding_set, GDK_Up, 0,
823                     GTK_MOVEMENT_DISPLAY_LINES, -1);
824
825   add_move_binding (binding_set, GDK_KP_Up, 0,
826                     GTK_MOVEMENT_DISPLAY_LINES, -1);
827   
828   add_move_binding (binding_set, GDK_Down, 0,
829                     GTK_MOVEMENT_DISPLAY_LINES, 1);
830
831   add_move_binding (binding_set, GDK_KP_Down, 0,
832                     GTK_MOVEMENT_DISPLAY_LINES, 1);
833   
834   add_move_binding (binding_set, GDK_Up, GDK_CONTROL_MASK,
835                     GTK_MOVEMENT_PARAGRAPHS, -1);
836
837   add_move_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK,
838                     GTK_MOVEMENT_PARAGRAPHS, -1);
839   
840   add_move_binding (binding_set, GDK_Down, GDK_CONTROL_MASK,
841                     GTK_MOVEMENT_PARAGRAPHS, 1);
842
843   add_move_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK,
844                     GTK_MOVEMENT_PARAGRAPHS, 1);
845   
846   add_move_binding (binding_set, GDK_Home, 0,
847                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
848
849   add_move_binding (binding_set, GDK_KP_Home, 0,
850                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
851   
852   add_move_binding (binding_set, GDK_End, 0,
853                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
854
855   add_move_binding (binding_set, GDK_KP_End, 0,
856                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
857   
858   add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
859                     GTK_MOVEMENT_BUFFER_ENDS, -1);
860
861   add_move_binding (binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
862                     GTK_MOVEMENT_BUFFER_ENDS, -1);
863   
864   add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
865                     GTK_MOVEMENT_BUFFER_ENDS, 1);
866
867   add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
868                     GTK_MOVEMENT_BUFFER_ENDS, 1);
869   
870   add_move_binding (binding_set, GDK_Page_Up, 0,
871                     GTK_MOVEMENT_PAGES, -1);
872
873   add_move_binding (binding_set, GDK_KP_Page_Up, 0,
874                     GTK_MOVEMENT_PAGES, -1);
875   
876   add_move_binding (binding_set, GDK_Page_Down, 0,
877                     GTK_MOVEMENT_PAGES, 1);
878
879   add_move_binding (binding_set, GDK_KP_Page_Down, 0,
880                     GTK_MOVEMENT_PAGES, 1);
881
882   add_move_binding (binding_set, GDK_Page_Up, GDK_CONTROL_MASK,
883                     GTK_MOVEMENT_HORIZONTAL_PAGES, -1);
884
885   add_move_binding (binding_set, GDK_KP_Page_Up, GDK_CONTROL_MASK,
886                     GTK_MOVEMENT_HORIZONTAL_PAGES, -1);
887   
888   add_move_binding (binding_set, GDK_Page_Down, GDK_CONTROL_MASK,
889                     GTK_MOVEMENT_HORIZONTAL_PAGES, 1);
890
891   add_move_binding (binding_set, GDK_KP_Page_Down, GDK_CONTROL_MASK,
892                     GTK_MOVEMENT_HORIZONTAL_PAGES, 1);
893
894   /* Select all */
895   gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
896                                 "select_all", 1,
897                                 G_TYPE_BOOLEAN, TRUE);
898
899   gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
900                                 "select_all", 1,
901                                 G_TYPE_BOOLEAN, TRUE);
902   
903   /* Unselect all */
904   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_CONTROL_MASK,
905                                  "select_all", 1,
906                                  G_TYPE_BOOLEAN, FALSE);
907
908   /* Deleting text */
909   gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
910                                 "delete_from_cursor", 2,
911                                 G_TYPE_ENUM, GTK_DELETE_CHARS,
912                                 G_TYPE_INT, 1);
913
914   gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, 0,
915                                 "delete_from_cursor", 2,
916                                 G_TYPE_ENUM, GTK_DELETE_CHARS,
917                                 G_TYPE_INT, 1);
918   
919   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
920                                 "delete_from_cursor", 2,
921                                 G_TYPE_ENUM, GTK_DELETE_CHARS,
922                                 G_TYPE_INT, -1);
923
924   /* Make this do the same as Backspace, to help with mis-typing */
925   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_SHIFT_MASK,
926                                 "delete_from_cursor", 2,
927                                 G_TYPE_ENUM, GTK_DELETE_CHARS,
928                                 G_TYPE_INT, -1);
929
930   gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
931                                 "delete_from_cursor", 2,
932                                 G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
933                                 G_TYPE_INT, 1);
934
935   gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, GDK_CONTROL_MASK,
936                                 "delete_from_cursor", 2,
937                                 G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
938                                 G_TYPE_INT, 1);
939   
940   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
941                                 "delete_from_cursor", 2,
942                                 G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
943                                 G_TYPE_INT, -1);
944
945   /* Cut/copy/paste */
946
947   gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
948                                 "cut_clipboard", 0);
949   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
950                                 "copy_clipboard", 0);
951   gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
952                                 "paste_clipboard", 0);
953
954   gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_SHIFT_MASK,
955                                 "cut_clipboard", 0);
956   gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_CONTROL_MASK,
957                                 "copy_clipboard", 0);
958   gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_SHIFT_MASK,
959                                 "paste_clipboard", 0);
960
961   /* Overwrite */
962   gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
963                                 "toggle_overwrite", 0);
964   gtk_binding_entry_add_signal (binding_set, GDK_KP_Insert, 0,
965                                 "toggle_overwrite", 0);
966
967   /* Control-tab focus motion */
968   gtk_binding_entry_add_signal (binding_set, GDK_Tab, GDK_CONTROL_MASK,
969                                 "move_focus", 1,
970                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
971   gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, GDK_CONTROL_MASK,
972                                 "move_focus", 1,
973                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
974   
975   gtk_binding_entry_add_signal (binding_set, GDK_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
976                                 "move_focus", 1,
977                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
978   gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
979                                 "move_focus", 1,
980                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
981 }
982
983 static void
984 gtk_text_view_init (GtkTextView *text_view)
985 {
986   GtkWidget *widget;
987
988   widget = GTK_WIDGET (text_view);
989
990   GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
991
992   /* Set up default style */
993   text_view->wrap_mode = GTK_WRAP_NONE;
994   text_view->pixels_above_lines = 0;
995   text_view->pixels_below_lines = 0;
996   text_view->pixels_inside_wrap = 0;
997   text_view->justify = GTK_JUSTIFY_LEFT;
998   text_view->left_margin = 0;
999   text_view->right_margin = 0;
1000   text_view->indent = 0;
1001   text_view->tabs = NULL;
1002   text_view->editable = TRUE;
1003
1004   gtk_drag_dest_set (widget,
1005                      0,
1006                      target_table, G_N_ELEMENTS (target_table),
1007                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
1008
1009   text_view->virtual_cursor_x = -1;
1010   text_view->virtual_cursor_y = -1;
1011
1012   /* This object is completely private. No external entity can gain a reference
1013    * to it; so we create it here and destroy it in finalize ().
1014    */
1015   text_view->im_context = gtk_im_multicontext_new ();
1016
1017   g_signal_connect (text_view->im_context, "commit",
1018                     G_CALLBACK (gtk_text_view_commit_handler), text_view);
1019   g_signal_connect (text_view->im_context, "preedit_changed",
1020                     G_CALLBACK (gtk_text_view_preedit_changed_handler), text_view);
1021   g_signal_connect (text_view->im_context, "retrieve_surrounding",
1022                     G_CALLBACK (gtk_text_view_retrieve_surrounding_handler), text_view);
1023   g_signal_connect (text_view->im_context, "delete_surrounding",
1024                     G_CALLBACK (gtk_text_view_delete_surrounding_handler), text_view);
1025
1026   text_view->cursor_visible = TRUE;
1027
1028   text_view->text_window = text_window_new (GTK_TEXT_WINDOW_TEXT,
1029                                             widget, 200, 200);
1030
1031   text_view->drag_start_x = -1;
1032   text_view->drag_start_y = -1;
1033
1034   text_view->pending_place_cursor_button = 0;
1035
1036   /* We handle all our own redrawing */
1037   gtk_widget_set_redraw_on_allocate (widget, FALSE);
1038 }
1039
1040 /**
1041  * gtk_text_view_new:
1042  *
1043  * Creates a new #GtkTextView. If you don't call gtk_text_view_set_buffer()
1044  * before using the text view, an empty default buffer will be created
1045  * for you. Get the buffer with gtk_text_view_get_buffer(). If you want
1046  * to specify your own buffer, consider gtk_text_view_new_with_buffer().
1047  *
1048  * Return value: a new #GtkTextView
1049  **/
1050 GtkWidget*
1051 gtk_text_view_new (void)
1052 {
1053   return g_object_new (GTK_TYPE_TEXT_VIEW, NULL);
1054 }
1055
1056 /**
1057  * gtk_text_view_new_with_buffer:
1058  * @buffer: a #GtkTextBuffer
1059  *
1060  * Creates a new #GtkTextView widget displaying the buffer
1061  * @buffer. One buffer can be shared among many widgets.
1062  * @buffer may be NULL to create a default buffer, in which case
1063  * this function is equivalent to gtk_text_view_new(). The
1064  * text view adds its own reference count to the buffer; it does not
1065  * take over an existing reference.
1066  *
1067  * Return value: a new #GtkTextView.
1068  **/
1069 GtkWidget*
1070 gtk_text_view_new_with_buffer (GtkTextBuffer *buffer)
1071 {
1072   GtkTextView *text_view;
1073
1074   text_view = (GtkTextView*)gtk_text_view_new ();
1075
1076   gtk_text_view_set_buffer (text_view, buffer);
1077
1078   return GTK_WIDGET (text_view);
1079 }
1080
1081 /**
1082  * gtk_text_view_set_buffer:
1083  * @text_view: a #GtkTextView
1084  * @buffer: a #GtkTextBuffer
1085  *
1086  * Sets @buffer as the buffer being displayed by @text_view. The previous
1087  * buffer displayed by the text view is unreferenced, and a reference is
1088  * added to @buffer. If you owned a reference to @buffer before passing it
1089  * to this function, you must remove that reference yourself; #GtkTextView
1090  * will not "adopt" it.
1091  *
1092  **/
1093 void
1094 gtk_text_view_set_buffer (GtkTextView   *text_view,
1095                           GtkTextBuffer *buffer)
1096 {
1097   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1098   g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
1099
1100   if (text_view->buffer == buffer)
1101     return;
1102
1103   if (text_view->buffer != NULL)
1104     {
1105       /* Destroy all anchored children */
1106       GSList *tmp_list;
1107       GSList *copy;
1108
1109       copy = g_slist_copy (text_view->children);
1110       tmp_list = copy;
1111       while (tmp_list != NULL)
1112         {
1113           GtkTextViewChild *vc = tmp_list->data;
1114
1115           if (vc->anchor)
1116             {
1117               gtk_widget_destroy (vc->widget);
1118               /* vc may now be invalid! */
1119             }
1120
1121           tmp_list = g_slist_next (tmp_list);
1122         }
1123
1124       g_slist_free (copy);
1125
1126       g_signal_handlers_disconnect_by_func (text_view->buffer,
1127                                             gtk_text_view_mark_set_handler,
1128                                             text_view);
1129       g_object_unref (text_view->buffer);
1130       text_view->dnd_mark = NULL;
1131
1132       if (GTK_WIDGET_REALIZED (text_view))
1133         {
1134           GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
1135                                                               GDK_SELECTION_PRIMARY);
1136           gtk_text_buffer_remove_selection_clipboard (text_view->buffer, clipboard);
1137         }
1138     }
1139
1140   text_view->buffer = buffer;
1141
1142   if (buffer != NULL)
1143     {
1144       GtkTextIter start;
1145
1146       g_object_ref (buffer);
1147
1148       if (text_view->layout)
1149         gtk_text_layout_set_buffer (text_view->layout, buffer);
1150
1151       gtk_text_buffer_get_iter_at_offset (text_view->buffer, &start, 0);
1152
1153       text_view->dnd_mark = gtk_text_buffer_create_mark (text_view->buffer,
1154                                                          "gtk_drag_target",
1155                                                          &start, FALSE);
1156
1157       text_view->first_para_mark = gtk_text_buffer_create_mark (text_view->buffer,
1158                                                                 NULL,
1159                                                                 &start, TRUE);
1160
1161       text_view->first_para_pixels = 0;
1162
1163       g_signal_connect (text_view->buffer, "mark_set",
1164                         G_CALLBACK (gtk_text_view_mark_set_handler), text_view);
1165
1166       if (GTK_WIDGET_REALIZED (text_view))
1167         {
1168           GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
1169                                                               GDK_SELECTION_PRIMARY);
1170           gtk_text_buffer_add_selection_clipboard (text_view->buffer, clipboard);
1171         }
1172     }
1173
1174   g_object_notify (G_OBJECT (text_view), "buffer");
1175   
1176   if (GTK_WIDGET_VISIBLE (text_view))
1177     gtk_widget_queue_draw (GTK_WIDGET (text_view));
1178
1179   DV(g_print ("Invalidating due to set_buffer\n"));
1180   gtk_text_view_invalidate (text_view);
1181 }
1182
1183 static GtkTextBuffer*
1184 get_buffer (GtkTextView *text_view)
1185 {
1186   if (text_view->buffer == NULL)
1187     {
1188       GtkTextBuffer *b;
1189       b = gtk_text_buffer_new (NULL);
1190       gtk_text_view_set_buffer (text_view, b);
1191       g_object_unref (b);
1192     }
1193
1194   return text_view->buffer;
1195 }
1196
1197 /**
1198  * gtk_text_view_get_buffer:
1199  * @text_view: a #GtkTextView
1200  *
1201  * Returns the #GtkTextBuffer being displayed by this text view.
1202  * The reference count on the buffer is not incremented; the caller
1203  * of this function won't own a new reference.
1204  *
1205  * Return value: a #GtkTextBuffer
1206  **/
1207 GtkTextBuffer*
1208 gtk_text_view_get_buffer (GtkTextView *text_view)
1209 {
1210   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
1211
1212   return get_buffer (text_view);
1213 }
1214
1215 /**
1216  * gtk_text_view_get_iter_at_location:
1217  * @text_view: a #GtkTextView
1218  * @iter: a #GtkTextIter
1219  * @x: x position, in buffer coordinates
1220  * @y: y position, in buffer coordinates
1221  *
1222  * Retrieves the iterator at buffer coordinates @x and @y. Buffer
1223  * coordinates are coordinates for the entire buffer, not just the
1224  * currently-displayed portion.  If you have coordinates from an
1225  * event, you have to convert those to buffer coordinates with
1226  * gtk_text_view_window_to_buffer_coords().
1227  *
1228  **/
1229 void
1230 gtk_text_view_get_iter_at_location (GtkTextView *text_view,
1231                                     GtkTextIter *iter,
1232                                     gint         x,
1233                                     gint         y)
1234 {
1235   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1236   g_return_if_fail (iter != NULL);
1237
1238   gtk_text_view_ensure_layout (text_view);
1239   
1240   gtk_text_layout_get_iter_at_pixel (text_view->layout,
1241                                      iter,
1242                                      x,
1243                                      y);
1244 }
1245
1246 /**
1247  * gtk_text_view_get_iter_location:
1248  * @text_view: a #GtkTextView
1249  * @iter: a #GtkTextIter
1250  * @location: bounds of the character at @iter
1251  *
1252  * Gets a rectangle which roughly contains the character at @iter.
1253  * The rectangle position is in buffer coordinates; use
1254  * gtk_text_view_buffer_to_window_coords() to convert these
1255  * coordinates to coordinates for one of the windows in the text view.
1256  *
1257  **/
1258 void
1259 gtk_text_view_get_iter_location (GtkTextView       *text_view,
1260                                  const GtkTextIter *iter,
1261                                  GdkRectangle      *location)
1262 {
1263   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1264   g_return_if_fail (gtk_text_iter_get_buffer (iter) == get_buffer (text_view));
1265
1266   gtk_text_view_ensure_layout (text_view);
1267   
1268   gtk_text_layout_get_iter_location (text_view->layout, iter, location);
1269 }
1270
1271 /**
1272  * gtk_text_view_get_line_yrange:
1273  * @text_view: a #GtkTextView
1274  * @iter: a #GtkTextIter
1275  * @y: return location for a y coordinate
1276  * @height: return location for a height
1277  *
1278  * Gets the y coordinate of the top of the line containing @iter,
1279  * and the height of the line. The coordinate is a buffer coordinate;
1280  * convert to window coordinates with gtk_text_view_buffer_to_window_coords().
1281  *
1282  **/
1283 void
1284 gtk_text_view_get_line_yrange (GtkTextView       *text_view,
1285                                const GtkTextIter *iter,
1286                                gint              *y,
1287                                gint              *height)
1288 {
1289   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1290   g_return_if_fail (gtk_text_iter_get_buffer (iter) == get_buffer (text_view));
1291
1292   gtk_text_view_ensure_layout (text_view);
1293   
1294   gtk_text_layout_get_line_yrange (text_view->layout,
1295                                    iter,
1296                                    y,
1297                                    height);
1298 }
1299
1300 /**
1301  * gtk_text_view_get_line_at_y:
1302  * @text_view: a #GtkTextView
1303  * @target_iter: a #GtkTextIter
1304  * @y: a y coordinate
1305  * @line_top: return location for top coordinate of the line
1306  *
1307  * Gets the #GtkTextIter at the start of the line containing
1308  * the coordinate @y. @y is in buffer coordinates, convert from
1309  * window coordinates with gtk_text_view_window_to_buffer_coords().
1310  * If non-%NULL, @line_top will be filled with the coordinate of the top
1311  * edge of the line.
1312  **/
1313 void
1314 gtk_text_view_get_line_at_y (GtkTextView *text_view,
1315                              GtkTextIter *target_iter,
1316                              gint         y,
1317                              gint        *line_top)
1318 {
1319   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1320
1321   gtk_text_view_ensure_layout (text_view);
1322   
1323   gtk_text_layout_get_line_at_y (text_view->layout,
1324                                  target_iter,
1325                                  y,
1326                                  line_top);
1327 }
1328
1329 static gboolean
1330 set_adjustment_clamped (GtkAdjustment *adj, gdouble val)
1331 {
1332   DV (g_print ("  Setting adj to raw value %g\n", val));
1333   
1334   /* We don't really want to clamp to upper; we want to clamp to
1335      upper - page_size which is the highest value the scrollbar
1336      will let us reach. */
1337   if (val > (adj->upper - adj->page_size))
1338     val = adj->upper - adj->page_size;
1339
1340   if (val < adj->lower)
1341     val = adj->lower;
1342
1343   if (val != adj->value)
1344     {
1345       DV (g_print ("  Setting adj to clamped value %g\n", val));
1346       gtk_adjustment_set_value (adj, val);
1347       return TRUE;
1348     }
1349   else
1350     return FALSE;
1351 }
1352
1353 /**
1354  * gtk_text_view_scroll_to_iter:
1355  * @text_view: a #GtkTextView
1356  * @iter: a #GtkTextIter
1357  * @within_margin: margin as a [0.0,0.5) fraction of screen size
1358  * @use_align: whether to use alignment arguments (if %FALSE, just get the mark onscreen)
1359  * @xalign: horizontal alignment of mark within visible area.
1360  * @yalign: vertical alignment of mark within visible area
1361  *
1362  * Scrolls @text_view so that @iter is on the screen in the position
1363  * indicated by @xalign and @yalign. An alignment of 0.0 indicates
1364  * left or top, 1.0 indicates right or bottom, 0.5 means center. If @use_align
1365  * is %FALSE, the text scrolls the minimal distance to get the mark onscreen,
1366  * possibly not scrolling at all. The effective screen for purposes
1367  * of this function is reduced by a margin of size @within_margin.
1368  * NOTE: This function uses the currently-computed height of the
1369  * lines in the text buffer. Note that line heights are computed
1370  * in an idle handler; so this function may not have the desired effect
1371  * if it's called before the height computations. To avoid oddness,
1372  * consider using gtk_text_view_scroll_to_mark() which saves a point
1373  * to be scrolled to after line validation.
1374  *
1375  * Return value: %TRUE if scrolling occurred
1376  **/
1377 gboolean
1378 gtk_text_view_scroll_to_iter (GtkTextView   *text_view,
1379                               GtkTextIter   *iter,
1380                               gdouble        within_margin,
1381                               gboolean       use_align,
1382                               gdouble        xalign,
1383                               gdouble        yalign)
1384 {
1385   GdkRectangle rect;
1386   GdkRectangle screen;
1387   gint screen_bottom;
1388   gint screen_right;
1389   gint scroll_dest;
1390   GtkWidget *widget;
1391   gboolean retval = FALSE;
1392   gint scroll_inc;
1393   gint screen_xoffset, screen_yoffset;
1394   gint current_x_scroll, current_y_scroll;
1395
1396   /* FIXME why don't we do the validate-at-scroll-destination thing
1397    * from flush_scroll in this function? I think it wasn't done before
1398    * because changed_handler was screwed up, but I could be wrong.
1399    */
1400   
1401   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1402   g_return_val_if_fail (iter != NULL, FALSE);
1403   g_return_val_if_fail (within_margin >= 0.0 && within_margin < 0.5, FALSE);
1404   g_return_val_if_fail (xalign >= 0.0 && xalign <= 1.0, FALSE);
1405   g_return_val_if_fail (yalign >= 0.0 && yalign <= 1.0, FALSE);
1406   
1407   widget = GTK_WIDGET (text_view);
1408
1409   DV(g_print(G_STRLOC"\n"));
1410   
1411   gtk_text_layout_get_iter_location (text_view->layout,
1412                                      iter,
1413                                      &rect);
1414
1415   DV (g_print (" target rect %d,%d  %d x %d\n", rect.x, rect.y, rect.width, rect.height));
1416   
1417   current_x_scroll = text_view->xoffset;
1418   current_y_scroll = text_view->yoffset;
1419
1420   screen.x = current_x_scroll;
1421   screen.y = current_y_scroll;
1422   screen.width = SCREEN_WIDTH (widget);
1423   screen.height = SCREEN_HEIGHT (widget);
1424   
1425   screen_xoffset = screen.width * within_margin;
1426   screen_yoffset = screen.height * within_margin;
1427   
1428   screen.x += screen_xoffset;
1429   screen.y += screen_yoffset;
1430   screen.width -= screen_xoffset * 2;
1431   screen.height -= screen_yoffset * 2;
1432
1433   /* paranoia check */
1434   if (screen.width < 1)
1435     screen.width = 1;
1436   if (screen.height < 1)
1437     screen.height = 1;
1438   
1439   /* The -1 here ensures that we leave enough space to draw the cursor
1440    * when this function is used for horizontal scrolling. 
1441    */
1442   screen_right = screen.x + screen.width - 1;
1443   screen_bottom = screen.y + screen.height;
1444   
1445   /* The alignment affects the point in the target character that we
1446    * choose to align. If we're doing right/bottom alignment, we align
1447    * the right/bottom edge of the character the mark is at; if we're
1448    * doing left/top we align the left/top edge of the character; if
1449    * we're doing center alignment we align the center of the
1450    * character.
1451    */
1452   
1453   /* Vertical scroll */
1454
1455   scroll_inc = 0;
1456   scroll_dest = current_y_scroll;
1457   
1458   if (use_align)
1459     {      
1460       scroll_dest = rect.y + (rect.height * yalign) - (screen.height * yalign);
1461       
1462       /* if scroll_dest < screen.y, we move a negative increment (up),
1463        * else a positive increment (down)
1464        */
1465       scroll_inc = scroll_dest - screen.y + screen_yoffset;
1466     }
1467   else
1468     {
1469       /* move minimum to get onscreen */
1470       if (rect.y < screen.y)
1471         {
1472           scroll_dest = rect.y;
1473           scroll_inc = scroll_dest - screen.y - screen_yoffset;
1474         }
1475       else if ((rect.y + rect.height) > screen_bottom)
1476         {
1477           scroll_dest = rect.y + rect.height;
1478           scroll_inc = scroll_dest - screen_bottom + screen_yoffset;
1479         }
1480     }  
1481   
1482   if (scroll_inc != 0)
1483     {
1484       retval = set_adjustment_clamped (get_vadjustment (text_view),
1485                                        current_y_scroll + scroll_inc);
1486
1487       DV (g_print (" vert increment %d\n", scroll_inc));
1488     }
1489
1490   /* Horizontal scroll */
1491   
1492   scroll_inc = 0;
1493   scroll_dest = current_x_scroll;
1494   
1495   if (use_align)
1496     {      
1497       scroll_dest = rect.x + (rect.width * xalign) - (screen.width * xalign);
1498
1499       /* if scroll_dest < screen.y, we move a negative increment (left),
1500        * else a positive increment (right)
1501        */
1502       scroll_inc = scroll_dest - screen.x + screen_xoffset;
1503     }
1504   else
1505     {
1506       /* move minimum to get onscreen */
1507       if (rect.x < screen.x)
1508         {
1509           scroll_dest = rect.x;
1510           scroll_inc = scroll_dest - screen.x - screen_xoffset;
1511         }
1512       else if ((rect.x + rect.width) > screen_right)
1513         {
1514           scroll_dest = rect.x + rect.width;
1515           scroll_inc = scroll_dest - screen_right + screen_xoffset;
1516         }
1517     }
1518   
1519   if (scroll_inc != 0)
1520     {
1521       retval = set_adjustment_clamped (get_hadjustment (text_view),
1522                                        current_x_scroll + scroll_inc);
1523
1524       DV (g_print (" horiz increment %d\n", scroll_inc));
1525     }
1526   
1527   if (retval)
1528     DV(g_print (">Actually scrolled ("G_STRLOC")\n"));
1529   else
1530     DV(g_print (">Didn't end up scrolling ("G_STRLOC")\n"));
1531   
1532   return retval;
1533 }
1534
1535 static void
1536 free_pending_scroll (GtkTextPendingScroll *scroll)
1537 {
1538   if (!gtk_text_mark_get_deleted (scroll->mark))
1539     gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (scroll->mark),
1540                                  scroll->mark);
1541   g_object_unref (scroll->mark);
1542   g_free (scroll);
1543 }
1544
1545 static void
1546 cancel_pending_scroll (GtkTextView *text_view)
1547 {
1548   if (text_view->pending_scroll)
1549     {
1550       free_pending_scroll (text_view->pending_scroll);
1551       text_view->pending_scroll = NULL;
1552     }
1553 }
1554     
1555 static void
1556 gtk_text_view_queue_scroll (GtkTextView   *text_view,
1557                             GtkTextMark   *mark,
1558                             gdouble        within_margin,
1559                             gboolean       use_align,
1560                             gdouble        xalign,
1561                             gdouble        yalign)
1562 {
1563   GtkTextIter iter;
1564   GtkTextPendingScroll *scroll;
1565
1566   DV(g_print(G_STRLOC"\n"));
1567   
1568   scroll = g_new (GtkTextPendingScroll, 1);
1569
1570   scroll->within_margin = within_margin;
1571   scroll->use_align = use_align;
1572   scroll->xalign = xalign;
1573   scroll->yalign = yalign;
1574   
1575   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, mark);
1576
1577   scroll->mark = gtk_text_buffer_create_mark (get_buffer (text_view),
1578                                               NULL,
1579                                               &iter,
1580                                               gtk_text_mark_get_left_gravity (mark));
1581
1582   g_object_ref (scroll->mark);
1583   
1584   cancel_pending_scroll (text_view);
1585
1586   text_view->pending_scroll = scroll;
1587 }
1588
1589 static gboolean
1590 gtk_text_view_flush_scroll (GtkTextView *text_view)
1591 {
1592   GtkTextIter iter;
1593   GtkTextPendingScroll *scroll;
1594   gboolean retval;
1595   GtkWidget *widget;
1596
1597   widget = GTK_WIDGET (text_view);
1598   
1599   DV(g_print(G_STRLOC"\n"));
1600   
1601   if (text_view->pending_scroll == NULL)
1602     {
1603       DV (g_print ("in flush scroll, no pending scroll\n"));
1604       return FALSE;
1605     }
1606
1607   scroll = text_view->pending_scroll;
1608
1609   /* avoid recursion */
1610   text_view->pending_scroll = NULL;
1611   
1612   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, scroll->mark);
1613
1614   /* Validate area around the scroll destination, so the adjustment
1615    * can meaningfully point into that area. We must validate
1616    * enough area to be sure that after we scroll, everything onscreen
1617    * is valid; otherwise, validation will maintain the first para
1618    * in one place, but may push the target iter off the bottom of
1619    * the screen.
1620    */
1621   DV(g_print (">Validating scroll destination ("G_STRLOC")\n"));
1622   gtk_text_layout_validate_yrange (text_view->layout, &iter,
1623                                    - (widget->allocation.height * 2),
1624                                    widget->allocation.height * 2);
1625   
1626   DV(g_print (">Done validating scroll destination ("G_STRLOC")\n"));
1627
1628   /* Ensure we have updated width/height */
1629   gtk_text_view_update_adjustments (text_view);
1630   
1631   retval = gtk_text_view_scroll_to_iter (text_view,
1632                                          &iter,
1633                                          scroll->within_margin,
1634                                          scroll->use_align,
1635                                          scroll->xalign,
1636                                          scroll->yalign);
1637   
1638   free_pending_scroll (scroll);
1639
1640   return retval;
1641 }
1642
1643 static void
1644 gtk_text_view_set_adjustment_upper (GtkAdjustment *adj, gdouble upper)
1645 {  
1646   if (upper != adj->upper)
1647     {
1648       gdouble min = MAX (0.0, upper - adj->page_size);
1649       gboolean value_changed = FALSE;
1650
1651       adj->upper = upper;
1652
1653       if (adj->value > min)
1654         {
1655           adj->value = min;
1656           value_changed = TRUE;
1657         }
1658
1659       gtk_adjustment_changed (adj);
1660       DV(g_print(">Changed adj upper to %g ("G_STRLOC")\n", upper));
1661       
1662       if (value_changed)
1663         {
1664           DV(g_print(">Changed adj value because upper decreased ("G_STRLOC")\n"));
1665           gtk_adjustment_value_changed (adj);
1666         }
1667     }
1668 }
1669
1670 static void
1671 gtk_text_view_update_adjustments (GtkTextView *text_view)
1672 {
1673   gint width = 0, height = 0;
1674
1675   DV(g_print(">Updating adjustments ("G_STRLOC")\n"));
1676
1677   if (text_view->layout)
1678     gtk_text_layout_get_size (text_view->layout, &width, &height);
1679
1680   /* Make room for the cursor after the last character in the widest line */
1681   width++;
1682
1683   if (text_view->width != width || text_view->height != height)
1684     {
1685       text_view->width = width;
1686       text_view->height = height;
1687
1688       gtk_text_view_set_adjustment_upper (get_hadjustment (text_view),
1689                                           MAX (SCREEN_WIDTH (text_view), width));
1690       gtk_text_view_set_adjustment_upper (get_vadjustment (text_view),
1691                                           MAX (SCREEN_HEIGHT (text_view), height));
1692       
1693       /* hadj/vadj exist since we called get_hadjustment/get_vadjustment above */
1694
1695       /* Set up the step sizes; we'll say that a page is
1696          our allocation minus one step, and a step is
1697          1/10 of our allocation. */
1698       text_view->hadjustment->step_increment =
1699         SCREEN_WIDTH (text_view) / 10.0;
1700       text_view->hadjustment->page_increment =
1701         SCREEN_WIDTH (text_view) * 0.9;
1702       
1703       text_view->vadjustment->step_increment =
1704         SCREEN_HEIGHT (text_view) / 10.0;
1705       text_view->vadjustment->page_increment =
1706         SCREEN_HEIGHT (text_view) * 0.9;
1707
1708       gtk_adjustment_changed (get_hadjustment (text_view));
1709       gtk_adjustment_changed (get_vadjustment (text_view));
1710     }
1711 }
1712
1713 static void
1714 gtk_text_view_update_layout_width (GtkTextView *text_view)
1715 {
1716   DV(g_print(">Updating layout width ("G_STRLOC")\n"));
1717   
1718   gtk_text_view_ensure_layout (text_view);
1719
1720   gtk_text_layout_set_screen_width (text_view->layout,
1721                                     SCREEN_WIDTH (text_view));
1722 }
1723
1724 static void
1725 gtk_text_view_update_im_spot_location (GtkTextView *text_view)
1726 {
1727   GdkRectangle area;
1728   gint cursor_x_pos, cursor_y_pos;
1729
1730   if (text_view->layout == NULL)
1731     return;
1732   
1733   gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
1734
1735   area.x = cursor_x_pos;
1736   area.y = cursor_y_pos;
1737   area.width = area.height = 0;
1738
1739   gtk_im_context_set_cursor_location (text_view->im_context, &area);
1740 }
1741
1742 /**
1743  * gtk_text_view_scroll_to_mark:
1744  * @text_view: a #GtkTextView
1745  * @mark: a #GtkTextMark
1746  * @within_margin: margin as a [0.0,0.5) fraction of screen size
1747  * @use_align: whether to use alignment arguments (if %FALSE, just get the mark onscreen)
1748  * @xalign: horizontal alignment of mark within visible area.
1749  * @yalign: vertical alignment of mark within visible area
1750  *
1751  * Scrolls @text_view so that @mark is on the screen in the position
1752  * indicated by @xalign and @yalign. An alignment of 0.0 indicates
1753  * left or top, 1.0 indicates right or bottom, 0.5 means center. If @use_align
1754  * is %FALSE, the text scrolls the minimal distance to get the mark onscreen,
1755  * possibly not scrolling at all. The effective screen for purposes
1756  * of this function is reduced by a margin of size @within_margin.
1757  *
1758  **/
1759 void
1760 gtk_text_view_scroll_to_mark (GtkTextView *text_view,
1761                               GtkTextMark *mark,
1762                               gdouble      within_margin,
1763                               gboolean     use_align,
1764                               gdouble      xalign,
1765                               gdouble      yalign)
1766 {  
1767   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1768   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
1769   g_return_if_fail (within_margin >= 0.0 && within_margin < 0.5);
1770   g_return_if_fail (xalign >= 0.0 && xalign <= 1.0);
1771   g_return_if_fail (yalign >= 0.0 && yalign <= 1.0);
1772
1773   gtk_text_view_queue_scroll (text_view, mark,
1774                               within_margin,
1775                               use_align,
1776                               xalign,
1777                               yalign);
1778
1779   /* If no validation is pending, we need to go ahead and force an
1780    * immediate scroll.
1781    */
1782   if (text_view->layout &&
1783       gtk_text_layout_is_valid (text_view->layout))
1784     gtk_text_view_flush_scroll (text_view);
1785 }
1786
1787 /**
1788  * gtk_text_view_scroll_mark_onscreen:
1789  * @text_view: a #GtkTextView
1790  * @mark: a mark in the buffer for @text_view
1791  * 
1792  * Scrolls @text_view the minimum distance such that @mark is contained
1793  * within the visible area of the widget.
1794  * 
1795  **/
1796 void
1797 gtk_text_view_scroll_mark_onscreen (GtkTextView *text_view,
1798                                     GtkTextMark *mark)
1799 {
1800   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1801   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
1802
1803   gtk_text_view_scroll_to_mark (text_view, mark, 0.0, FALSE, 0.0, 0.0);
1804 }
1805
1806 static gboolean
1807 clamp_iter_onscreen (GtkTextView *text_view, GtkTextIter *iter)
1808 {
1809   GdkRectangle visible_rect;
1810   gtk_text_view_get_visible_rect (text_view, &visible_rect);
1811
1812   return gtk_text_layout_clamp_iter_to_vrange (text_view->layout, iter,
1813                                                visible_rect.y,
1814                                                visible_rect.y + visible_rect.height);
1815 }
1816
1817 /**
1818  * gtk_text_view_move_mark_onscreen:
1819  * @text_view: a #GtkTextView
1820  * @mark: a #GtkTextMark
1821  *
1822  * Moves a mark within the buffer so that it's
1823  * located within the currently-visible text area.
1824  *
1825  * Return value: %TRUE if the mark moved (wasn't already onscreen)
1826  **/
1827 gboolean
1828 gtk_text_view_move_mark_onscreen (GtkTextView *text_view,
1829                                   GtkTextMark *mark)
1830 {
1831   GtkTextIter iter;
1832
1833   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1834   g_return_val_if_fail (mark != NULL, FALSE);
1835
1836   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, mark);
1837
1838   if (clamp_iter_onscreen (text_view, &iter))
1839     {
1840       gtk_text_buffer_move_mark (get_buffer (text_view), mark, &iter);
1841       return TRUE;
1842     }
1843   else
1844     return FALSE;
1845 }
1846
1847 /**
1848  * gtk_text_view_get_visible_rect:
1849  * @text_view: a #GtkTextView
1850  * @visible_rect: rectangle to fill
1851  *
1852  * Fills @visible_rect with the currently-visible
1853  * region of the buffer, in buffer coordinates. Convert to window coordinates
1854  * with gtk_text_view_buffer_to_window_coords().
1855  **/
1856 void
1857 gtk_text_view_get_visible_rect (GtkTextView  *text_view,
1858                                 GdkRectangle *visible_rect)
1859 {
1860   GtkWidget *widget;
1861
1862   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1863
1864   widget = GTK_WIDGET (text_view);
1865
1866   if (visible_rect)
1867     {
1868       visible_rect->x = text_view->xoffset;
1869       visible_rect->y = text_view->yoffset;
1870       visible_rect->width = SCREEN_WIDTH (widget);
1871       visible_rect->height = SCREEN_HEIGHT (widget);
1872
1873       DV(g_print(" visible rect: %d,%d %d x %d\n",
1874                  visible_rect->x,
1875                  visible_rect->y,
1876                  visible_rect->width,
1877                  visible_rect->height));
1878     }
1879 }
1880
1881 /**
1882  * gtk_text_view_set_wrap_mode:
1883  * @text_view: a #GtkTextView
1884  * @wrap_mode: a #GtkWrapMode
1885  *
1886  * Sets the line wrapping for the view.
1887  **/
1888 void
1889 gtk_text_view_set_wrap_mode (GtkTextView *text_view,
1890                              GtkWrapMode  wrap_mode)
1891 {
1892   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1893
1894   if (text_view->wrap_mode != wrap_mode)
1895     {
1896       text_view->wrap_mode = wrap_mode;
1897
1898       if (text_view->layout)
1899         {
1900           text_view->layout->default_style->wrap_mode = wrap_mode;
1901           gtk_text_layout_default_style_changed (text_view->layout);
1902         }
1903     }
1904
1905   g_object_notify (G_OBJECT (text_view), "wrap_mode");
1906 }
1907
1908 /**
1909  * gtk_text_view_get_wrap_mode:
1910  * @text_view: a #GtkTextView
1911  *
1912  * Gets the line wrapping for the view.
1913  *
1914  * Return value: the line wrap setting
1915  **/
1916 GtkWrapMode
1917 gtk_text_view_get_wrap_mode (GtkTextView *text_view)
1918 {
1919   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_WRAP_NONE);
1920
1921   return text_view->wrap_mode;
1922 }
1923
1924 /**
1925  * gtk_text_view_set_editable:
1926  * @text_view: a #GtkTextView
1927  * @setting: whether it's editable
1928  *
1929  * Sets the default editability of the #GtkTextView. You can override
1930  * this default setting with tags in the buffer, using the "editable"
1931  * attribute of tags.
1932  **/
1933 void
1934 gtk_text_view_set_editable (GtkTextView *text_view,
1935                             gboolean     setting)
1936 {
1937   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1938   setting = setting != FALSE;
1939
1940   if (text_view->editable != setting)
1941     {
1942       text_view->editable = setting;
1943
1944       if (text_view->layout)
1945         {
1946           text_view->layout->default_style->editable = text_view->editable;
1947           gtk_text_layout_default_style_changed (text_view->layout);
1948         }
1949
1950       g_object_notify (G_OBJECT (text_view), "editable");
1951     }
1952 }
1953
1954 /**
1955  * gtk_text_view_get_editable:
1956  * @text_view: a #GtkTextView
1957  *
1958  * Returns the default editability of the #GtkTextView. Tags in the
1959  * buffer may override this setting for some ranges of text.
1960  *
1961  * Return value: whether text is editable by default
1962  **/
1963 gboolean
1964 gtk_text_view_get_editable (GtkTextView *text_view)
1965 {
1966   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1967
1968   return text_view->editable;
1969 }
1970
1971 /**
1972  * gtk_text_view_set_pixels_above_lines:
1973  * @text_view: a #GtkTextView
1974  * @pixels_above_lines: pixels above paragraphs
1975  * 
1976  * Sets the default number of blank pixels above paragraphs in @text_view.
1977  * Tags in the buffer for @text_view may override the defaults.
1978  * 
1979  **/
1980 void
1981 gtk_text_view_set_pixels_above_lines (GtkTextView *text_view,
1982                                       gint         pixels_above_lines)
1983 {
1984   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1985
1986   if (text_view->pixels_above_lines != pixels_above_lines)
1987     {
1988       text_view->pixels_above_lines = pixels_above_lines;
1989
1990       if (text_view->layout)
1991         {
1992           text_view->layout->default_style->pixels_above_lines = pixels_above_lines;
1993           gtk_text_layout_default_style_changed (text_view->layout);
1994         }
1995
1996       g_object_notify (G_OBJECT (text_view), "pixels_above_lines");
1997     }
1998 }
1999
2000 /**
2001  * gtk_text_view_get_pixels_above_lines:
2002  * @text_view: a #GtkTextView
2003  * 
2004  * Gets the default number of pixels to put above paragraphs.
2005  * 
2006  * Return value: default number of pixels above paragraphs
2007  **/
2008 gint
2009 gtk_text_view_get_pixels_above_lines (GtkTextView *text_view)
2010 {
2011   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2012
2013   return text_view->pixels_above_lines;
2014 }
2015
2016 /**
2017  * gtk_text_view_set_pixels_below_lines:
2018  * @text_view: a #GtkTextView
2019  * @pixels_below_lines: pixels below paragraphs 
2020  *
2021  * Sets the default number of pixels of blank space
2022  * to put below paragraphs in @text_view. May be overridden
2023  * by tags applied to @text_view's buffer. 
2024  * 
2025  **/
2026 void
2027 gtk_text_view_set_pixels_below_lines (GtkTextView *text_view,
2028                                       gint         pixels_below_lines)
2029 {
2030   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2031
2032   if (text_view->pixels_below_lines != pixels_below_lines)
2033     {
2034       text_view->pixels_below_lines = pixels_below_lines;
2035
2036       if (text_view->layout)
2037         {
2038           text_view->layout->default_style->pixels_below_lines = pixels_below_lines;
2039           gtk_text_layout_default_style_changed (text_view->layout);
2040         }
2041
2042       g_object_notify (G_OBJECT (text_view), "pixels_below_lines");
2043     }
2044 }
2045
2046 /**
2047  * gtk_text_view_get_pixels_below_lines:
2048  * @text_view: a #GtkTextView
2049  * 
2050  * Gets the value set by gtk_text_view_set_pixels_below_lines().
2051  * 
2052  * Return value: default number of blank pixels below paragraphs
2053  **/
2054 gint
2055 gtk_text_view_get_pixels_below_lines (GtkTextView *text_view)
2056 {
2057   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2058
2059   return text_view->pixels_below_lines;
2060 }
2061
2062 /**
2063  * gtk_text_view_set_pixels_inside_wrap:
2064  * @text_view: a #GtkTextView
2065  * @pixels_inside_wrap: default number of pixels between wrapped lines
2066  *
2067  * Sets the default number of pixels of blank space to leave between
2068  * display/wrapped lines within a paragraph. May be overridden by
2069  * tags in @text_view's buffer.
2070  * 
2071  **/
2072 void
2073 gtk_text_view_set_pixels_inside_wrap (GtkTextView *text_view,
2074                                       gint         pixels_inside_wrap)
2075 {
2076   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2077
2078   if (text_view->pixels_inside_wrap != pixels_inside_wrap)
2079     {
2080       text_view->pixels_inside_wrap = pixels_inside_wrap;
2081
2082       if (text_view->layout)
2083         {
2084           text_view->layout->default_style->pixels_inside_wrap = pixels_inside_wrap;
2085           gtk_text_layout_default_style_changed (text_view->layout);
2086         }
2087
2088       g_object_notify (G_OBJECT (text_view), "pixels_inside_wrap");
2089     }
2090 }
2091
2092 /**
2093  * gtk_text_view_get_pixels_inside_wrap:
2094  * @text_view: a #GtkTextView
2095  * 
2096  * Gets the value set by gtk_text_view_set_pixels_inside_wrap().
2097  * 
2098  * Return value: default number of pixels of blank space between wrapped lines
2099  **/
2100 gint
2101 gtk_text_view_get_pixels_inside_wrap (GtkTextView *text_view)
2102 {
2103   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2104
2105   return text_view->pixels_inside_wrap;
2106 }
2107
2108 /**
2109  * gtk_text_view_set_justification:
2110  * @text_view: a #GtkTextView
2111  * @justification: justification
2112  *
2113  * Sets the default justification of text in @text_view.
2114  * Tags in the view's buffer may override the default.
2115  * 
2116  **/
2117 void
2118 gtk_text_view_set_justification (GtkTextView     *text_view,
2119                                  GtkJustification justification)
2120 {
2121   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2122
2123   if (text_view->justify != justification)
2124     {
2125       text_view->justify = justification;
2126
2127       if (text_view->layout)
2128         {
2129           text_view->layout->default_style->justification = justification;
2130           gtk_text_layout_default_style_changed (text_view->layout);
2131         }
2132
2133       g_object_notify (G_OBJECT (text_view), "justification");
2134     }
2135 }
2136
2137 /**
2138  * gtk_text_view_get_justification:
2139  * @text_view: a #GtkTextView
2140  * 
2141  * Gets the default justification of paragraphs in @text_view.
2142  * Tags in the buffer may override the default.
2143  * 
2144  * Return value: default justification
2145  **/
2146 GtkJustification
2147 gtk_text_view_get_justification (GtkTextView *text_view)
2148 {
2149   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_JUSTIFY_LEFT);
2150
2151   return text_view->justify;
2152 }
2153
2154 /**
2155  * gtk_text_view_set_left_margin:
2156  * @text_view: a #GtkTextView
2157  * @left_margin: left margin in pixels
2158  * 
2159  * Sets the default left margin for text in @text_view.
2160  * Tags in the buffer may override the default.
2161  * 
2162  **/
2163 void
2164 gtk_text_view_set_left_margin (GtkTextView *text_view,
2165                                gint         left_margin)
2166 {
2167   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2168
2169   if (text_view->left_margin != left_margin)
2170     {
2171       text_view->left_margin = left_margin;
2172
2173       if (text_view->layout)
2174         {
2175           text_view->layout->default_style->left_margin = left_margin;
2176           gtk_text_layout_default_style_changed (text_view->layout);
2177         }
2178
2179       g_object_notify (G_OBJECT (text_view), "left_margin");
2180     }
2181 }
2182
2183 /**
2184  * gtk_text_view_get_left_margin:
2185  * @text_view: a #GtkTextView
2186  * 
2187  * Gets the default left margin size of paragraphs in the @text_view.
2188  * Tags in the buffer may override the default.
2189  * 
2190  * Return value: left margin in pixels
2191  **/
2192 gint
2193 gtk_text_view_get_left_margin (GtkTextView *text_view)
2194 {
2195   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2196
2197   return text_view->left_margin;
2198 }
2199
2200 /**
2201  * gtk_text_view_set_right_margin:
2202  * @text_view: a #GtkTextView
2203  * @right_margin: right margin in pixels
2204  *
2205  * Sets the default right margin for text in the text view.
2206  * Tags in the buffer may override the default.
2207  * 
2208  **/
2209 void
2210 gtk_text_view_set_right_margin (GtkTextView *text_view,
2211                                 gint         right_margin)
2212 {
2213   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2214
2215   if (text_view->right_margin != right_margin)
2216     {
2217       text_view->right_margin = right_margin;
2218
2219       if (text_view->layout)
2220         {
2221           text_view->layout->default_style->right_margin = right_margin;
2222           gtk_text_layout_default_style_changed (text_view->layout);
2223         }
2224
2225       g_object_notify (G_OBJECT (text_view), "right_margin");
2226     }
2227 }
2228
2229 /**
2230  * gtk_text_view_get_right_margin:
2231  * @text_view: a #GtkTextView
2232  * 
2233  * Gets the default right margin for text in @text_view. Tags
2234  * in the buffer may override the default.
2235  * 
2236  * Return value: right margin in pixels
2237  **/
2238 gint
2239 gtk_text_view_get_right_margin (GtkTextView *text_view)
2240 {
2241   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2242
2243   return text_view->right_margin;
2244 }
2245
2246 /**
2247  * gtk_text_view_set_indent:
2248  * @text_view: a #GtkTextView
2249  * @indent: indentation in pixels
2250  *
2251  * Sets the default indentation for paragraphs in @text_view.
2252  * Tags in the buffer may override the default.
2253  * 
2254  **/
2255 void
2256 gtk_text_view_set_indent (GtkTextView *text_view,
2257                           gint         indent)
2258 {
2259   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2260
2261   if (text_view->indent != indent)
2262     {
2263       text_view->indent = indent;
2264
2265       if (text_view->layout)
2266         {
2267           text_view->layout->default_style->indent = indent;
2268           gtk_text_layout_default_style_changed (text_view->layout);
2269         }
2270
2271       g_object_notify (G_OBJECT (text_view), "indent");
2272     }
2273 }
2274
2275 /**
2276  * gtk_text_view_get_indent:
2277  * @text_view: a #GtkTextView
2278  * 
2279  * Gets the default indentation of paragraphs in @text_view.
2280  * Tags in the view's buffer may override the default.
2281  * The indentation may be negative.
2282  * 
2283  * Return value: number of pixels of indentation
2284  **/
2285 gint
2286 gtk_text_view_get_indent (GtkTextView *text_view)
2287 {
2288   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2289
2290   return text_view->indent;
2291 }
2292
2293 /**
2294  * gtk_text_view_set_tabs:
2295  * @text_view: a #GtkTextView
2296  * @tabs: tabs as a #PangoTabArray
2297  *
2298  * Sets the default tab stops for paragraphs in @text_view.
2299  * Tags in the buffer may override the default.
2300  * 
2301  **/
2302 void
2303 gtk_text_view_set_tabs (GtkTextView   *text_view,
2304                         PangoTabArray *tabs)
2305 {
2306   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2307
2308   if (text_view->tabs)
2309     pango_tab_array_free (text_view->tabs);
2310
2311   text_view->tabs = tabs ? pango_tab_array_copy (tabs) : NULL;
2312
2313   if (text_view->layout)
2314     {
2315       /* some unkosher futzing in internal struct details... */
2316       if (text_view->layout->default_style->tabs)
2317         pango_tab_array_free (text_view->layout->default_style->tabs);
2318
2319       text_view->layout->default_style->tabs =
2320         text_view->tabs ? pango_tab_array_copy (text_view->tabs) : NULL;
2321
2322       gtk_text_layout_default_style_changed (text_view->layout);
2323     }
2324
2325   g_object_notify (G_OBJECT (text_view), "tabs");
2326 }
2327
2328 /**
2329  * gtk_text_view_get_tabs:
2330  * @text_view: a #GtkTextView
2331  * 
2332  * Gets the default tabs for @text_view. Tags in the buffer may
2333  * override the defaults. The returned array will be %NULL if
2334  * "standard" (8-space) tabs are used. Free the return value
2335  * with pango_tab_array_free().
2336  * 
2337  * Return value: copy of default tab array, or %NULL if "standard" tabs are used; must be freed with pango_tab_array_free().
2338  **/
2339 PangoTabArray*
2340 gtk_text_view_get_tabs (GtkTextView *text_view)
2341 {
2342   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
2343
2344   return text_view->tabs ? pango_tab_array_copy (text_view->tabs) : NULL;
2345 }
2346
2347 /**
2348  * gtk_text_view_set_cursor_visible:
2349  * @text_view: a #GtkTextView
2350  * @setting: whether to show the insertion cursor
2351  *
2352  * Toggles whether the insertion point is displayed. A buffer with no editable
2353  * text probably shouldn't have a visible cursor, so you may want to turn
2354  * the cursor off.
2355  **/
2356 void
2357 gtk_text_view_set_cursor_visible    (GtkTextView   *text_view,
2358                                      gboolean       setting)
2359 {
2360   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2361
2362   setting = (setting != FALSE);
2363
2364   if (text_view->cursor_visible != setting)
2365     {
2366       text_view->cursor_visible = setting;
2367
2368       if (GTK_WIDGET_HAS_FOCUS (text_view))
2369         {
2370           if (text_view->layout)
2371             {
2372               gtk_text_layout_set_cursor_visible (text_view->layout, setting);
2373               gtk_text_view_check_cursor_blink (text_view);
2374             }
2375         }
2376
2377       g_object_notify (G_OBJECT (text_view), "cursor_visible");
2378     }
2379 }
2380
2381 /**
2382  * gtk_text_view_get_cursor_visible:
2383  * @text_view: a #GtkTextView
2384  *
2385  * Find out whether the cursor is being displayed.
2386  *
2387  * Return value: whether the insertion mark is visible
2388  **/
2389 gboolean
2390 gtk_text_view_get_cursor_visible    (GtkTextView   *text_view)
2391 {
2392   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
2393
2394   return text_view->cursor_visible;
2395 }
2396
2397
2398 /**
2399  * gtk_text_view_place_cursor_onscreen:
2400  * @text_view: a #GtkTextView
2401  *
2402  * Moves the cursor to the currently visible region of the
2403  * buffer, it it isn't there already.
2404  *
2405  * Return value: TRUE if the cursor had to be moved.
2406  **/
2407 gboolean
2408 gtk_text_view_place_cursor_onscreen (GtkTextView *text_view)
2409 {
2410   GtkTextIter insert;
2411
2412   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
2413
2414   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
2415                                     gtk_text_buffer_get_mark (get_buffer (text_view),
2416                                                               "insert"));
2417
2418   if (clamp_iter_onscreen (text_view, &insert))
2419     {
2420       gtk_text_buffer_place_cursor (get_buffer (text_view), &insert);
2421       return TRUE;
2422     }
2423   else
2424     return FALSE;
2425 }
2426
2427 static void
2428 gtk_text_view_remove_validate_idles (GtkTextView *text_view)
2429 {
2430   if (text_view->first_validate_idle != 0)
2431     {
2432       DV (g_print ("Removing first validate idle: %s\n", G_STRLOC));
2433       g_source_remove (text_view->first_validate_idle);
2434       text_view->first_validate_idle = 0;
2435     }
2436
2437   if (text_view->incremental_validate_idle != 0)
2438     {
2439       g_source_remove (text_view->incremental_validate_idle);
2440       text_view->incremental_validate_idle = 0;
2441     }
2442 }
2443
2444 static void
2445 gtk_text_view_destroy (GtkObject *object)
2446 {
2447   GtkTextView *text_view;
2448   GtkTextLayout *layout;
2449   
2450   text_view = GTK_TEXT_VIEW (object);
2451
2452   layout = text_view->layout;
2453   
2454   gtk_text_view_remove_validate_idles (text_view);
2455   gtk_text_view_set_buffer (text_view, NULL);
2456   gtk_text_view_destroy_layout (text_view);
2457
2458   (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
2459 }
2460
2461 static void
2462 gtk_text_view_finalize (GObject *object)
2463 {
2464   GtkTextView *text_view;
2465
2466   text_view = GTK_TEXT_VIEW (object);
2467
2468   g_return_if_fail (text_view->buffer == NULL);
2469
2470   gtk_text_view_destroy_layout (text_view);
2471   gtk_text_view_set_buffer (text_view, NULL);
2472   
2473   cancel_pending_scroll (text_view);
2474
2475   if (text_view->tabs)
2476     pango_tab_array_free (text_view->tabs);
2477   
2478   if (text_view->hadjustment)
2479     g_object_unref (text_view->hadjustment);
2480   if (text_view->vadjustment)
2481     g_object_unref (text_view->vadjustment);
2482
2483   text_window_free (text_view->text_window);
2484
2485   if (text_view->left_window)
2486     text_window_free (text_view->left_window);
2487
2488   if (text_view->top_window)
2489     text_window_free (text_view->top_window);
2490
2491   if (text_view->right_window)
2492     text_window_free (text_view->right_window);
2493
2494   if (text_view->bottom_window)
2495     text_window_free (text_view->bottom_window);
2496
2497   g_object_unref (text_view->im_context);
2498   
2499   (* G_OBJECT_CLASS (parent_class)->finalize) (object);
2500 }
2501
2502 static void
2503 gtk_text_view_set_property (GObject         *object,
2504                             guint            prop_id,
2505                             const GValue    *value,
2506                             GParamSpec      *pspec)
2507 {
2508   GtkTextView *text_view;
2509
2510   text_view = GTK_TEXT_VIEW (object);
2511
2512   switch (prop_id)
2513     {
2514     case PROP_PIXELS_ABOVE_LINES:
2515       gtk_text_view_set_pixels_above_lines (text_view, g_value_get_int (value));
2516       break;
2517
2518     case PROP_PIXELS_BELOW_LINES:
2519       gtk_text_view_set_pixels_below_lines (text_view, g_value_get_int (value));
2520       break;
2521
2522     case PROP_PIXELS_INSIDE_WRAP:
2523       gtk_text_view_set_pixels_inside_wrap (text_view, g_value_get_int (value));
2524       break;
2525
2526     case PROP_EDITABLE:
2527       gtk_text_view_set_editable (text_view, g_value_get_boolean (value));
2528       break;
2529
2530     case PROP_WRAP_MODE:
2531       gtk_text_view_set_wrap_mode (text_view, g_value_get_enum (value));
2532       break;
2533       
2534     case PROP_JUSTIFICATION:
2535       gtk_text_view_set_justification (text_view, g_value_get_enum (value));
2536       break;
2537
2538     case PROP_LEFT_MARGIN:
2539       gtk_text_view_set_left_margin (text_view, g_value_get_int (value));
2540       break;
2541
2542     case PROP_RIGHT_MARGIN:
2543       gtk_text_view_set_right_margin (text_view, g_value_get_int (value));
2544       break;
2545
2546     case PROP_INDENT:
2547       gtk_text_view_set_indent (text_view, g_value_get_int (value));
2548       break;
2549
2550     case PROP_TABS:
2551       gtk_text_view_set_tabs (text_view, g_value_get_boxed (value));
2552       break;
2553
2554     case PROP_CURSOR_VISIBLE:
2555       gtk_text_view_set_cursor_visible (text_view, g_value_get_boolean (value));
2556       break;
2557
2558     case PROP_BUFFER:
2559       gtk_text_view_set_buffer (text_view, GTK_TEXT_BUFFER (g_value_get_object (value)));
2560       break;
2561
2562     default:
2563       g_assert_not_reached ();
2564       break;
2565     }
2566 }
2567
2568 static void
2569 gtk_text_view_get_property (GObject         *object,
2570                             guint            prop_id,
2571                             GValue          *value,
2572                             GParamSpec      *pspec)
2573 {
2574   GtkTextView *text_view;
2575
2576   text_view = GTK_TEXT_VIEW (object);
2577
2578   switch (prop_id)
2579     {
2580     case PROP_PIXELS_ABOVE_LINES:
2581       g_value_set_int (value, text_view->pixels_above_lines);
2582       break;
2583
2584     case PROP_PIXELS_BELOW_LINES:
2585       g_value_set_int (value, text_view->pixels_below_lines);
2586       break;
2587
2588     case PROP_PIXELS_INSIDE_WRAP:
2589       g_value_set_int (value, text_view->pixels_inside_wrap);
2590       break;
2591
2592     case PROP_EDITABLE:
2593       g_value_set_boolean (value, text_view->editable);
2594       break;
2595       
2596     case PROP_WRAP_MODE:
2597       g_value_set_enum (value, text_view->wrap_mode);
2598       break;
2599
2600     case PROP_JUSTIFICATION:
2601       g_value_set_enum (value, text_view->justify);
2602       break;
2603
2604     case PROP_LEFT_MARGIN:
2605       g_value_set_int (value, text_view->left_margin);
2606       break;
2607
2608     case PROP_RIGHT_MARGIN:
2609       g_value_set_int (value, text_view->right_margin);
2610       break;
2611
2612     case PROP_INDENT:
2613       g_value_set_int (value, text_view->indent);
2614       break;
2615
2616     case PROP_TABS:
2617       g_value_set_boxed (value, text_view->tabs);
2618       break;
2619
2620     case PROP_CURSOR_VISIBLE:
2621       g_value_set_boolean (value, text_view->cursor_visible);
2622       break;
2623
2624     case PROP_BUFFER:
2625       g_value_set_object (value, get_buffer (text_view));
2626       break;
2627
2628     default:
2629       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2630       break;
2631     }
2632 }
2633
2634 static void
2635 gtk_text_view_size_request (GtkWidget      *widget,
2636                             GtkRequisition *requisition)
2637 {
2638   GtkTextView *text_view;
2639   GSList *tmp_list;
2640   gint focus_edge_width;
2641   gint focus_width;
2642   gboolean interior_focus;
2643   
2644   text_view = GTK_TEXT_VIEW (widget);
2645
2646   gtk_widget_style_get (widget,
2647                         "interior_focus", &interior_focus,
2648                         "focus_line_width", &focus_width,
2649                         NULL);
2650
2651   if (interior_focus)
2652     focus_edge_width = 0;
2653   else
2654     focus_edge_width = focus_width;
2655
2656   if (text_view->layout)
2657     {
2658       text_view->text_window->requisition.width = text_view->layout->width;
2659       text_view->text_window->requisition.height = text_view->layout->height;
2660     }
2661   else
2662     {
2663       text_view->text_window->requisition.width = 0;
2664       text_view->text_window->requisition.height = 0;
2665     }
2666   
2667   requisition->width = text_view->text_window->requisition.width + focus_edge_width * 2;
2668   requisition->height = text_view->text_window->requisition.height + focus_edge_width * 2;
2669
2670   if (text_view->left_window)
2671     requisition->width += text_view->left_window->requisition.width;
2672
2673   if (text_view->right_window)
2674     requisition->width += text_view->right_window->requisition.width;
2675
2676   if (text_view->top_window)
2677     requisition->height += text_view->top_window->requisition.height;
2678
2679   if (text_view->bottom_window)
2680     requisition->height += text_view->bottom_window->requisition.height;
2681
2682   requisition->width += GTK_CONTAINER (text_view)->border_width * 2;
2683   requisition->height += GTK_CONTAINER (text_view)->border_width * 2;
2684   
2685   tmp_list = text_view->children;
2686   while (tmp_list != NULL)
2687     {
2688       GtkTextViewChild *child = tmp_list->data;
2689
2690       if (child->anchor)
2691         {
2692           GtkRequisition child_req;
2693           GtkRequisition old_req;
2694
2695           gtk_widget_get_child_requisition (child->widget, &old_req);
2696           
2697           gtk_widget_size_request (child->widget, &child_req);
2698
2699           gtk_widget_get_child_requisition (child->widget, &child_req);
2700
2701           /* Invalidate layout lines if required */
2702           if (text_view->layout &&
2703               (old_req.width != child_req.width ||
2704                old_req.height != child_req.height))
2705             gtk_text_child_anchor_queue_resize (child->anchor,
2706                                                 text_view->layout);
2707         }
2708       else
2709         {
2710           GtkRequisition child_req;
2711           
2712           gtk_widget_size_request (child->widget, &child_req);
2713         }
2714
2715       tmp_list = g_slist_next (tmp_list);
2716     }
2717 }
2718
2719 static void
2720 gtk_text_view_compute_child_allocation (GtkTextView      *text_view,
2721                                         GtkTextViewChild *vc,
2722                                         GtkAllocation    *allocation)
2723 {
2724   gint buffer_y;
2725   GtkTextIter iter;
2726   GtkRequisition req;
2727   
2728   gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view),
2729                                             &iter,
2730                                             vc->anchor);
2731
2732   gtk_text_layout_get_line_yrange (text_view->layout, &iter,
2733                                    &buffer_y, NULL);
2734
2735   buffer_y += vc->from_top_of_line;
2736
2737   allocation->x = vc->from_left_of_buffer - text_view->xoffset;
2738   allocation->y = buffer_y - text_view->yoffset;
2739
2740   gtk_widget_get_child_requisition (vc->widget, &req);
2741   allocation->width = req.width;
2742   allocation->height = req.height;
2743 }
2744
2745 static void
2746 gtk_text_view_update_child_allocation (GtkTextView      *text_view,
2747                                        GtkTextViewChild *vc)
2748 {
2749   GtkAllocation allocation;
2750
2751   gtk_text_view_compute_child_allocation (text_view, vc, &allocation);
2752   
2753   gtk_widget_size_allocate (vc->widget, &allocation);
2754
2755 #if 0
2756   g_print ("allocation for %p allocated to %d,%d yoffset = %d\n",
2757            vc->widget,
2758            vc->widget->allocation.x,
2759            vc->widget->allocation.y,
2760            text_view->yoffset);
2761 #endif
2762 }
2763
2764 static void
2765 gtk_text_view_child_allocated (GtkTextLayout *layout,
2766                                GtkWidget     *child,
2767                                gint           x,
2768                                gint           y,
2769                                gpointer       data)
2770 {
2771   GtkTextViewChild *vc = NULL;
2772   GtkTextView *text_view = data;
2773   
2774   /* x,y is the position of the child from the top of the line, and
2775    * from the left of the buffer. We have to translate that into text
2776    * window coordinates, then size_allocate the child.
2777    */
2778
2779   vc = g_object_get_data (G_OBJECT (child),
2780                           "gtk-text-view-child");
2781
2782   g_assert (vc != NULL);
2783
2784   DV (g_print ("child allocated at %d,%d\n", x, y));
2785   
2786   vc->from_left_of_buffer = x;
2787   vc->from_top_of_line = y;
2788
2789   gtk_text_view_update_child_allocation (text_view, vc);
2790 }
2791
2792 static void
2793 gtk_text_view_allocate_children (GtkTextView *text_view)
2794 {
2795   GSList *tmp_list;
2796
2797   DV(g_print(G_STRLOC"\n"));
2798   
2799   tmp_list = text_view->children;
2800   while (tmp_list != NULL)
2801     {
2802       GtkTextViewChild *child = tmp_list->data;
2803
2804       if (child->anchor)
2805         {
2806           /* We need to force-validate the regions containing
2807            * children.
2808            */
2809           GtkTextIter child_loc;
2810           gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view),
2811                                                     &child_loc,
2812                                                     child->anchor);
2813
2814           gtk_text_layout_validate_yrange (text_view->layout,
2815                                            &child_loc,
2816                                            0, 1);
2817         }
2818       else
2819         {
2820           GtkAllocation allocation;          
2821           GtkRequisition child_req;
2822              
2823           g_assert (child != NULL);
2824           
2825           allocation.x = child->x;
2826           allocation.y = child->y;
2827
2828           gtk_widget_get_child_requisition (child->widget, &child_req);
2829           
2830           allocation.width = child_req.width;
2831           allocation.height = child_req.height;
2832           
2833           gtk_widget_size_allocate (child->widget, &allocation);          
2834         }
2835
2836       tmp_list = g_slist_next (tmp_list);
2837     }
2838 }
2839
2840 static void
2841 gtk_text_view_size_allocate (GtkWidget *widget,
2842                              GtkAllocation *allocation)
2843 {
2844   GtkTextView *text_view;
2845   GtkTextIter first_para;
2846   gint y;
2847   gint width, height;
2848   GdkRectangle text_rect;
2849   GdkRectangle left_rect;
2850   GdkRectangle right_rect;
2851   GdkRectangle top_rect;
2852   GdkRectangle bottom_rect;
2853   gint focus_edge_width;
2854   gint focus_width;
2855   gboolean interior_focus;
2856   gboolean size_changed;
2857   
2858   text_view = GTK_TEXT_VIEW (widget);
2859
2860   DV(g_print(G_STRLOC"\n"));
2861
2862   size_changed =
2863     widget->allocation.width != allocation->width ||
2864     widget->allocation.height != allocation->height;
2865   
2866   widget->allocation = *allocation;
2867
2868   if (GTK_WIDGET_REALIZED (widget))
2869     {
2870       gdk_window_move_resize (widget->window,
2871                               allocation->x, allocation->y,
2872                               allocation->width, allocation->height);
2873     }
2874
2875   /* distribute width/height among child windows. Ensure all
2876    * windows get at least a 1x1 allocation.
2877    */
2878
2879   gtk_widget_style_get (widget,
2880                         "interior_focus", &interior_focus,
2881                         "focus_line_width", &focus_width,
2882                         NULL);
2883
2884   if (interior_focus)
2885     focus_edge_width = 0;
2886   else
2887     focus_edge_width = focus_width;
2888   
2889   width = allocation->width - focus_edge_width * 2 - GTK_CONTAINER (text_view)->border_width * 2;
2890
2891   if (text_view->left_window)
2892     left_rect.width = text_view->left_window->requisition.width;
2893   else
2894     left_rect.width = 0;
2895
2896   width -= left_rect.width;
2897
2898   if (text_view->right_window)
2899     right_rect.width = text_view->right_window->requisition.width;
2900   else
2901     right_rect.width = 0;
2902
2903   width -= right_rect.width;
2904
2905   text_rect.width = MAX (1, width);
2906
2907   top_rect.width = text_rect.width;
2908   bottom_rect.width = text_rect.width;
2909
2910
2911   height = allocation->height - focus_edge_width * 2 - GTK_CONTAINER (text_view)->border_width * 2;
2912
2913   if (text_view->top_window)
2914     top_rect.height = text_view->top_window->requisition.height;
2915   else
2916     top_rect.height = 0;
2917
2918   height -= top_rect.height;
2919
2920   if (text_view->bottom_window)
2921     bottom_rect.height = text_view->bottom_window->requisition.height;
2922   else
2923     bottom_rect.height = 0;
2924
2925   height -= bottom_rect.height;
2926
2927   text_rect.height = MAX (1, height);
2928
2929   left_rect.height = text_rect.height;
2930   right_rect.height = text_rect.height;
2931
2932   /* Origins */
2933   left_rect.x = focus_edge_width + GTK_CONTAINER (text_view)->border_width;
2934   top_rect.y = focus_edge_width + GTK_CONTAINER (text_view)->border_width;
2935
2936   text_rect.x = left_rect.x + left_rect.width;
2937   text_rect.y = top_rect.y + top_rect.height;
2938
2939   left_rect.y = text_rect.y;
2940   right_rect.y = text_rect.y;
2941
2942   top_rect.x = text_rect.x;
2943   bottom_rect.x = text_rect.x;
2944
2945   right_rect.x = text_rect.x + text_rect.width;
2946   bottom_rect.y = text_rect.y + text_rect.height;
2947
2948   text_window_size_allocate (text_view->text_window,
2949                              &text_rect);
2950
2951   if (text_view->left_window)
2952     text_window_size_allocate (text_view->left_window,
2953                                &left_rect);
2954
2955   if (text_view->right_window)
2956     text_window_size_allocate (text_view->right_window,
2957                                &right_rect);
2958
2959   if (text_view->top_window)
2960     text_window_size_allocate (text_view->top_window,
2961                                &top_rect);
2962
2963   if (text_view->bottom_window)
2964     text_window_size_allocate (text_view->bottom_window,
2965                                &bottom_rect);
2966
2967   gtk_text_view_update_layout_width (text_view);
2968   
2969   /* Note that this will do some layout validation */
2970   gtk_text_view_allocate_children (text_view);
2971
2972   /* Ensure h/v adj exist */
2973   get_hadjustment (text_view);
2974   get_vadjustment (text_view);
2975
2976   text_view->hadjustment->page_size = SCREEN_WIDTH (text_view);
2977   text_view->hadjustment->page_increment = SCREEN_WIDTH (text_view) * 0.9;
2978   text_view->hadjustment->step_increment = SCREEN_WIDTH (text_view) * 0.1;
2979   text_view->hadjustment->lower = 0;
2980   text_view->hadjustment->upper = MAX (SCREEN_WIDTH (text_view),
2981                                        text_view->width);
2982
2983   if (text_view->hadjustment->value > text_view->hadjustment->upper - text_view->hadjustment->page_size)
2984     gtk_adjustment_set_value (text_view->hadjustment, MAX (0, text_view->hadjustment->upper - text_view->hadjustment->page_size));
2985
2986   gtk_adjustment_changed (text_view->hadjustment);
2987
2988   text_view->vadjustment->page_size = SCREEN_HEIGHT (text_view);
2989   text_view->vadjustment->page_increment = SCREEN_HEIGHT (text_view) * 0.9;
2990   text_view->vadjustment->step_increment = SCREEN_HEIGHT (text_view) * 0.1;
2991   text_view->vadjustment->lower = 0;
2992   text_view->vadjustment->upper = MAX (SCREEN_HEIGHT (text_view),
2993                                        text_view->height);
2994
2995   /* Now adjust the value of the adjustment to keep the cursor at the
2996    * same place in the buffer
2997    */
2998   gtk_text_view_get_first_para_iter (text_view, &first_para);
2999   gtk_text_layout_get_line_yrange (text_view->layout, &first_para, &y, NULL);
3000
3001   y += text_view->first_para_pixels;
3002
3003   if (y > text_view->vadjustment->upper - text_view->vadjustment->page_size)
3004     y = MAX (0, text_view->vadjustment->upper - text_view->vadjustment->page_size);
3005
3006   if (y != text_view->yoffset)
3007     gtk_adjustment_set_value (text_view->vadjustment, y);
3008
3009   gtk_adjustment_changed (text_view->vadjustment);
3010
3011   /* The GTK resize loop processes all the pending exposes right
3012    * after doing the resize stuff, so the idle sizer won't have a
3013    * chance to run. So we do the work here. 
3014    */
3015   gtk_text_view_flush_first_validate (text_view);
3016
3017   /* widget->window doesn't get auto-redrawn as the layout is computed, so has to
3018    * be invalidated
3019    */
3020   if (size_changed && GTK_WIDGET_REALIZED (widget))
3021     gdk_window_invalidate_rect (widget->window, NULL, FALSE);
3022 }
3023
3024 static void
3025 gtk_text_view_get_first_para_iter (GtkTextView *text_view,
3026                                    GtkTextIter *iter)
3027 {
3028   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), iter,
3029                                     text_view->first_para_mark);
3030 }
3031
3032 static void
3033 gtk_text_view_validate_onscreen (GtkTextView *text_view)
3034 {
3035   GtkWidget *widget = GTK_WIDGET (text_view);
3036   
3037   DV(g_print(">Validating onscreen ("G_STRLOC")\n"));
3038   
3039   if (SCREEN_HEIGHT (widget) > 0)
3040     {
3041       GtkTextIter first_para;
3042
3043       /* Be sure we've validated the stuff onscreen; if we
3044        * scrolled, these calls won't have any effect, because
3045        * they were called in the recursive validate_onscreen
3046        */
3047       gtk_text_view_get_first_para_iter (text_view, &first_para);
3048
3049       gtk_text_layout_validate_yrange (text_view->layout,
3050                                        &first_para,
3051                                        0,
3052                                        text_view->first_para_pixels +
3053                                        SCREEN_HEIGHT (widget));
3054     }
3055
3056   text_view->onscreen_validated = TRUE;
3057
3058   DV(g_print(">Done validating onscreen, onscreen_validated = TRUE ("G_STRLOC")\n"));
3059   
3060   /* This can have the odd side effect of triggering a scroll, which should
3061    * flip "onscreen_validated" back to FALSE, but should also get us
3062    * back into this function to turn it on again.
3063    */
3064   gtk_text_view_update_adjustments (text_view);
3065
3066   g_assert (text_view->onscreen_validated);
3067 }
3068
3069 static void
3070 gtk_text_view_flush_first_validate (GtkTextView *text_view)
3071 {
3072   if (text_view->first_validate_idle == 0)
3073     return;
3074
3075   /* Do this first, which means that if an "invalidate"
3076    * occurs during any of this process, a new first_validate_callback
3077    * will be installed, and we'll start again.
3078    */
3079   DV (g_print ("removing first validate in %s\n", G_STRLOC));
3080   g_source_remove (text_view->first_validate_idle);
3081   text_view->first_validate_idle = 0;
3082   
3083   /* be sure we have up-to-date screen size set on the
3084    * layout.
3085    */
3086   gtk_text_view_update_layout_width (text_view);
3087
3088   /* Bail out if we invalidated stuff; scrolling right away will just
3089    * confuse the issue.
3090    */
3091   if (text_view->first_validate_idle != 0)
3092     {
3093       DV(g_print(">Width change forced requeue ("G_STRLOC")\n"));
3094     }
3095   else
3096     {
3097       /* scroll to any marks, if that's pending. This can jump us to
3098        * the validation codepath used for scrolling onscreen, if so we
3099        * bail out.  It won't jump if already in that codepath since
3100        * value_changed is not recursive, so also validate if
3101        * necessary.
3102        */
3103       if (!gtk_text_view_flush_scroll (text_view) ||
3104           !text_view->onscreen_validated)
3105         gtk_text_view_validate_onscreen (text_view);
3106       
3107       DV(g_print(">Leaving first validate idle ("G_STRLOC")\n"));
3108       
3109       g_assert (text_view->onscreen_validated);
3110     }
3111 }
3112
3113 static gboolean
3114 first_validate_callback (gpointer data)
3115 {
3116   GtkTextView *text_view = data;
3117
3118   GDK_THREADS_ENTER ();
3119   
3120   /* Note that some of this code is duplicated at the end of size_allocate,
3121    * keep in sync with that.
3122    */
3123   
3124   DV(g_print(G_STRLOC"\n"));
3125
3126   gtk_text_view_flush_first_validate (text_view);
3127   
3128   GDK_THREADS_LEAVE ();
3129   
3130   return FALSE;
3131 }
3132
3133 static gboolean
3134 incremental_validate_callback (gpointer data)
3135 {
3136   GtkTextView *text_view = data;
3137   gboolean result = TRUE;
3138
3139   GDK_THREADS_ENTER ();
3140   
3141   DV(g_print(G_STRLOC"\n"));
3142   
3143   gtk_text_layout_validate (text_view->layout, 2000);
3144
3145   gtk_text_view_update_adjustments (text_view);
3146   
3147   if (gtk_text_layout_is_valid (text_view->layout))
3148     {
3149       text_view->incremental_validate_idle = 0;
3150       result = FALSE;
3151     }
3152
3153   GDK_THREADS_LEAVE ();
3154
3155   return result;
3156 }
3157
3158 static void
3159 gtk_text_view_invalidate (GtkTextView *text_view)
3160 {  
3161   DV (g_print (">Invalidate, onscreen_validated = %d now FALSE ("G_STRLOC")\n",
3162                text_view->onscreen_validated));
3163
3164   text_view->onscreen_validated = FALSE;
3165
3166   /* We'll invalidate when the layout is created */
3167   if (text_view->layout == NULL)
3168     return;
3169   
3170   if (!text_view->first_validate_idle)
3171     {
3172       text_view->first_validate_idle = g_idle_add_full (GTK_PRIORITY_RESIZE - 2, first_validate_callback, text_view, NULL);
3173       DV (g_print (G_STRLOC": adding first validate idle %d\n",
3174                    text_view->first_validate_idle));
3175     }
3176       
3177   if (!text_view->incremental_validate_idle)
3178     {
3179       text_view->incremental_validate_idle = g_idle_add_full (GTK_TEXT_VIEW_PRIORITY_VALIDATE, incremental_validate_callback, text_view, NULL);
3180       DV (g_print (G_STRLOC": adding incremental validate idle %d\n",
3181                    text_view->incremental_validate_idle));
3182     }
3183 }
3184
3185 static void
3186 invalidated_handler (GtkTextLayout *layout,
3187                      gpointer       data)
3188 {
3189   GtkTextView *text_view;
3190
3191   text_view = GTK_TEXT_VIEW (data);
3192
3193   DV (g_print ("Invalidating due to layout invalidate signal\n"));
3194   gtk_text_view_invalidate (text_view);
3195 }
3196
3197 static void
3198 changed_handler (GtkTextLayout     *layout,
3199                  gint               start_y,
3200                  gint               old_height,
3201                  gint               new_height,
3202                  gpointer           data)
3203 {
3204   GtkTextView *text_view;
3205   GtkWidget *widget;
3206   GdkRectangle visible_rect;
3207   GdkRectangle redraw_rect;
3208   
3209   text_view = GTK_TEXT_VIEW (data);
3210   widget = GTK_WIDGET (data);
3211   
3212   DV(g_print(">Lines Validated ("G_STRLOC")\n"));
3213
3214   if (GTK_WIDGET_REALIZED (text_view))
3215     {      
3216       gtk_text_view_get_visible_rect (text_view, &visible_rect);
3217
3218       redraw_rect.x = visible_rect.x;
3219       redraw_rect.width = visible_rect.width;
3220       redraw_rect.y = start_y;
3221
3222       if (old_height == new_height)
3223         redraw_rect.height = old_height;
3224       else if (start_y + old_height > visible_rect.y)
3225         redraw_rect.height = MAX (0, visible_rect.y + visible_rect.height - start_y);
3226       else
3227         redraw_rect.height = 0;
3228         
3229       if (gdk_rectangle_intersect (&redraw_rect, &visible_rect, &redraw_rect))
3230         {
3231           /* text_window_invalidate_rect() takes buffer coordinates */
3232           text_window_invalidate_rect (text_view->text_window,
3233                                        &redraw_rect);
3234
3235           DV(g_print(" invalidated rect: %d,%d %d x %d\n",
3236                      redraw_rect.x,
3237                      redraw_rect.y,
3238                      redraw_rect.width,
3239                      redraw_rect.height));
3240           
3241           if (text_view->left_window)
3242             text_window_invalidate_rect (text_view->left_window,
3243                                          &redraw_rect);
3244           if (text_view->right_window)
3245             text_window_invalidate_rect (text_view->right_window,
3246                                          &redraw_rect);
3247           if (text_view->top_window)
3248             text_window_invalidate_rect (text_view->top_window,
3249                                          &redraw_rect);
3250           if (text_view->bottom_window)
3251             text_window_invalidate_rect (text_view->bottom_window,
3252                                          &redraw_rect);
3253
3254           gtk_text_view_update_im_spot_location (text_view);
3255         }
3256     }
3257   
3258   if (old_height != new_height)
3259     {
3260       gboolean yoffset_changed = FALSE;
3261       GSList *tmp_list;
3262       int new_first_para_top;
3263       int old_first_para_top;
3264       GtkTextIter first;
3265       
3266       /* If the bottom of the old area was above the top of the
3267        * screen, we need to scroll to keep the current top of the
3268        * screen in place.  Remember that first_para_pixels is the
3269        * position of the top of the screen in coordinates relative to
3270        * the first paragraph onscreen.
3271        *
3272        * In short we are adding the height delta of the portion of the
3273        * changed region above first_para_mark to text_view->yoffset.
3274        */
3275       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &first,
3276                                         text_view->first_para_mark);
3277
3278       gtk_text_layout_get_line_yrange (layout, &first, &new_first_para_top, NULL);
3279
3280       old_first_para_top = text_view->yoffset - text_view->first_para_pixels;
3281
3282       if (new_first_para_top != old_first_para_top)
3283         {
3284           text_view->yoffset += new_first_para_top - old_first_para_top;
3285           
3286           get_vadjustment (text_view)->value = text_view->yoffset;
3287           yoffset_changed = TRUE;
3288         }
3289
3290       if (yoffset_changed)
3291         {
3292           DV(g_print ("Changing scroll position (%s)\n", G_STRLOC));
3293           gtk_adjustment_value_changed (get_vadjustment (text_view));
3294         }
3295
3296       /* FIXME be smarter about which anchored widgets we update */
3297
3298       tmp_list = text_view->children;
3299       while (tmp_list != NULL)
3300         {
3301           GtkTextViewChild *child = tmp_list->data;
3302
3303           if (child->anchor)
3304             gtk_text_view_update_child_allocation (text_view, child);
3305
3306           tmp_list = g_slist_next (tmp_list);
3307         }
3308     }
3309
3310   {
3311     GtkRequisition old_req;
3312     GtkRequisition new_req;
3313
3314     old_req = widget->requisition;
3315
3316     /* Use this instead of gtk_widget_size_request wrapper
3317      * to avoid the optimization which just returns widget->requisition
3318      * if a resize hasn't been queued.
3319      */
3320     GTK_WIDGET_GET_CLASS (widget)->size_request (widget, &new_req);
3321
3322     if (old_req.width != new_req.width ||
3323         old_req.height != new_req.height)
3324       {
3325         /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=72258 */
3326         _gtk_size_group_queue_resize (widget);
3327       }
3328   }
3329 }
3330
3331 static void
3332 gtk_text_view_realize (GtkWidget *widget)
3333 {
3334   GtkTextView *text_view;
3335   GdkWindowAttr attributes;
3336   gint attributes_mask;
3337   GSList *tmp_list;
3338   
3339   text_view = GTK_TEXT_VIEW (widget);
3340   GTK_WIDGET_SET_FLAGS (text_view, GTK_REALIZED);
3341
3342   attributes.window_type = GDK_WINDOW_CHILD;
3343   attributes.x = widget->allocation.x;
3344   attributes.y = widget->allocation.y;
3345   attributes.width = widget->allocation.width;
3346   attributes.height = widget->allocation.height;
3347   attributes.wclass = GDK_INPUT_OUTPUT;
3348   attributes.visual = gtk_widget_get_visual (widget);
3349   attributes.colormap = gtk_widget_get_colormap (widget);
3350   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK;
3351
3352   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3353
3354   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
3355                                    &attributes, attributes_mask);
3356   gdk_window_set_user_data (widget->window, widget);
3357
3358   /* must come before text_window_realize calls */
3359   widget->style = gtk_style_attach (widget->style, widget->window);
3360
3361   gdk_window_set_background (widget->window,
3362                              &widget->style->bg[GTK_WIDGET_STATE (widget)]);
3363
3364   text_window_realize (text_view->text_window, widget->window);
3365
3366   if (text_view->left_window)
3367     text_window_realize (text_view->left_window,
3368                          widget->window);
3369
3370   if (text_view->top_window)
3371     text_window_realize (text_view->top_window,
3372                          widget->window);
3373
3374   if (text_view->right_window)
3375     text_window_realize (text_view->right_window,
3376                          widget->window);
3377
3378   if (text_view->bottom_window)
3379     text_window_realize (text_view->bottom_window,
3380                          widget->window);
3381
3382   gtk_text_view_ensure_layout (text_view);
3383
3384   if (text_view->buffer)
3385     {
3386       GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
3387                                                           GDK_SELECTION_PRIMARY);
3388       gtk_text_buffer_add_selection_clipboard (text_view->buffer, clipboard);
3389     }
3390
3391   tmp_list = text_view->children;
3392   while (tmp_list != NULL)
3393     {
3394       GtkTextViewChild *vc = tmp_list->data;
3395       
3396       text_view_child_set_parent_window (text_view, vc);
3397       
3398       tmp_list = tmp_list->next;
3399     }
3400 }
3401
3402 static void
3403 gtk_text_view_unrealize (GtkWidget *widget)
3404 {
3405   GtkTextView *text_view;
3406   
3407   text_view = GTK_TEXT_VIEW (widget);
3408
3409   if (text_view->buffer)
3410     {
3411       GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
3412                                                           GDK_SELECTION_PRIMARY);
3413       gtk_text_buffer_remove_selection_clipboard (text_view->buffer, clipboard);
3414     }
3415
3416   gtk_text_view_remove_validate_idles (text_view);
3417
3418   if (text_view->popup_menu)
3419     {
3420       gtk_widget_destroy (text_view->popup_menu);
3421       text_view->popup_menu = NULL;
3422     }
3423
3424   text_window_unrealize (text_view->text_window);
3425
3426   if (text_view->left_window)
3427     text_window_unrealize (text_view->left_window);
3428
3429   if (text_view->top_window)
3430     text_window_unrealize (text_view->top_window);
3431
3432   if (text_view->right_window)
3433     text_window_unrealize (text_view->right_window);
3434
3435   if (text_view->bottom_window)
3436     text_window_unrealize (text_view->bottom_window);
3437
3438   gtk_text_view_destroy_layout (text_view);
3439   
3440   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
3441 }
3442
3443 static void 
3444 gtk_text_view_set_background (GtkTextView *text_view)
3445 {
3446   GtkWidget *widget = GTK_WIDGET (text_view);
3447
3448   gdk_window_set_background (widget->window,
3449                              &widget->style->bg[GTK_WIDGET_STATE (widget)]);
3450   
3451   gdk_window_set_background (text_view->text_window->bin_window,
3452                              &widget->style->base[GTK_WIDGET_STATE (widget)]);
3453   
3454   if (text_view->left_window)
3455     gdk_window_set_background (text_view->left_window->bin_window,
3456                                &widget->style->bg[GTK_WIDGET_STATE (widget)]);
3457   if (text_view->right_window)
3458     gdk_window_set_background (text_view->right_window->bin_window,
3459                                &widget->style->bg[GTK_WIDGET_STATE (widget)]);
3460   
3461   if (text_view->top_window)
3462     gdk_window_set_background (text_view->top_window->bin_window,
3463                                &widget->style->bg[GTK_WIDGET_STATE (widget)]);
3464   
3465   if (text_view->bottom_window)
3466     gdk_window_set_background (text_view->bottom_window->bin_window,
3467                                &widget->style->bg[GTK_WIDGET_STATE (widget)]);
3468 }
3469
3470 static void
3471 gtk_text_view_style_set (GtkWidget *widget,
3472                          GtkStyle  *previous_style)
3473 {
3474   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
3475
3476   if (GTK_WIDGET_REALIZED (widget))
3477     {
3478       gtk_text_view_set_background (text_view);
3479     }
3480
3481   if (text_view->layout && previous_style)
3482     {
3483       gtk_text_view_set_attributes_from_style (text_view,
3484                                                text_view->layout->default_style,
3485                                                widget->style);
3486       gtk_text_layout_default_style_changed (text_view->layout);
3487     }
3488 }
3489
3490 static void
3491 gtk_text_view_direction_changed (GtkWidget        *widget,
3492                                  GtkTextDirection  previous_direction)
3493 {
3494   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
3495
3496   if (text_view->layout)
3497     {
3498       text_view->layout->default_style->direction = gtk_widget_get_direction (widget);
3499       gtk_text_layout_default_style_changed (text_view->layout);
3500     }
3501 }
3502
3503 static void
3504 gtk_text_view_state_changed (GtkWidget      *widget,
3505                              GtkStateType    previous_state)
3506 {
3507   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
3508
3509   if (GTK_WIDGET_REALIZED (widget))
3510     {
3511       gtk_text_view_set_background (text_view);
3512     }
3513
3514   if (!GTK_WIDGET_IS_SENSITIVE (widget))
3515     {
3516       /* Clear any selection */
3517       gtk_text_view_unselect (text_view);
3518     }
3519   
3520   gtk_widget_queue_draw (widget);
3521 }
3522
3523 static void
3524 set_invisible_cursor (GdkWindow *window)
3525 {
3526   GdkBitmap *empty_bitmap;
3527   GdkCursor *cursor;
3528   GdkColor useless;
3529   char invisible_cursor_bits[] = { 0x0 };       
3530         
3531   useless.red = useless.green = useless.blue = 0;
3532   useless.pixel = 0;
3533   
3534   empty_bitmap = gdk_bitmap_create_from_data (window,
3535                                               invisible_cursor_bits,
3536                                               1, 1);
3537   
3538   cursor = gdk_cursor_new_from_pixmap (empty_bitmap,
3539                                        empty_bitmap,
3540                                        &useless,
3541                                        &useless, 0, 0);
3542   
3543   gdk_window_set_cursor (window, cursor);
3544   
3545   gdk_cursor_unref (cursor);
3546   
3547   g_object_unref (empty_bitmap);
3548 }
3549
3550 static void
3551 gtk_text_view_obscure_mouse_cursor (GtkTextView *text_view)
3552 {
3553   if (text_view->mouse_cursor_obscured)
3554     return;
3555
3556   set_invisible_cursor (text_view->text_window->bin_window);
3557   
3558   text_view->mouse_cursor_obscured = TRUE;  
3559 }
3560
3561 static void
3562 gtk_text_view_unobscure_mouse_cursor (GtkTextView *text_view)
3563 {
3564   if (text_view->mouse_cursor_obscured)
3565     {
3566       GdkCursor *cursor;
3567       
3568       cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (text_view)),
3569                                            GDK_XTERM);
3570       gdk_window_set_cursor (text_view->text_window->bin_window, cursor);
3571       gdk_cursor_unref (cursor);
3572       text_view->mouse_cursor_obscured = FALSE;
3573     }
3574 }
3575
3576 static void
3577 gtk_text_view_grab_notify (GtkWidget *widget,
3578                            gboolean   was_grabbed)
3579 {
3580   if (!was_grabbed)
3581     gtk_text_view_unobscure_mouse_cursor (GTK_TEXT_VIEW (widget));
3582 }
3583
3584
3585 /*
3586  * Events
3587  */
3588
3589 static gboolean
3590 get_event_coordinates (GdkEvent *event, gint *x, gint *y)
3591 {
3592   if (event)
3593     switch (event->type)
3594       {
3595       case GDK_MOTION_NOTIFY:
3596         *x = event->motion.x;
3597         *y = event->motion.y;
3598         return TRUE;
3599         break;
3600
3601       case GDK_BUTTON_PRESS:
3602       case GDK_2BUTTON_PRESS:
3603       case GDK_3BUTTON_PRESS:
3604       case GDK_BUTTON_RELEASE:
3605         *x = event->button.x;
3606         *y = event->button.y;
3607         return TRUE;
3608         break;
3609
3610       case GDK_KEY_PRESS:
3611       case GDK_KEY_RELEASE:
3612       case GDK_ENTER_NOTIFY:
3613       case GDK_LEAVE_NOTIFY:
3614       case GDK_PROPERTY_NOTIFY:
3615       case GDK_SELECTION_CLEAR:
3616       case GDK_SELECTION_REQUEST:
3617       case GDK_SELECTION_NOTIFY:
3618       case GDK_PROXIMITY_IN:
3619       case GDK_PROXIMITY_OUT:
3620       case GDK_DRAG_ENTER:
3621       case GDK_DRAG_LEAVE:
3622       case GDK_DRAG_MOTION:
3623       case GDK_DRAG_STATUS:
3624       case GDK_DROP_START:
3625       case GDK_DROP_FINISHED:
3626       default:
3627         return FALSE;
3628         break;
3629       }
3630
3631   return FALSE;
3632 }
3633
3634 static gint
3635 emit_event_on_tags (GtkWidget   *widget,
3636                     GdkEvent    *event,
3637                     GtkTextIter *iter)
3638 {
3639   GSList *tags;
3640   GSList *tmp;
3641   gboolean retval = FALSE;
3642   GtkTextView *text_view;
3643
3644   text_view = GTK_TEXT_VIEW (widget);
3645
3646   tags = gtk_text_iter_get_tags (iter);
3647
3648   tmp = tags;
3649   while (tmp != NULL)
3650     {
3651       GtkTextTag *tag = tmp->data;
3652
3653       if (gtk_text_tag_event (tag, G_OBJECT (widget), event, iter))
3654         {
3655           retval = TRUE;
3656           break;
3657         }
3658
3659       tmp = g_slist_next (tmp);
3660     }
3661
3662   g_slist_free (tags);
3663
3664   return retval;
3665 }
3666
3667 static gint
3668 gtk_text_view_event (GtkWidget *widget, GdkEvent *event)
3669 {
3670   GtkTextView *text_view;
3671   gint x = 0, y = 0;
3672
3673   text_view = GTK_TEXT_VIEW (widget);
3674
3675   if (text_view->layout == NULL ||
3676       get_buffer (text_view) == NULL)
3677     return FALSE;
3678
3679   if (event->any.window != text_view->text_window->bin_window)
3680     return FALSE;
3681
3682   if (get_event_coordinates (event, &x, &y))
3683     {
3684       GtkTextIter iter;
3685
3686       x += text_view->xoffset;
3687       y += text_view->yoffset;
3688
3689       /* FIXME this is slow and we do it twice per event.
3690        * My favorite solution is to have GtkTextLayout cache
3691        * the last couple lookups.
3692        */
3693       gtk_text_layout_get_iter_at_pixel (text_view->layout,
3694                                          &iter,
3695                                          x, y);
3696
3697       return emit_event_on_tags (widget, event, &iter);
3698     }
3699   else if (event->type == GDK_KEY_PRESS ||
3700            event->type == GDK_KEY_RELEASE)
3701     {
3702       GtkTextMark *insert;
3703       GtkTextIter iter;
3704
3705       insert = gtk_text_buffer_get_mark (get_buffer (text_view),
3706                                          "insert");
3707
3708       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert);
3709
3710       return emit_event_on_tags (widget, event, &iter);
3711     }
3712   else
3713     return FALSE;
3714 }
3715
3716 static gint
3717 gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
3718 {
3719   gboolean retval = FALSE;
3720   gboolean obscure = FALSE;
3721   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
3722   GtkTextMark *insert;
3723   GtkTextIter iter;
3724   gboolean can_insert;
3725   
3726   if (text_view->layout == NULL ||
3727       get_buffer (text_view) == NULL)
3728     return FALSE;
3729
3730   insert = gtk_text_buffer_get_insert (get_buffer (text_view));
3731   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert);
3732   can_insert = gtk_text_iter_can_insert (&iter, text_view->editable);
3733   if (can_insert &&
3734       gtk_im_context_filter_keypress (text_view->im_context, event))
3735     {
3736       text_view->need_im_reset = TRUE;
3737       obscure = TRUE;
3738       retval = TRUE;
3739     }
3740   /* Binding set */
3741   else if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
3742            GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
3743     retval = TRUE;
3744   /* use overall editability not can_insert, more predictable for users */
3745   else if (text_view->editable &&
3746            (event->keyval == GDK_Return ||
3747             event->keyval == GDK_KP_Enter))
3748     {
3749       /* this won't actually insert the newline if the cursor isn't
3750        * editable
3751        */
3752       gtk_text_view_commit_text (text_view, "\n");
3753
3754       obscure = TRUE;
3755       retval = TRUE;
3756     }
3757   /* Pass through Tab as literal tab, unless Control is held down */
3758   else if ((event->keyval == GDK_Tab ||
3759             event->keyval == GDK_KP_Tab) &&
3760            !(event->state & GDK_CONTROL_MASK))
3761     {
3762       /* If the text widget isn't editable overall, move the focus
3763        * instead
3764        */
3765       if (text_view->editable)
3766         {
3767           gtk_text_view_commit_text (text_view, "\t");
3768           obscure = TRUE;
3769         }
3770       else
3771         gtk_text_view_move_focus (text_view,
3772                                   (event->state & GDK_SHIFT_MASK) ?
3773                                   GTK_DIR_TAB_BACKWARD: GTK_DIR_TAB_FORWARD);
3774
3775       retval = TRUE;
3776     }
3777   else
3778     retval = FALSE;
3779
3780   if (obscure)
3781     gtk_text_view_obscure_mouse_cursor (text_view);
3782   
3783   gtk_text_view_pend_cursor_blink (text_view);
3784
3785   return retval;
3786 }
3787
3788 static gint
3789 gtk_text_view_key_release_event (GtkWidget *widget, GdkEventKey *event)
3790 {
3791   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
3792   GtkTextMark *insert;
3793   GtkTextIter iter;
3794
3795   if (text_view->layout == NULL || get_buffer (text_view) == NULL)
3796     return FALSE;
3797       
3798   insert = gtk_text_buffer_get_insert (get_buffer (text_view));
3799   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert);
3800   if (gtk_text_iter_can_insert (&iter, text_view->editable) &&
3801       gtk_im_context_filter_keypress (text_view->im_context, event))
3802     {
3803       text_view->need_im_reset = TRUE;
3804       return TRUE;
3805     }
3806   else
3807     return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
3808 }
3809
3810 static gint
3811 gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
3812 {
3813   GtkTextView *text_view;
3814
3815   text_view = GTK_TEXT_VIEW (widget);
3816
3817   text_view->disable_scroll_on_focus = TRUE;
3818   gtk_widget_grab_focus (widget);
3819   text_view->disable_scroll_on_focus = FALSE;
3820
3821   if (event->window != text_view->text_window->bin_window)
3822     {
3823       /* Remove selection if any. */
3824       gtk_text_view_unselect (text_view);
3825       return FALSE;
3826     }
3827
3828 #if 0
3829   /* debug hack */
3830   if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0)
3831     _gtk_text_buffer_spew (GTK_TEXT_VIEW (widget)->buffer);
3832   else if (event->button == 3)
3833     gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout);
3834 #endif
3835
3836   if (event->type == GDK_BUTTON_PRESS)
3837     {
3838       gtk_text_view_reset_im_context (text_view);
3839
3840       if (event->button == 1)
3841         {
3842           /* If we're in the selection, start a drag copy/move of the
3843            * selection; otherwise, start creating a new selection.
3844            */
3845           GtkTextIter iter;
3846           GtkTextIter start, end;
3847
3848           gtk_text_layout_get_iter_at_pixel (text_view->layout,
3849                                              &iter,
3850                                              event->x + text_view->xoffset,
3851                                              event->y + text_view->yoffset);
3852
3853           if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
3854                                                     &start, &end) &&
3855               gtk_text_iter_in_range (&iter, &start, &end))
3856             {
3857               text_view->drag_start_x = event->x;
3858               text_view->drag_start_y = event->y;
3859               text_view->pending_place_cursor_button = event->button;
3860             }
3861           else
3862             {
3863               gtk_text_view_start_selection_drag (text_view, &iter, event);
3864             }
3865
3866           return TRUE;
3867         }
3868       else if (event->button == 2)
3869         {
3870           GtkTextIter iter;
3871
3872           gtk_text_layout_get_iter_at_pixel (text_view->layout,
3873                                              &iter,
3874                                              event->x + text_view->xoffset,
3875                                              event->y + text_view->yoffset);
3876
3877           gtk_text_buffer_paste_clipboard (get_buffer (text_view),
3878                                            gtk_widget_get_clipboard (widget, GDK_SELECTION_PRIMARY),
3879                                            &iter,
3880                                            text_view->editable);
3881           return TRUE;
3882         }
3883       else if (event->button == 3)
3884         {
3885           gtk_text_view_do_popup (text_view, event);
3886           return TRUE;
3887         }
3888     }
3889   else if ((event->type == GDK_2BUTTON_PRESS ||
3890             event->type == GDK_3BUTTON_PRESS) &&
3891            event->button == 1) 
3892     {
3893       GtkTextIter iter;
3894
3895       gtk_text_view_end_selection_drag (text_view, event);      
3896
3897       gtk_text_layout_get_iter_at_pixel (text_view->layout,
3898                                          &iter,
3899                                          event->x + text_view->xoffset,
3900                                          event->y + text_view->yoffset);
3901       
3902       gtk_text_view_start_selection_drag (text_view, &iter, event);
3903       return TRUE;
3904     }
3905   
3906   return FALSE;
3907 }
3908
3909 static gint
3910 gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
3911 {
3912   GtkTextView *text_view;
3913
3914   text_view = GTK_TEXT_VIEW (widget);
3915
3916   if (event->window != text_view->text_window->bin_window)
3917     return FALSE;
3918
3919   if (event->button == 1)
3920     {
3921       if (text_view->drag_start_x >= 0)
3922         {
3923           text_view->drag_start_x = -1;
3924           text_view->drag_start_y = -1;
3925         }
3926
3927       if (gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget), event))
3928         return TRUE;
3929       else if (text_view->pending_place_cursor_button == event->button)
3930         {
3931           GtkTextIter iter;
3932
3933           /* Unselect everything; we clicked inside selection, but
3934            * didn't move by the drag threshold, so just clear selection
3935            * and place cursor.
3936            */
3937           gtk_text_layout_get_iter_at_pixel (text_view->layout,
3938                                              &iter,
3939                                              event->x + text_view->xoffset,
3940                                              event->y + text_view->yoffset);
3941
3942           gtk_text_buffer_place_cursor (get_buffer (text_view), &iter);
3943
3944           text_view->pending_place_cursor_button = 0;
3945           
3946           return FALSE;
3947         }
3948     }
3949
3950   return FALSE;
3951 }
3952
3953 static void
3954 keymap_direction_changed (GdkKeymap   *keymap,
3955                           GtkTextView *text_view)
3956 {
3957   gtk_text_view_check_keymap_direction (text_view);
3958 }
3959
3960 static gint
3961 gtk_text_view_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
3962 {
3963   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
3964
3965   gtk_widget_queue_draw (widget);
3966
3967   DV(g_print (G_STRLOC": focus_in_event\n"));
3968   
3969   if (text_view->cursor_visible && text_view->layout)
3970     {
3971       gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);
3972       gtk_text_view_check_cursor_blink (text_view);
3973     }
3974
3975   g_signal_connect (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
3976                     "direction_changed",
3977                     G_CALLBACK (keymap_direction_changed), text_view);
3978   gtk_text_view_check_keymap_direction (text_view);
3979   
3980   text_view->need_im_reset = TRUE;
3981   gtk_im_context_focus_in (GTK_TEXT_VIEW (widget)->im_context);
3982
3983   return FALSE;
3984 }
3985
3986 static gint
3987 gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
3988 {
3989   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
3990
3991   gtk_widget_queue_draw (widget);
3992
3993   DV(g_print (G_STRLOC": focus_out_event\n"));
3994   
3995   if (text_view->cursor_visible && text_view->layout)
3996     {
3997       gtk_text_layout_set_cursor_visible (text_view->layout, FALSE);
3998       gtk_text_view_check_cursor_blink (text_view);
3999     }
4000
4001   g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
4002                                         keymap_direction_changed,
4003                                         text_view);
4004
4005   text_view->need_im_reset = TRUE;
4006   gtk_im_context_focus_out (GTK_TEXT_VIEW (widget)->im_context);
4007
4008   return FALSE;
4009 }
4010
4011 static gint
4012 gtk_text_view_motion_event (GtkWidget *widget, GdkEventMotion *event)
4013 {
4014   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
4015
4016   gtk_text_view_unobscure_mouse_cursor (text_view);
4017
4018   if (event->window == text_view->text_window->bin_window &&
4019       text_view->drag_start_x >= 0)
4020     {
4021       gint x, y;
4022
4023       gdk_window_get_pointer (text_view->text_window->bin_window,
4024                               &x, &y, NULL);
4025
4026       if (gtk_drag_check_threshold (widget,
4027                                     text_view->drag_start_x, 
4028                                     text_view->drag_start_y,
4029                                     x, y))
4030         {
4031           GtkTextIter iter;
4032           gint buffer_x, buffer_y;
4033
4034           gtk_text_view_window_to_buffer_coords (text_view,
4035                                                  GTK_TEXT_WINDOW_TEXT,
4036                                                  text_view->drag_start_x,
4037                                                  text_view->drag_start_y,
4038                                                  &buffer_x,
4039                                                  &buffer_y);
4040
4041           gtk_text_layout_get_iter_at_pixel (text_view->layout,
4042                                              &iter,
4043                                              buffer_x, buffer_y);
4044
4045           gtk_text_view_start_selection_dnd (text_view, &iter, event);
4046           return TRUE;
4047         }
4048     }
4049
4050   return FALSE;
4051 }
4052
4053 static void
4054 gtk_text_view_paint (GtkWidget      *widget,
4055                      GdkRectangle   *area,
4056                      GdkEventExpose *event)
4057 {
4058   GtkTextView *text_view;
4059   GList *child_exposes;
4060   GList *tmp_list;
4061   GdkRegion *updates;
4062   
4063   text_view = GTK_TEXT_VIEW (widget);
4064
4065   g_return_if_fail (text_view->layout != NULL);
4066   g_return_if_fail (text_view->xoffset >= 0);
4067   g_return_if_fail (text_view->yoffset >= 0);
4068
4069   while (text_view->first_validate_idle != 0)
4070     {
4071       DV (g_print (G_STRLOC": first_validate_idle: %d\n",
4072                    text_view->first_validate_idle));
4073       gtk_text_view_flush_first_validate (text_view);
4074     }
4075
4076   /* More regions could have become invalid in the above loop */
4077   updates = gdk_window_get_update_area (text_view->text_window->bin_window);
4078   if (updates)
4079     {
4080       GdkRectangle rect;
4081       
4082       gdk_region_get_clipbox (updates, &rect);
4083
4084       gdk_rectangle_union (area, &rect, area);
4085       
4086       gdk_region_destroy (updates);
4087     }
4088   
4089   if (!text_view->onscreen_validated)
4090     {
4091       g_warning (G_STRLOC ": somehow some text lines were modified or scrolling occurred since the last validation of lines on the screen - may be a text widget bug.");
4092       g_assert_not_reached ();
4093     }
4094   
4095 #if 0
4096   printf ("painting %d,%d  %d x %d\n",
4097           area->x, area->y,
4098           area->width, area->height);
4099 #endif
4100
4101   child_exposes = NULL;
4102   gtk_text_layout_draw (text_view->layout,
4103                         widget,
4104                         text_view->text_window->bin_window,
4105                         NULL,
4106                         text_view->xoffset,
4107                         text_view->yoffset,
4108                         area->x, area->y,
4109                         area->width, area->height,
4110                         &child_exposes);
4111
4112   tmp_list = child_exposes;
4113   while (tmp_list != NULL)
4114     {
4115       GtkWidget *child = tmp_list->data;
4116       
4117       gtk_container_propagate_expose (GTK_CONTAINER (text_view),
4118                                       child,
4119                                       event);
4120
4121       g_object_unref (child);
4122       
4123       tmp_list = tmp_list->next;
4124     }
4125
4126   g_list_free (child_exposes);
4127 }
4128
4129 static gint
4130 gtk_text_view_expose_event (GtkWidget *widget, GdkEventExpose *event)
4131 {
4132   GSList *tmp_list;
4133   
4134   if (event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (widget),
4135                                                  GTK_TEXT_WINDOW_TEXT))
4136     {
4137       DV(g_print (">Exposed ("G_STRLOC")\n"));
4138       gtk_text_view_paint (widget, &event->area, event);
4139     }
4140
4141   if (event->window == widget->window)
4142     gtk_text_view_draw_focus (widget);
4143
4144   /* Propagate exposes to all children not in the buffer. */
4145   tmp_list = GTK_TEXT_VIEW (widget)->children;
4146   while (tmp_list != NULL)
4147     {
4148       GtkTextViewChild *vc = tmp_list->data;
4149
4150       /* propagate_expose checks that event->window matches
4151        * child->window
4152        */
4153       if (vc->type != GTK_TEXT_WINDOW_TEXT)
4154         gtk_container_propagate_expose (GTK_CONTAINER (widget),
4155                                         vc->widget,
4156                                         event);
4157       
4158       tmp_list = tmp_list->next;
4159     }
4160   
4161   return FALSE;
4162 }
4163
4164 static void
4165 gtk_text_view_draw_focus (GtkWidget *widget)
4166 {
4167   gboolean interior_focus;
4168
4169   /* We clear the focus if we are in interior focus mode. */
4170   gtk_widget_style_get (widget,
4171                         "interior_focus", &interior_focus,
4172                         NULL);
4173   
4174   if (GTK_WIDGET_DRAWABLE (widget))
4175     {
4176       if (GTK_WIDGET_HAS_FOCUS (widget) && !interior_focus)
4177         {          
4178           gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget), 
4179                            NULL, widget, "textview",
4180                            0, 0,
4181                            widget->allocation.width,
4182                            widget->allocation.height);
4183         }
4184       else
4185         {
4186           gdk_window_clear (widget->window);
4187         }
4188     }
4189 }
4190
4191 static void
4192 gtk_text_view_grab_focus (GtkWidget *widget)
4193 {
4194   GtkTextView *text_view;
4195
4196   text_view = GTK_TEXT_VIEW (widget);
4197   
4198   GTK_WIDGET_CLASS (parent_class)->grab_focus (widget);
4199
4200   if (!text_view->disable_scroll_on_focus)
4201     gtk_text_view_scroll_mark_onscreen (text_view,
4202                                         gtk_text_buffer_get_mark (get_buffer (text_view),
4203                                                                   "insert"));
4204 }
4205
4206 static gboolean
4207 gtk_text_view_focus (GtkWidget        *widget,
4208                      GtkDirectionType  direction)
4209 {
4210   GtkTextView *text_view;
4211   GtkContainer *container;
4212   gboolean result;
4213   
4214   text_view = GTK_TEXT_VIEW (widget);
4215   container = GTK_CONTAINER (widget);  
4216
4217   if (!gtk_widget_is_focus (widget) &&
4218       container->focus_child == NULL)
4219     {
4220       gtk_widget_grab_focus (widget);
4221       return TRUE;
4222     }
4223   else
4224     {
4225       /*
4226        * Unset CAN_FOCUS flag so that gtk_container_focus() allows
4227        * children to get the focus
4228        */
4229       GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS); 
4230       result = GTK_WIDGET_CLASS (parent_class)->focus (widget, direction);
4231       GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); 
4232
4233       return result;
4234     }
4235 }
4236
4237 /*
4238  * Container
4239  */
4240
4241 static void
4242 gtk_text_view_add (GtkContainer *container,
4243                    GtkWidget    *child)
4244 {
4245   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
4246   g_return_if_fail (GTK_IS_WIDGET (child));
4247
4248   /* This is pretty random. */
4249   gtk_text_view_add_child_in_window (GTK_TEXT_VIEW (container),
4250                                      child,
4251                                      GTK_TEXT_WINDOW_WIDGET,
4252                                      0, 0);
4253 }
4254
4255 static void
4256 gtk_text_view_remove (GtkContainer *container,
4257                       GtkWidget    *child)
4258 {
4259   GSList *iter;
4260   GtkTextView *text_view;
4261   GtkTextViewChild *vc;
4262
4263   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
4264   g_return_if_fail (GTK_IS_WIDGET (child));
4265   g_return_if_fail (child->parent == (GtkWidget*) container);
4266
4267   text_view = GTK_TEXT_VIEW (container);
4268
4269   vc = NULL;
4270   iter = text_view->children;
4271
4272   while (iter != NULL)
4273     {
4274       vc = iter->data;
4275
4276       if (vc->widget == child)
4277         break;
4278
4279       iter = g_slist_next (iter);
4280     }
4281
4282   g_assert (iter != NULL); /* be sure we had the child in the list */
4283
4284   text_view->children = g_slist_remove (text_view->children, vc);
4285
4286   gtk_widget_unparent (vc->widget);
4287
4288   text_view_child_free (vc);
4289 }
4290
4291 static void
4292 gtk_text_view_forall (GtkContainer *container,
4293                       gboolean      include_internals,
4294                       GtkCallback   callback,
4295                       gpointer      callback_data)
4296 {
4297   GSList *iter;
4298   GtkTextView *text_view;
4299   GSList *copy;
4300
4301   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
4302   g_return_if_fail (callback != NULL);
4303
4304   text_view = GTK_TEXT_VIEW (container);
4305
4306   copy = g_slist_copy (text_view->children);
4307   iter = copy;
4308
4309   while (iter != NULL)
4310     {
4311       GtkTextViewChild *vc = iter->data;
4312
4313       (* callback) (vc->widget, callback_data);
4314
4315       iter = g_slist_next (iter);
4316     }
4317
4318   g_slist_free (copy);
4319 }
4320
4321 #define CURSOR_ON_MULTIPLIER 0.66
4322 #define CURSOR_OFF_MULTIPLIER 0.34
4323 #define CURSOR_PEND_MULTIPLIER 1.0
4324
4325 static gboolean
4326 cursor_blinks (GtkTextView *text_view)
4327 {
4328   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
4329   gboolean blink;
4330
4331 #ifdef DEBUG_VALIDATION_AND_SCROLLING
4332   return FALSE;
4333 #endif
4334   if (gtk_debug_flags & GTK_DEBUG_UPDATES)
4335     return FALSE;
4336   
4337   g_object_get (settings, "gtk-cursor-blink", &blink, NULL);
4338   return blink;
4339 }
4340
4341 static gint
4342 get_cursor_time (GtkTextView *text_view)
4343 {
4344   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
4345   gint time;
4346
4347   g_object_get (settings, "gtk-cursor-blink-time", &time, NULL);
4348
4349   return time;
4350 }
4351
4352 /*
4353  * Blink!
4354  */
4355
4356 static gint
4357 blink_cb (gpointer data)
4358 {
4359   GtkTextView *text_view;
4360   gboolean visible;
4361
4362   GDK_THREADS_ENTER ();
4363
4364   text_view = GTK_TEXT_VIEW (data);
4365   
4366   g_assert (text_view->layout);
4367   g_assert (GTK_WIDGET_HAS_FOCUS (text_view));
4368   g_assert (text_view->cursor_visible);
4369
4370   visible = gtk_text_layout_get_cursor_visible (text_view->layout);
4371
4372   if (visible)
4373     text_view->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_OFF_MULTIPLIER,
4374                                               blink_cb,
4375                                               text_view);
4376   else
4377     text_view->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_ON_MULTIPLIER,
4378                                               blink_cb,
4379                                               text_view);
4380   
4381   gtk_text_layout_set_cursor_visible (text_view->layout,
4382                                       !visible);
4383
4384   GDK_THREADS_LEAVE ();
4385
4386   /* Remove ourselves */
4387   return FALSE;
4388 }
4389
4390
4391 static void
4392 gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
4393 {
4394   if (text_view->blink_timeout)  
4395     { 
4396       g_source_remove (text_view->blink_timeout);
4397       text_view->blink_timeout = 0;
4398     }
4399 }
4400
4401 static void
4402 gtk_text_view_check_cursor_blink (GtkTextView *text_view)
4403 {
4404   if (text_view->layout != NULL &&
4405       text_view->cursor_visible &&
4406       GTK_WIDGET_HAS_FOCUS (text_view))
4407     {
4408       if (cursor_blinks (text_view))
4409         {
4410           if (text_view->blink_timeout == 0)
4411             {
4412               gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);
4413               
4414               text_view->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_OFF_MULTIPLIER,
4415                                                         blink_cb,
4416                                                         text_view);
4417             }
4418         }
4419       else
4420         gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);   
4421     }
4422   else
4423     {
4424       gtk_text_view_stop_cursor_blink (text_view);
4425     }
4426 }
4427
4428 static void
4429 gtk_text_view_pend_cursor_blink(GtkTextView *text_view)
4430 {
4431   if (text_view->layout != NULL &&
4432       text_view->cursor_visible &&
4433       GTK_WIDGET_HAS_FOCUS (text_view) &&
4434       cursor_blinks (text_view))
4435     {
4436       if (text_view->blink_timeout != 0)
4437         {
4438           g_source_remove (text_view->blink_timeout);
4439           text_view->blink_timeout = 0;
4440         }
4441       
4442       gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);
4443       
4444       text_view->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_PEND_MULTIPLIER,
4445                                                 blink_cb,
4446                                                 text_view);
4447     }
4448 }
4449
4450
4451 /*
4452  * Key binding handlers
4453  */
4454
4455 static void
4456 gtk_text_view_move_iter_by_lines (GtkTextView *text_view,
4457                                   GtkTextIter *newplace,
4458                                   gint         count)
4459 {
4460   while (count < 0)
4461     {
4462       gtk_text_layout_move_iter_to_previous_line (text_view->layout, newplace);
4463       count++;
4464     }
4465
4466   while (count > 0)
4467     {
4468       gtk_text_layout_move_iter_to_next_line (text_view->layout, newplace);
4469       count--;
4470     }
4471 }
4472
4473 static void
4474 move_cursor (GtkTextView       *text_view,
4475              const GtkTextIter *new_location,
4476              gboolean           extend_selection)
4477 {
4478   if (extend_selection)
4479     gtk_text_buffer_move_mark_by_name (get_buffer (text_view),
4480                                        "insert",
4481                                        new_location);
4482   else
4483     gtk_text_buffer_place_cursor (get_buffer (text_view),
4484                                   new_location);
4485 }
4486
4487 static void
4488 gtk_text_view_move_cursor_internal (GtkTextView     *text_view,
4489                                     GtkMovementStep  step,
4490                                     gint             count,
4491                                     gboolean         extend_selection)
4492 {
4493   GtkTextIter insert;
4494   GtkTextIter newplace;
4495
4496   gint cursor_x_pos = 0;
4497
4498   gtk_text_view_reset_im_context (text_view);
4499
4500   if (step == GTK_MOVEMENT_PAGES)
4501     {
4502       gtk_text_view_scroll_pages (text_view, count, extend_selection);
4503       gtk_text_view_pend_cursor_blink (text_view);
4504       return;
4505     }
4506   else if (step == GTK_MOVEMENT_HORIZONTAL_PAGES)
4507     {
4508       gtk_text_view_scroll_hpages (text_view, count, extend_selection);
4509       gtk_text_view_pend_cursor_blink (text_view);
4510       return;
4511     }
4512
4513   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
4514                                     gtk_text_buffer_get_mark (get_buffer (text_view),
4515                                                               "insert"));
4516   newplace = insert;
4517
4518   if (step == GTK_MOVEMENT_DISPLAY_LINES)
4519     gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, NULL);
4520
4521   switch (step)
4522     {
4523     case GTK_MOVEMENT_LOGICAL_POSITIONS:
4524       gtk_text_iter_forward_visible_cursor_positions (&newplace, count);
4525       break;
4526
4527     case GTK_MOVEMENT_VISUAL_POSITIONS:
4528       gtk_text_layout_move_iter_visually (text_view->layout,
4529                                           &newplace, count);
4530       break;
4531
4532     case GTK_MOVEMENT_WORDS:
4533       if (count < 0)
4534         gtk_text_iter_backward_visible_word_starts (&newplace, -count);
4535       else if (count > 0)
4536         gtk_text_iter_forward_visible_word_ends (&newplace, count);
4537       break;
4538
4539     case GTK_MOVEMENT_DISPLAY_LINES:
4540       gtk_text_view_move_iter_by_lines (text_view, &newplace, count);
4541       gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
4542       break;
4543
4544     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
4545       if (count > 1)
4546         gtk_text_view_move_iter_by_lines (text_view, &newplace, --count);
4547       else if (count < -1)
4548         gtk_text_view_move_iter_by_lines (text_view, &newplace, ++count);
4549
4550       if (count != 0)
4551         gtk_text_layout_move_iter_to_line_end (text_view->layout, &newplace, count);
4552       break;
4553
4554     case GTK_MOVEMENT_PARAGRAPHS:
4555       if (count > 0)
4556         {
4557           if (!gtk_text_iter_ends_line (&newplace))
4558             {
4559               gtk_text_iter_forward_to_line_end (&newplace);
4560               --count;
4561             }
4562           gtk_text_iter_forward_lines (&newplace, count);
4563           gtk_text_iter_forward_to_line_end (&newplace);
4564         }
4565       else if (count < 0)
4566         {
4567           if (gtk_text_iter_get_line_offset (&newplace) > 0)
4568             {
4569               gtk_text_iter_set_line_offset (&newplace, 0);
4570               ++count;
4571             }
4572           gtk_text_iter_forward_lines (&newplace, count);
4573           gtk_text_iter_set_line_offset (&newplace, 0);
4574         }
4575       break;
4576
4577     case GTK_MOVEMENT_PARAGRAPH_ENDS:
4578       if (count > 0)
4579         {
4580           if (!gtk_text_iter_ends_line (&newplace))
4581             gtk_text_iter_forward_to_line_end (&newplace);
4582         }
4583       else if (count < 0)
4584         {
4585           gtk_text_iter_set_line_offset (&newplace, 0);
4586         }
4587       break;
4588
4589     case GTK_MOVEMENT_BUFFER_ENDS:
4590       if (count > 0)
4591         gtk_text_buffer_get_end_iter (get_buffer (text_view), &newplace);
4592       else if (count < 0)
4593         gtk_text_buffer_get_iter_at_offset (get_buffer (text_view), &newplace, 0);
4594      break;
4595       
4596     default:
4597       break;
4598     }
4599
4600   /* call move_cursor() even if the cursor hasn't moved, since it 
4601      cancels the selection
4602   */
4603   move_cursor (text_view, &newplace, extend_selection);
4604
4605   if (!gtk_text_iter_equal (&insert, &newplace))
4606     {
4607       DV(g_print (G_STRLOC": scrolling onscreen\n"));
4608       gtk_text_view_scroll_mark_onscreen (text_view,
4609                                           gtk_text_buffer_get_mark (get_buffer (text_view),
4610                                                                     "insert"));
4611
4612       if (step == GTK_MOVEMENT_DISPLAY_LINES)
4613         {
4614           gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
4615         }
4616     }
4617
4618   gtk_text_view_pend_cursor_blink (text_view);
4619 }
4620
4621 static void
4622 gtk_text_view_move_cursor (GtkTextView     *text_view,
4623                            GtkMovementStep  step,
4624                            gint             count,
4625                            gboolean         extend_selection)
4626 {
4627   gtk_text_view_move_cursor_internal (text_view, step, count, extend_selection);
4628 }
4629
4630 static void
4631 gtk_text_view_page_horizontally (GtkTextView     *text_view,
4632                                  gint             count,
4633                                  gboolean         extend_selection)
4634 {
4635   gtk_text_view_move_cursor_internal (text_view, GTK_MOVEMENT_HORIZONTAL_PAGES,
4636                                       count, extend_selection);
4637 }
4638
4639 static void
4640 gtk_text_view_set_anchor (GtkTextView *text_view)
4641 {
4642   GtkTextIter insert;
4643
4644   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
4645                                     gtk_text_buffer_get_mark (get_buffer (text_view),
4646                                                               "insert"));
4647
4648   gtk_text_buffer_create_mark (get_buffer (text_view), "anchor", &insert, TRUE);
4649 }
4650
4651 static void
4652 gtk_text_view_scroll_pages (GtkTextView *text_view,
4653                             gint         count,
4654                             gboolean     extend_selection)
4655 {
4656   gdouble newval;
4657   gdouble oldval;
4658   GtkAdjustment *adj;
4659   gint cursor_x_pos, cursor_y_pos;
4660   GtkTextIter new_insert;
4661   GtkTextIter anchor;
4662   gint y0, y1;
4663
4664   g_return_if_fail (text_view->vadjustment != NULL);
4665
4666   cancel_pending_scroll (text_view);
4667   
4668   adj = text_view->vadjustment;
4669
4670   /* Validate the region that will be brought into view by the cursor motion
4671    */
4672   if (count < 0)
4673     {
4674       gtk_text_view_get_first_para_iter (text_view, &anchor);
4675       y0 = adj->page_size;
4676       y1 = adj->page_size + count * adj->page_increment;
4677     }
4678   else
4679     {
4680       gtk_text_view_get_first_para_iter (text_view, &anchor);
4681       y0 = count * adj->page_increment + adj->page_size;
4682       y1 = 0;
4683     }
4684
4685   gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1);
4686   /* FIXME do we need to update the adjustment ranges here? */
4687
4688   if (count < 0 && adj->value <= (adj->lower + 1e-12))
4689     {
4690       /* already at top, just be sure we are at offset 0 */
4691       gtk_text_buffer_get_start_iter (get_buffer (text_view), &new_insert);
4692       move_cursor (text_view, &new_insert, extend_selection);
4693     }
4694   else if (count > 0 && adj->value >= (adj->upper - adj->page_size - 1e-12))
4695     {
4696       /* already at bottom, just be sure we are at the end */
4697       gtk_text_buffer_get_end_iter (get_buffer (text_view), &new_insert);
4698       move_cursor (text_view, &new_insert, extend_selection);
4699     }
4700   else
4701     {
4702       gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
4703
4704       newval = adj->value;
4705       oldval = adj->value;
4706   
4707       newval += count * adj->page_increment;
4708
4709       set_adjustment_clamped (adj, newval);
4710       cursor_y_pos += adj->value - oldval;
4711
4712       gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos);
4713       clamp_iter_onscreen (text_view, &new_insert);
4714       move_cursor (text_view, &new_insert, extend_selection);
4715
4716       gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
4717     }
4718   
4719   /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
4720    * only guarantees 1 pixel onscreen.
4721    */
4722   DV(g_print (G_STRLOC": scrolling onscreen\n"));
4723   gtk_text_view_scroll_mark_onscreen (text_view,
4724                                       gtk_text_buffer_get_mark (get_buffer (text_view),
4725                                                                 "insert"));
4726 }
4727
4728 static void
4729 gtk_text_view_scroll_hpages (GtkTextView *text_view,
4730                              gint         count,
4731                              gboolean     extend_selection)
4732 {
4733   gdouble newval;
4734   gdouble oldval;
4735   GtkAdjustment *adj;
4736   gint cursor_x_pos, cursor_y_pos;
4737   GtkTextIter new_insert;
4738   gint y, height;
4739   
4740   g_return_if_fail (text_view->hadjustment != NULL);
4741
4742   cancel_pending_scroll (text_view);
4743   
4744   adj = text_view->hadjustment;
4745
4746   /* Validate the line that we're moving within.
4747    */
4748   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
4749                                     &new_insert,
4750                                     gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
4751   gtk_text_layout_get_line_yrange (text_view->layout, &new_insert, &y, &height);
4752   gtk_text_layout_validate_yrange (text_view->layout, &new_insert, y, y + height);
4753   /* FIXME do we need to update the adjustment ranges here? */
4754   
4755   if (count < 0 && adj->value <= (adj->lower + 1e-12))
4756     {
4757       /* already at far left, just be sure we are at offset 0 */
4758       gtk_text_iter_set_line_offset (&new_insert, 0);
4759       move_cursor (text_view, &new_insert, extend_selection);
4760     }
4761   else if (count > 0 && adj->value >= (adj->upper - adj->page_size - 1e-12))
4762     {
4763       /* already at far right, just be sure we are at the end */
4764       gtk_text_iter_forward_to_line_end (&new_insert);
4765       move_cursor (text_view, &new_insert, extend_selection);
4766     }
4767   else
4768     {
4769       gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
4770
4771       newval = adj->value;
4772       oldval = adj->value;
4773   
4774       newval += count * adj->page_increment;
4775
4776       set_adjustment_clamped (adj, newval);
4777       cursor_x_pos += adj->value - oldval;
4778
4779       gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos);
4780       clamp_iter_onscreen (text_view, &new_insert);
4781       move_cursor (text_view, &new_insert, extend_selection);
4782
4783       gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
4784     }
4785
4786   /*  FIXME for lines shorter than the overall widget width, this results in a
4787    *  "bounce" effect as we scroll to the right of the widget, then scroll
4788    *  back to get the end of the line onscreen.
4789    *      http://bugzilla.gnome.org/show_bug.cgi?id=68963
4790    */
4791   
4792   /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
4793    * only guarantees 1 pixel onscreen.
4794    */
4795   DV(g_print (G_STRLOC": scrolling onscreen\n"));
4796   gtk_text_view_scroll_mark_onscreen (text_view,
4797                                       gtk_text_buffer_get_mark (get_buffer (text_view),
4798                                                                 "insert"));
4799 }
4800
4801 static gboolean
4802 whitespace (gunichar ch, gpointer user_data)
4803 {
4804   return (ch == ' ' || ch == '\t');
4805 }
4806
4807 static gboolean
4808 not_whitespace (gunichar ch, gpointer user_data)
4809 {
4810   return !whitespace (ch, user_data);
4811 }
4812
4813 static gboolean
4814 find_whitepace_region (const GtkTextIter *center,
4815                        GtkTextIter *start, GtkTextIter *end)
4816 {
4817   *start = *center;
4818   *end = *center;
4819
4820   if (gtk_text_iter_backward_find_char (start, not_whitespace, NULL, NULL))
4821     gtk_text_iter_forward_char (start); /* we want the first whitespace... */
4822   if (whitespace (gtk_text_iter_get_char (end), NULL))
4823     gtk_text_iter_forward_find_char (end, not_whitespace, NULL, NULL);
4824
4825   return !gtk_text_iter_equal (start, end);
4826 }
4827
4828 static void
4829 gtk_text_view_insert_at_cursor (GtkTextView *text_view,
4830                                 const gchar *str)
4831 {
4832   gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
4833                                                 text_view->editable);
4834 }
4835
4836 static void
4837 gtk_text_view_delete_from_cursor (GtkTextView   *text_view,
4838                                   GtkDeleteType  type,
4839                                   gint           count)
4840 {
4841   GtkTextIter insert;
4842   GtkTextIter start;
4843   GtkTextIter end;
4844   gboolean leave_one = FALSE;
4845
4846   gtk_text_view_reset_im_context (text_view);
4847
4848   if (type == GTK_DELETE_CHARS)
4849     {
4850       /* Char delete deletes the selection, if one exists */
4851       if (gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
4852                                             text_view->editable))
4853         return;
4854     }
4855
4856   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
4857                                     &insert,
4858                                     gtk_text_buffer_get_mark (get_buffer (text_view),
4859                                                               "insert"));
4860
4861   start = insert;
4862   end = insert;
4863
4864   switch (type)
4865     {
4866     case GTK_DELETE_CHARS:
4867       gtk_text_iter_forward_cursor_positions (&end, count);
4868       break;
4869
4870     case GTK_DELETE_WORD_ENDS:
4871       if (count > 0)
4872         gtk_text_iter_forward_word_ends (&end, count);
4873       else if (count < 0)
4874         gtk_text_iter_backward_word_starts (&start, 0 - count);
4875       break;
4876
4877     case GTK_DELETE_WORDS:
4878       break;
4879
4880     case GTK_DELETE_DISPLAY_LINE_ENDS:
4881       break;
4882
4883     case GTK_DELETE_DISPLAY_LINES:
4884       break;
4885
4886     case GTK_DELETE_PARAGRAPH_ENDS:
4887       /* If we're already at a newline, we need to
4888        * simply delete that newline, instead of
4889        * moving to the next one.
4890        */
4891       if (gtk_text_iter_ends_line (&end))
4892         {
4893           gtk_text_iter_forward_line (&end);
4894           --count;
4895         }
4896
4897       while (count > 0)
4898         {
4899           if (!gtk_text_iter_forward_to_line_end (&end))
4900             break;
4901
4902           --count;
4903         }
4904
4905       /* FIXME figure out what a negative count means
4906          and support that */
4907       break;
4908
4909     case GTK_DELETE_PARAGRAPHS:
4910       if (count > 0)
4911         {
4912           gtk_text_iter_set_line_offset (&start, 0);
4913           gtk_text_iter_forward_to_line_end (&end);
4914
4915           /* Do the lines beyond the first. */
4916           while (count > 1)
4917             {
4918               gtk_text_iter_forward_to_line_end (&end);
4919
4920               --count;
4921             }
4922         }
4923
4924       /* FIXME negative count? */
4925
4926       break;
4927
4928     case GTK_DELETE_WHITESPACE:
4929       {
4930         find_whitepace_region (&insert, &start, &end);
4931       }
4932       break;
4933
4934     default:
4935       break;
4936     }
4937
4938   if (!gtk_text_iter_equal (&start, &end))
4939     {
4940       gtk_text_buffer_begin_user_action (get_buffer (text_view));
4941
4942       if (gtk_text_buffer_delete_interactive (get_buffer (text_view), &start, &end,
4943                                               text_view->editable))
4944         {
4945           if (leave_one)
4946             gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view),
4947                                                           " ", 1,
4948                                                           text_view->editable);
4949         }
4950
4951       gtk_text_buffer_end_user_action (get_buffer (text_view));
4952
4953       DV(g_print (G_STRLOC": scrolling onscreen\n"));
4954       gtk_text_view_scroll_mark_onscreen (text_view,
4955                                           gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
4956     }
4957 }
4958
4959 static void
4960 gtk_text_view_cut_clipboard (GtkTextView *text_view)
4961 {
4962   GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
4963                                                       GDK_SELECTION_CLIPBOARD);
4964   
4965   gtk_text_buffer_cut_clipboard (get_buffer (text_view),
4966                                  clipboard,
4967                                  text_view->editable);
4968   DV(g_print (G_STRLOC": scrolling onscreen\n"));
4969   gtk_text_view_scroll_mark_onscreen (text_view,
4970                                       gtk_text_buffer_get_mark (get_buffer (text_view),
4971                                                                 "insert"));
4972 }
4973
4974 static void
4975 gtk_text_view_copy_clipboard (GtkTextView *text_view)
4976 {
4977   GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
4978                                                       GDK_SELECTION_CLIPBOARD);
4979   
4980   gtk_text_buffer_copy_clipboard (get_buffer (text_view),
4981                                   clipboard);
4982   DV(g_print (G_STRLOC": scrolling onscreen\n"));
4983   gtk_text_view_scroll_mark_onscreen (text_view,
4984                                       gtk_text_buffer_get_mark (get_buffer (text_view),
4985                                                                 "insert"));
4986 }
4987
4988 static void
4989 gtk_text_view_paste_clipboard (GtkTextView *text_view)
4990 {
4991   GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
4992                                                       GDK_SELECTION_CLIPBOARD);
4993   
4994   gtk_text_buffer_paste_clipboard (get_buffer (text_view),
4995                                    clipboard,
4996                                    NULL,
4997                                    text_view->editable);
4998   DV(g_print (G_STRLOC": scrolling onscreen\n"));
4999   gtk_text_view_scroll_mark_onscreen (text_view,
5000                                       gtk_text_buffer_get_mark (get_buffer (text_view),
5001                                                                 "insert"));
5002 }
5003
5004 static void
5005 gtk_text_view_toggle_overwrite (GtkTextView *text_view)
5006 {
5007   text_view->overwrite_mode = !text_view->overwrite_mode;
5008 }
5009
5010 static void
5011 gtk_text_view_move_focus (GtkTextView     *text_view,
5012                           GtkDirectionType direction_type)
5013 {
5014   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (text_view));
5015
5016   if (!GTK_WIDGET_TOPLEVEL (toplevel))
5017     return;
5018
5019   /* Propagate to toplevel */
5020   g_signal_emit_by_name (toplevel, "move_focus", direction_type);
5021 }
5022
5023 /*
5024  * Selections
5025  */
5026
5027 static void
5028 gtk_text_view_unselect (GtkTextView *text_view)
5029 {
5030   GtkTextIter insert;
5031
5032   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
5033                                     &insert,
5034                                     gtk_text_buffer_get_mark (get_buffer (text_view),
5035                                                               "insert"));
5036
5037   gtk_text_buffer_move_mark (get_buffer (text_view),
5038                              gtk_text_buffer_get_mark (get_buffer (text_view),
5039                                                        "selection_bound"),
5040                              &insert);
5041 }
5042
5043 static void
5044 move_mark_to_pointer_and_scroll (GtkTextView *text_view,
5045                                  const gchar *mark_name)
5046 {
5047   gint x, y;
5048   GdkModifierType state;
5049   GtkTextIter newplace;
5050
5051   /*   DV(g_print (G_STRLOC": begin\n")); */
5052   
5053   gdk_window_get_pointer (text_view->text_window->bin_window,
5054                           &x, &y, &state);
5055
5056   /*   DV(g_print (G_STRLOC": get iter at pixel\n"); */
5057   gtk_text_layout_get_iter_at_pixel (text_view->layout,
5058                                      &newplace,
5059                                      x + text_view->xoffset,
5060                                      y + text_view->yoffset);
5061
5062   {
5063     GtkTextMark *mark =
5064       gtk_text_buffer_get_mark (get_buffer (text_view), mark_name);
5065
5066     /* This may invalidate the layout */
5067     DV(g_print (G_STRLOC": move mark\n"));
5068     gtk_text_buffer_move_mark (get_buffer (text_view),
5069                                mark,
5070                                &newplace);
5071
5072     DV(g_print (G_STRLOC": scrolling onscreen\n"));
5073     gtk_text_view_scroll_mark_onscreen (text_view, mark);
5074   }
5075
5076   DV (g_print ("first validate idle leaving %s is %d\n",
5077                G_STRLOC, text_view->first_validate_idle));
5078 }
5079
5080 static gint
5081 selection_scan_timeout (gpointer data)
5082 {
5083   GtkTextView *text_view;
5084
5085   GDK_THREADS_ENTER ();
5086   
5087   text_view = GTK_TEXT_VIEW (data);
5088
5089   DV(g_print (G_STRLOC": calling move_mark_to_pointer_and_scroll\n"));
5090   gtk_text_view_scroll_mark_onscreen (text_view, 
5091                                       gtk_text_buffer_get_mark (get_buffer (text_view),
5092                                                                 "insert"));
5093
5094   GDK_THREADS_LEAVE ();
5095   
5096   return TRUE; /* remain installed. */
5097 }
5098
5099 #define DND_SCROLL_MARGIN 0.20
5100
5101 static gint
5102 drag_scan_timeout (gpointer data)
5103 {
5104   GtkTextView *text_view;
5105   gint x, y;
5106   GdkModifierType state;
5107   GtkTextIter newplace;
5108
5109   GDK_THREADS_ENTER ();
5110   
5111   text_view = GTK_TEXT_VIEW (data);
5112
5113   gdk_window_get_pointer (text_view->text_window->bin_window,
5114                           &x, &y, &state);
5115   
5116   gtk_text_layout_get_iter_at_pixel (text_view->layout,
5117                                      &newplace,
5118                                      x + text_view->xoffset,
5119                                      y + text_view->yoffset);
5120   
5121   gtk_text_buffer_move_mark (get_buffer (text_view),
5122                              text_view->dnd_mark,
5123                              &newplace);
5124
5125   DV(g_print (G_STRLOC": scrolling onscreen\n"));
5126   gtk_text_view_scroll_to_mark (text_view,
5127                                 text_view->dnd_mark,
5128                                 DND_SCROLL_MARGIN, FALSE, 0.0, 0.0);
5129
5130   GDK_THREADS_LEAVE ();
5131   
5132   return TRUE;
5133 }
5134
5135 typedef enum 
5136 {
5137   SELECT_CHARACTERS,
5138   SELECT_WORDS,
5139   SELECT_LINES
5140 } SelectionGranularity;
5141
5142 /*
5143  * Move @start and @end to the boundaries of the selection unit (indicated by 
5144  * @granularity) which contained @start initially. Return wether @start was
5145  * contained in a selection unit at all (which may not be the case for words).
5146  */
5147 static gboolean 
5148 extend_selection (GtkTextView *text_view, 
5149                   SelectionGranularity granularity, 
5150                   GtkTextIter *start, 
5151                   GtkTextIter *end)
5152 {
5153   gboolean extend = TRUE;
5154
5155   *end = *start;
5156
5157   if (granularity == SELECT_WORDS) 
5158     {
5159       if (gtk_text_iter_inside_word (start))
5160         {
5161           if (!gtk_text_iter_starts_word (start))
5162             gtk_text_iter_backward_visible_word_start (start);
5163           
5164           if (!gtk_text_iter_ends_word (end))
5165             gtk_text_iter_forward_visible_word_end (end);
5166         }
5167       else
5168         extend = FALSE;
5169     }
5170   else if (granularity == SELECT_LINES) 
5171     {
5172       if (gtk_text_view_starts_display_line (text_view, start))
5173         {
5174           /* If on a display line boundary, we assume the user
5175            * clicked off the end of a line and we therefore select
5176            * the line before the boundary.
5177            */
5178           gtk_text_view_backward_display_line_start (text_view, start);
5179         }
5180       else
5181         {
5182           /* start isn't on the start of a line, so we move it to the
5183            * start, and move end to the end unless it's already there.
5184            */
5185           gtk_text_view_backward_display_line_start (text_view, start);
5186           
5187           if (!gtk_text_view_starts_display_line (text_view, end))
5188             gtk_text_view_forward_display_line_end (text_view, end);
5189         }
5190     }
5191   
5192   return extend;
5193 }
5194  
5195 static gint
5196 selection_motion_event_handler (GtkTextView *text_view, GdkEventMotion *event, gpointer data)
5197 {
5198   SelectionGranularity granularity = GPOINTER_TO_INT (data);
5199
5200   if (granularity == SELECT_CHARACTERS) 
5201     {
5202       move_mark_to_pointer_and_scroll (text_view, "insert");
5203     }
5204   else 
5205     {
5206       gint x, y;
5207       GdkModifierType state;
5208       GtkTextIter start, end;
5209       GtkTextIter old_start, old_end;    
5210       GtkTextIter ins, bound;    
5211       GtkTextBuffer *buffer;
5212       
5213       buffer = get_buffer (text_view);
5214
5215       gdk_window_get_pointer (text_view->text_window->bin_window,
5216                               &x, &y, &state);
5217       
5218       gtk_text_layout_get_iter_at_pixel (text_view->layout,
5219                                          &start,
5220                                          event->x + text_view->xoffset,
5221                                          event->y + text_view->yoffset); 
5222       
5223       if (extend_selection (text_view, granularity, &start, &end)) 
5224         {
5225           /* Extend selection */
5226           gtk_text_buffer_get_iter_at_mark (buffer, 
5227                                             &ins, 
5228                                             gtk_text_buffer_get_insert (buffer));
5229           gtk_text_buffer_get_iter_at_mark (buffer, 
5230                                             &bound,
5231                                             gtk_text_buffer_get_selection_bound (buffer));
5232
5233           if (gtk_text_iter_compare (&ins, &bound) < 0) 
5234             {
5235               old_start = ins;
5236               old_end = bound;
5237             }
5238           else
5239             {
5240               old_start = bound;
5241               old_end = ins;
5242             }
5243
5244           if (gtk_text_iter_compare (&start, &old_start) < 0) 
5245             {
5246               /* newly selected unit before the current selection */
5247               ins = start;
5248               bound = old_end;
5249             }
5250           else if (gtk_text_iter_compare (&old_end, &end) < 0)
5251             {
5252               /* newly selected unit after the current selection */
5253               ins = end;
5254               bound = old_start;
5255             }
5256           else if (gtk_text_iter_equal (&ins, &old_start)) 
5257             {
5258               /* newly selected unit inside the current selection 
5259                  at the start */
5260               if (!gtk_text_iter_equal (&ins, &start)) 
5261                 ins = end;
5262             }
5263           else
5264             {
5265               /* newly selected unit inside the current selection 
5266                  at the end */
5267               if (!gtk_text_iter_equal (&ins, &end)) 
5268                   ins = start;
5269             }
5270
5271           gtk_text_buffer_select_range (buffer, &ins, &bound);
5272         }
5273
5274       gtk_text_view_scroll_mark_onscreen (text_view, 
5275                                           gtk_text_buffer_get_mark (buffer,
5276                                                                     "insert"));
5277     }
5278
5279   
5280   /* If we had to scroll offscreen, insert a timeout to do so
5281    * again. Note that in the timeout, even if the mouse doesn't
5282    * move, due to this scroll xoffset/yoffset will have changed
5283    * and we'll need to scroll again.
5284    */
5285   if (text_view->scroll_timeout != 0) /* reset on every motion event */
5286     g_source_remove (text_view->scroll_timeout);
5287   
5288   text_view->scroll_timeout =
5289     g_timeout_add (50, selection_scan_timeout, text_view);
5290
5291   return TRUE;
5292 }
5293
5294 static void
5295 gtk_text_view_start_selection_drag (GtkTextView       *text_view,
5296                                     const GtkTextIter *iter,
5297                                     GdkEventButton    *button)
5298 {
5299   GtkTextIter start, end;
5300   GtkTextBuffer *buffer;
5301   SelectionGranularity granularity;
5302
5303   g_return_if_fail (text_view->selection_drag_handler == 0);
5304
5305   if (button->type == GDK_2BUTTON_PRESS)
5306     granularity = SELECT_WORDS;
5307   else if (button->type == GDK_3BUTTON_PRESS)
5308     granularity = SELECT_LINES;
5309   else 
5310     granularity = SELECT_CHARACTERS;
5311
5312   gtk_grab_add (GTK_WIDGET (text_view));
5313
5314   buffer = get_buffer (text_view);
5315   
5316   start = *iter;
5317   
5318   extend_selection (text_view, granularity, &start, &end);
5319
5320   if (button->state & GDK_SHIFT_MASK)
5321     {
5322       /* Extend selection */
5323       GtkTextIter old_start, old_end;
5324
5325       gtk_text_buffer_get_selection_bounds (buffer, &old_start, &old_end);
5326       
5327       gtk_text_iter_order (&start, &old_start);
5328       gtk_text_iter_order (&old_end, &end);
5329       
5330       /* Now start is the first of the starts, and end is the
5331        * last of the ends
5332        */
5333     }
5334
5335   gtk_text_buffer_select_range (buffer, &end, &start);
5336
5337   text_view->selection_drag_handler = g_signal_connect (text_view,
5338                                                         "motion_notify_event",
5339                                                         G_CALLBACK (selection_motion_event_handler),
5340                                                         GINT_TO_POINTER (granularity));
5341 }
5342
5343 /* returns whether we were really dragging */
5344 static gboolean
5345 gtk_text_view_end_selection_drag (GtkTextView *text_view, GdkEventButton *event)
5346 {
5347   if (text_view->selection_drag_handler == 0)
5348     return FALSE;
5349
5350   g_signal_handler_disconnect (text_view, text_view->selection_drag_handler);
5351   text_view->selection_drag_handler = 0;
5352
5353   if (text_view->scroll_timeout != 0)
5354     {
5355       g_source_remove (text_view->scroll_timeout);
5356       text_view->scroll_timeout = 0;
5357     }
5358
5359   gtk_grab_remove (GTK_WIDGET (text_view));
5360
5361   return TRUE;
5362 }
5363
5364 /*
5365  * Layout utils
5366  */
5367
5368 static void
5369 gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
5370                                          GtkTextAttributes  *values,
5371                                          GtkStyle           *style)
5372 {
5373   values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
5374   values->appearance.fg_color = style->text[GTK_STATE_NORMAL];
5375
5376   if (values->font)
5377     pango_font_description_free (values->font);
5378
5379   values->font = pango_font_description_copy (style->font_desc);
5380 }
5381
5382 static void
5383 gtk_text_view_check_keymap_direction (GtkTextView *text_view)
5384 {
5385   if (text_view->layout)
5386     {
5387       gboolean split_cursor;
5388       GtkTextDirection new_dir;
5389       GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
5390   
5391       g_object_get (settings,
5392                     "gtk-split-cursor", &split_cursor,
5393                     NULL);
5394       if (split_cursor)
5395         {
5396           new_dir = GTK_TEXT_DIR_NONE;
5397         }
5398       else
5399         {
5400           GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (text_view)));
5401           new_dir = (gdk_keymap_get_direction (keymap) == PANGO_DIRECTION_LTR) ?
5402             GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
5403         }
5404       
5405       if (text_view->layout->cursor_direction != new_dir)
5406         gtk_text_layout_set_cursor_direction (text_view->layout, new_dir);
5407     }
5408 }
5409
5410 static void
5411 gtk_text_view_ensure_layout (GtkTextView *text_view)
5412 {
5413   GtkWidget *widget;
5414
5415   widget = GTK_WIDGET (text_view);
5416
5417   if (text_view->layout == NULL)
5418     {
5419       GtkTextAttributes *style;
5420       PangoContext *ltr_context, *rtl_context;
5421       GSList *tmp_list;
5422
5423       DV(g_print(G_STRLOC"\n"));
5424       
5425       text_view->layout = gtk_text_layout_new ();
5426
5427       g_signal_connect (text_view->layout,
5428                         "invalidated",
5429                         G_CALLBACK (invalidated_handler),
5430                         text_view);
5431
5432       g_signal_connect (text_view->layout,
5433                         "changed",
5434                         G_CALLBACK (changed_handler),
5435                         text_view);
5436
5437       g_signal_connect (text_view->layout,
5438                         "allocate_child",
5439                         G_CALLBACK (gtk_text_view_child_allocated),
5440                         text_view);
5441       
5442       if (get_buffer (text_view))
5443         gtk_text_layout_set_buffer (text_view->layout, get_buffer (text_view));
5444
5445       if ((GTK_WIDGET_HAS_FOCUS (text_view) && text_view->cursor_visible))
5446         gtk_text_view_pend_cursor_blink (text_view);
5447       else
5448         gtk_text_layout_set_cursor_visible (text_view->layout, FALSE);
5449
5450       ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
5451       pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
5452       rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
5453       pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
5454
5455       gtk_text_layout_set_contexts (text_view->layout, ltr_context, rtl_context);
5456
5457       g_object_unref (ltr_context);
5458       g_object_unref (rtl_context);
5459
5460       gtk_text_view_check_keymap_direction (text_view);
5461
5462       style = gtk_text_attributes_new ();
5463
5464       gtk_widget_ensure_style (widget);
5465       gtk_text_view_set_attributes_from_style (text_view,
5466                                                style, widget->style);
5467
5468       style->pixels_above_lines = text_view->pixels_above_lines;
5469       style->pixels_below_lines = text_view->pixels_below_lines;
5470       style->pixels_inside_wrap = text_view->pixels_inside_wrap;
5471       style->left_margin = text_view->left_margin;
5472       style->right_margin = text_view->right_margin;
5473       style->indent = text_view->indent;
5474       style->tabs = text_view->tabs ? pango_tab_array_copy (text_view->tabs) : NULL;
5475
5476       style->wrap_mode = text_view->wrap_mode;
5477       style->justification = text_view->justify;
5478       style->direction = gtk_widget_get_direction (GTK_WIDGET (text_view));
5479
5480       gtk_text_layout_set_default_style (text_view->layout, style);
5481
5482       gtk_text_attributes_unref (style);
5483
5484       /* Set layout for all anchored children */
5485
5486       tmp_list = text_view->children;
5487       while (tmp_list != NULL)
5488         {
5489           GtkTextViewChild *vc = tmp_list->data;
5490
5491           if (vc->anchor)
5492             {
5493               gtk_text_anchored_child_set_layout (vc->widget,
5494                                                   text_view->layout);
5495               /* vc may now be invalid! */
5496             }
5497
5498           tmp_list = g_slist_next (tmp_list);
5499         }
5500
5501       gtk_text_view_invalidate (text_view);
5502     }
5503 }
5504
5505 /**
5506  * gtk_text_view_get_default_attributes:
5507  * @text_view: a #GtkTextView
5508  * 
5509  * Obtains a copy of the default text attributes. These are the
5510  * attributes used for text unless a tag overrides them.
5511  * You'd typically pass the default attributes in to
5512  * gtk_text_iter_get_attributes() in order to get the
5513  * attributes in effect at a given text position.
5514  *
5515  * The return value is a copy owned by the caller of this function,
5516  * and should be freed.
5517  * 
5518  * Return value: a new #GtkTextAttributes
5519  **/
5520 GtkTextAttributes*
5521 gtk_text_view_get_default_attributes (GtkTextView *text_view)
5522 {
5523   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
5524   
5525   gtk_text_view_ensure_layout (text_view);
5526
5527   return gtk_text_attributes_copy (text_view->layout->default_style);
5528 }
5529
5530 static void
5531 gtk_text_view_destroy_layout (GtkTextView *text_view)
5532 {
5533   if (text_view->layout)
5534     {
5535       GSList *tmp_list;
5536
5537       gtk_text_view_remove_validate_idles (text_view);
5538
5539       g_signal_handlers_disconnect_by_func (text_view->layout,
5540                                             invalidated_handler,
5541                                             text_view);
5542       g_signal_handlers_disconnect_by_func (text_view->layout,
5543                                             changed_handler, 
5544                                             text_view);
5545       
5546       /* Remove layout from all anchored children */
5547       tmp_list = text_view->children;
5548       while (tmp_list != NULL)
5549         {
5550           GtkTextViewChild *vc = tmp_list->data;
5551
5552           if (vc->anchor)
5553             {
5554               gtk_text_anchored_child_set_layout (vc->widget, NULL);
5555               /* vc may now be invalid! */
5556             }
5557
5558           tmp_list = g_slist_next (tmp_list);
5559         }
5560       
5561       gtk_text_view_stop_cursor_blink (text_view);
5562       gtk_text_view_end_selection_drag (text_view, NULL);
5563
5564       g_object_unref (text_view->layout);
5565       text_view->layout = NULL;
5566     }
5567 }
5568
5569 static void
5570 gtk_text_view_reset_im_context (GtkTextView *text_view)
5571 {
5572   if (text_view->need_im_reset)
5573     {
5574       text_view->need_im_reset = FALSE;
5575       gtk_im_context_reset (text_view->im_context);
5576     }
5577 }
5578
5579 /*
5580  * DND feature
5581  */
5582
5583 static void
5584 gtk_text_view_start_selection_dnd (GtkTextView       *text_view,
5585                                    const GtkTextIter *iter,
5586                                    GdkEventMotion    *event)
5587 {
5588   GdkDragContext *context;
5589   GtkTargetList *target_list;
5590
5591   text_view->drag_start_x = -1;
5592   text_view->drag_start_y = -1;
5593   text_view->pending_place_cursor_button = 0;
5594   
5595   target_list = gtk_target_list_new (target_table,
5596                                      G_N_ELEMENTS (target_table));
5597
5598   context = gtk_drag_begin (GTK_WIDGET (text_view), target_list,
5599                             GDK_ACTION_COPY | GDK_ACTION_MOVE,
5600                             1, (GdkEvent*)event);
5601
5602   gtk_target_list_unref (target_list);
5603
5604   gtk_drag_set_icon_default (context);
5605 }
5606
5607 static void
5608 gtk_text_view_drag_begin (GtkWidget        *widget,
5609                           GdkDragContext   *context)
5610 {
5611   /* do nothing */
5612 }
5613
5614 static void
5615 gtk_text_view_drag_end (GtkWidget        *widget,
5616                         GdkDragContext   *context)
5617 {
5618   GtkTextView *text_view;
5619
5620   text_view = GTK_TEXT_VIEW (widget);
5621 }
5622
5623 static void
5624 gtk_text_view_drag_data_get (GtkWidget        *widget,
5625                              GdkDragContext   *context,
5626                              GtkSelectionData *selection_data,
5627                              guint             info,
5628                              guint             time)
5629 {
5630   GtkTextView *text_view;
5631
5632   text_view = GTK_TEXT_VIEW (widget);
5633
5634   if (selection_data->target == gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE))
5635     {
5636       GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
5637
5638       gtk_selection_data_set (selection_data,
5639                               gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE),
5640                               8, /* bytes */
5641                               (void*)&buffer,
5642                               sizeof (buffer));
5643     }
5644   else
5645     {
5646       gchar *str;
5647       GtkTextIter start;
5648       GtkTextIter end;
5649
5650       str = NULL;
5651
5652       if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
5653                                                 &start, &end))
5654         {
5655           /* Extract the selected text */
5656           str = gtk_text_iter_get_visible_text (&start, &end);
5657         }
5658
5659       if (str)
5660         {
5661           gtk_selection_data_set_text (selection_data, str, -1);
5662           g_free (str);
5663         }
5664     }
5665 }
5666
5667 static void
5668 gtk_text_view_drag_data_delete (GtkWidget        *widget,
5669                                 GdkDragContext   *context)
5670 {
5671   GtkTextView *text_view;
5672
5673   text_view = GTK_TEXT_VIEW (widget);
5674
5675   gtk_text_buffer_delete_selection (GTK_TEXT_VIEW (widget)->buffer,
5676                                     TRUE, GTK_TEXT_VIEW (widget)->editable);
5677 }
5678
5679 static void
5680 gtk_text_view_drag_leave (GtkWidget        *widget,
5681                           GdkDragContext   *context,
5682                           guint             time)
5683 {
5684   GtkTextView *text_view;
5685
5686   text_view = GTK_TEXT_VIEW (widget);
5687
5688   gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
5689   
5690   if (text_view->scroll_timeout != 0)
5691     g_source_remove (text_view->scroll_timeout);
5692
5693   text_view->scroll_timeout = 0;
5694 }
5695
5696 static gboolean
5697 gtk_text_view_drag_motion (GtkWidget        *widget,
5698                            GdkDragContext   *context,
5699                            gint              x,
5700                            gint              y,
5701                            guint             time)
5702 {
5703   GtkTextIter newplace;
5704   GtkTextView *text_view;
5705   GtkTextIter start;
5706   GtkTextIter end;
5707   GdkRectangle target_rect;
5708   gint bx, by;
5709   GdkDragAction suggested_action = 0;
5710   
5711   text_view = GTK_TEXT_VIEW (widget);
5712
5713   target_rect = text_view->text_window->allocation;
5714   
5715   if (x < target_rect.x ||
5716       y < target_rect.y ||
5717       x > (target_rect.x + target_rect.width) ||
5718       y > (target_rect.y + target_rect.height))
5719     return FALSE; /* outside the text window, allow parent widgets to handle event */
5720
5721   gtk_text_view_window_to_buffer_coords (text_view,
5722                                          GTK_TEXT_WINDOW_WIDGET,
5723                                          x, y,
5724                                          &bx, &by);
5725
5726   gtk_text_layout_get_iter_at_pixel (text_view->layout,
5727                                      &newplace,
5728                                      bx, by);  
5729
5730   if (gtk_drag_dest_find_target (widget, context,
5731                                  gtk_drag_dest_get_target_list (widget)) == GDK_NONE)
5732     {
5733       /* can't accept any of the offered targets */
5734     }                                 
5735   else if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
5736                                             &start, &end) &&
5737            gtk_text_iter_compare (&newplace, &start) >= 0 &&
5738            gtk_text_iter_compare (&newplace, &end) <= 0)
5739     {
5740       /* We're inside the selection. */
5741     }
5742   else
5743     {      
5744       if (gtk_text_iter_can_insert (&newplace, text_view->editable))
5745         {
5746           GtkWidget *source_widget;
5747           
5748           suggested_action = context->suggested_action;
5749           
5750           source_widget = gtk_drag_get_source_widget (context);
5751           
5752           if (source_widget == widget)
5753             {
5754               /* Default to MOVE, unless the user has
5755                * pressed ctrl or alt to affect available actions
5756                */
5757               if ((context->actions & GDK_ACTION_MOVE) != 0)
5758                 suggested_action = GDK_ACTION_MOVE;
5759             }
5760         }
5761       else
5762         {
5763           /* Can't drop here. */
5764         }
5765     }
5766
5767   if (suggested_action != 0)
5768     {
5769       gtk_text_mark_set_visible (text_view->dnd_mark,
5770                                  text_view->cursor_visible);
5771       
5772       gdk_drag_status (context, suggested_action, time);
5773     }
5774   else
5775     {
5776       gdk_drag_status (context, 0, time);
5777       gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
5778     }
5779       
5780   gtk_text_buffer_move_mark (get_buffer (text_view),
5781                              text_view->dnd_mark,
5782                              &newplace);
5783
5784   DV(g_print (G_STRLOC": scrolling to mark\n"));
5785   gtk_text_view_scroll_to_mark (text_view,
5786                                 text_view->dnd_mark,
5787                                 DND_SCROLL_MARGIN, FALSE, 0.0, 0.0);
5788   
5789   if (text_view->scroll_timeout != 0) /* reset on every motion event */
5790     g_source_remove (text_view->scroll_timeout);
5791       
5792   text_view->scroll_timeout =
5793     g_timeout_add (50, drag_scan_timeout, text_view);
5794
5795   /* TRUE return means don't propagate the drag motion to parent
5796    * widgets that may also be drop sites.
5797    */
5798   return TRUE;
5799 }
5800
5801 static gboolean
5802 gtk_text_view_drag_drop (GtkWidget        *widget,
5803                          GdkDragContext   *context,
5804                          gint              x,
5805                          gint              y,
5806                          guint             time)
5807 {
5808   GtkTextView *text_view;
5809   GtkTextIter drop_point;
5810   GdkAtom target = GDK_NONE;
5811   
5812   text_view = GTK_TEXT_VIEW (widget);
5813   
5814   if (text_view->scroll_timeout != 0)
5815     g_source_remove (text_view->scroll_timeout);
5816
5817   text_view->scroll_timeout = 0;
5818
5819   gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
5820
5821   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
5822                                     &drop_point,
5823                                     text_view->dnd_mark);
5824
5825   if (gtk_text_iter_can_insert (&drop_point, text_view->editable))
5826     target = gtk_drag_dest_find_target (widget, context, NULL);
5827
5828   if (target != GDK_NONE)
5829     gtk_drag_get_data (widget, context, target, time);
5830   else
5831     gtk_drag_finish (context, FALSE, FALSE, time);
5832
5833   return TRUE;
5834 }
5835
5836 static void
5837 insert_text_data (GtkTextView      *text_view,
5838                   GtkTextIter      *drop_point,
5839                   GtkSelectionData *selection_data)
5840 {
5841   gchar *str;
5842
5843   str = gtk_selection_data_get_text (selection_data);
5844
5845   if (str)
5846     {
5847       gtk_text_buffer_insert_interactive (get_buffer (text_view),
5848                                           drop_point, str, -1,
5849                                           text_view->editable);
5850       g_free (str);
5851     }
5852 }
5853
5854 static void
5855 gtk_text_view_drag_data_received (GtkWidget        *widget,
5856                                   GdkDragContext   *context,
5857                                   gint              x,
5858                                   gint              y,
5859                                   GtkSelectionData *selection_data,
5860                                   guint             info,
5861                                   guint             time)
5862 {
5863   GtkTextIter drop_point;
5864   GtkTextView *text_view;
5865   gboolean success = FALSE;
5866
5867   text_view = GTK_TEXT_VIEW (widget);
5868
5869   if (!text_view->dnd_mark)
5870     goto done;
5871
5872   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
5873                                     &drop_point,
5874                                     text_view->dnd_mark);
5875   
5876   if (!gtk_text_iter_can_insert (&drop_point, text_view->editable))
5877     goto done;
5878
5879   if (selection_data->target == gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE))
5880     {
5881       GtkTextBuffer *src_buffer = NULL;
5882       GtkTextIter start, end;
5883       gboolean copy_tags = TRUE;
5884
5885       if (selection_data->length != sizeof (src_buffer))
5886         return;
5887
5888       memcpy (&src_buffer, selection_data->data, sizeof (src_buffer));
5889
5890       if (src_buffer == NULL)
5891         return;
5892
5893       g_return_if_fail (GTK_IS_TEXT_BUFFER (src_buffer));
5894
5895       if (gtk_text_buffer_get_tag_table (src_buffer) !=
5896           gtk_text_buffer_get_tag_table (get_buffer (text_view)))
5897         copy_tags = FALSE;
5898
5899       if (gtk_text_buffer_get_selection_bounds (src_buffer,
5900                                                 &start,
5901                                                 &end))
5902         {
5903           if (copy_tags)
5904             gtk_text_buffer_insert_range_interactive (get_buffer (text_view),
5905                                                       &drop_point,
5906                                                       &start,
5907                                                       &end,
5908                                                       text_view->editable);
5909           else
5910             {
5911               gchar *str;
5912
5913               str = gtk_text_iter_get_visible_text (&start, &end);
5914               gtk_text_buffer_insert_interactive (get_buffer (text_view),
5915                                                   &drop_point, str, -1,
5916                                                   text_view->editable);
5917               g_free (str);
5918             }
5919         }
5920     }
5921   else
5922     insert_text_data (text_view, &drop_point, selection_data);
5923
5924   gtk_text_buffer_place_cursor (get_buffer (text_view), &drop_point);
5925
5926   success = TRUE;
5927
5928  done:
5929   gtk_drag_finish (context, success,
5930                    success && context->action == GDK_ACTION_MOVE,
5931                    time);
5932 }
5933
5934 static GtkAdjustment*
5935 get_hadjustment (GtkTextView *text_view)
5936 {
5937   if (text_view->hadjustment == NULL)
5938     gtk_text_view_set_scroll_adjustments (text_view,
5939                                           NULL, /* forces creation */
5940                                           text_view->vadjustment);
5941
5942   return text_view->hadjustment;
5943 }
5944
5945 static GtkAdjustment*
5946 get_vadjustment (GtkTextView *text_view)
5947 {
5948   if (text_view->vadjustment == NULL)
5949     gtk_text_view_set_scroll_adjustments (text_view,
5950                                           text_view->hadjustment,
5951                                           NULL); /* forces creation */
5952   return text_view->vadjustment;
5953 }
5954
5955
5956 static void
5957 gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
5958                                       GtkAdjustment *hadj,
5959                                       GtkAdjustment *vadj)
5960 {
5961   gboolean need_adjust = FALSE;
5962
5963   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
5964
5965   if (hadj)
5966     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
5967   else
5968     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
5969   if (vadj)
5970     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
5971   else
5972     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
5973
5974   if (text_view->hadjustment && (text_view->hadjustment != hadj))
5975     {
5976       g_signal_handlers_disconnect_by_func (text_view->hadjustment,
5977                                             gtk_text_view_value_changed,
5978                                             text_view);
5979       g_object_unref (text_view->hadjustment);
5980     }
5981
5982   if (text_view->vadjustment && (text_view->vadjustment != vadj))
5983     {
5984       g_signal_handlers_disconnect_by_func (text_view->vadjustment,
5985                                             gtk_text_view_value_changed,
5986                                             text_view);
5987       g_object_unref (text_view->vadjustment);
5988     }
5989
5990   if (text_view->hadjustment != hadj)
5991     {
5992       text_view->hadjustment = hadj;
5993       g_object_ref (text_view->hadjustment);
5994       gtk_object_sink (GTK_OBJECT (text_view->hadjustment));
5995       
5996       g_signal_connect (text_view->hadjustment, "value_changed",
5997                         G_CALLBACK (gtk_text_view_value_changed),
5998                         text_view);
5999       need_adjust = TRUE;
6000     }
6001
6002   if (text_view->vadjustment != vadj)
6003     {
6004       text_view->vadjustment = vadj;
6005       g_object_ref (text_view->vadjustment);
6006       gtk_object_sink (GTK_OBJECT (text_view->vadjustment));
6007       
6008       g_signal_connect (text_view->vadjustment, "value_changed",
6009                         G_CALLBACK (gtk_text_view_value_changed),
6010                         text_view);
6011       need_adjust = TRUE;
6012     }
6013
6014   if (need_adjust)
6015     gtk_text_view_value_changed (NULL, text_view);
6016 }
6017
6018 /* FIXME this adjust_allocation is a big cut-and-paste from
6019  * GtkCList, needs to be some "official" way to do this
6020  * factored out.
6021  */
6022 typedef struct
6023 {
6024   GdkWindow *window;
6025   int dx;
6026   int dy;
6027 } ScrollData;
6028
6029 /* The window to which widget->window is relative */
6030 #define ALLOCATION_WINDOW(widget)               \
6031    (GTK_WIDGET_NO_WINDOW (widget) ?             \
6032     (widget)->window :                          \
6033      gdk_window_get_parent ((widget)->window))
6034
6035 static void
6036 adjust_allocation_recurse (GtkWidget *widget,
6037                            gpointer   data)
6038 {
6039   ScrollData *scroll_data = data;
6040
6041   /* Need to really size allocate instead of just poking
6042    * into widget->allocation if the widget is not realized.
6043    * FIXME someone figure out why this was.
6044    */
6045   if (!GTK_WIDGET_REALIZED (widget))
6046     {
6047       if (GTK_WIDGET_VISIBLE (widget))
6048         {
6049           GdkRectangle tmp_rectangle = widget->allocation;
6050           tmp_rectangle.x += scroll_data->dx;
6051           tmp_rectangle.y += scroll_data->dy;
6052           
6053           gtk_widget_size_allocate (widget, &tmp_rectangle);
6054         }
6055     }
6056   else
6057     {
6058       if (ALLOCATION_WINDOW (widget) == scroll_data->window)
6059         {
6060           widget->allocation.x += scroll_data->dx;
6061           widget->allocation.y += scroll_data->dy;
6062           
6063           if (GTK_IS_CONTAINER (widget))
6064             gtk_container_forall (GTK_CONTAINER (widget),
6065                                   adjust_allocation_recurse,
6066                                   data);
6067         }
6068     }
6069 }
6070
6071 static void
6072 adjust_allocation (GtkWidget *widget,
6073                    int        dx,
6074                    int        dy)
6075 {
6076   ScrollData scroll_data;
6077
6078   if (GTK_WIDGET_REALIZED (widget))
6079     scroll_data.window = ALLOCATION_WINDOW (widget);
6080   else
6081     scroll_data.window = NULL;
6082     
6083   scroll_data.dx = dx;
6084   scroll_data.dy = dy;
6085   
6086   adjust_allocation_recurse (widget, &scroll_data);
6087 }
6088             
6089 static void
6090 gtk_text_view_value_changed (GtkAdjustment *adj,
6091                              GtkTextView   *text_view)
6092 {
6093   GtkTextIter iter;
6094   gint line_top;
6095   gint dx = 0;
6096   gint dy = 0;
6097   
6098   /* Note that we oddly call this function with adj == NULL
6099    * sometimes
6100    */
6101   
6102   text_view->onscreen_validated = FALSE;
6103
6104   DV(g_print(">Scroll offset changed %s/%g, onscreen_validated = FALSE ("G_STRLOC")\n",
6105              adj == text_view->hadjustment ? "hadj" : adj == text_view->vadjustment ? "vadj" : "none",
6106              adj ? adj->value : 0.0));
6107   
6108   if (adj == text_view->hadjustment)
6109     {
6110       dx = text_view->xoffset - (gint)adj->value;
6111       text_view->xoffset = adj->value;
6112     }
6113   else if (adj == text_view->vadjustment)
6114     {
6115       dy = text_view->yoffset - (gint)adj->value;
6116       text_view->yoffset = adj->value;
6117
6118       if (text_view->layout)
6119         {
6120           gtk_text_layout_get_line_at_y (text_view->layout, &iter, adj->value, &line_top);
6121
6122           gtk_text_buffer_move_mark (get_buffer (text_view), text_view->first_para_mark, &iter);
6123
6124           text_view->first_para_pixels = adj->value - line_top;
6125         }
6126     }
6127   
6128   if (dx != 0 || dy != 0)
6129     {
6130       GSList *tmp_list;
6131
6132       if (GTK_WIDGET_REALIZED (text_view))
6133         {
6134           if (dy != 0)
6135             {
6136               if (text_view->left_window)
6137                 text_window_scroll (text_view->left_window, 0, dy);
6138               if (text_view->right_window)
6139                 text_window_scroll (text_view->right_window, 0, dy);
6140             }
6141       
6142           if (dx != 0)
6143             {
6144               if (text_view->top_window)
6145                 text_window_scroll (text_view->top_window, dx, 0);
6146               if (text_view->bottom_window)
6147                 text_window_scroll (text_view->bottom_window, dx, 0);
6148             }
6149       
6150           /* It looks nicer to scroll the main area last, because
6151            * it takes a while, and making the side areas update
6152            * afterward emphasizes the slowness of scrolling the
6153            * main area.
6154            */
6155           text_window_scroll (text_view->text_window, dx, dy);
6156         }
6157       
6158       /* Children are now "moved" in the text window, poke
6159        * into widget->allocation for each child
6160        */
6161       tmp_list = text_view->children;
6162       while (tmp_list != NULL)
6163         {
6164           GtkTextViewChild *child = tmp_list->data;
6165           
6166           if (child->anchor)
6167             adjust_allocation (child->widget, dx, dy);
6168           
6169           tmp_list = g_slist_next (tmp_list);
6170         }
6171     }
6172
6173   /* This could result in invalidation, which would install the
6174    * first_validate_idle, which would validate onscreen;
6175    * but we're going to go ahead and validate here, so
6176    * first_validate_idle shouldn't have anything to do.
6177    */
6178   gtk_text_view_update_layout_width (text_view);
6179   
6180   /* note that validation of onscreen could invoke this function
6181    * recursively, by scrolling to maintain first_para, or in response
6182    * to updating the layout width, however there is no problem with
6183    * that, or shouldn't be.
6184    */
6185   gtk_text_view_validate_onscreen (text_view);
6186   
6187   /* process exposes */
6188   if (GTK_WIDGET_REALIZED (text_view))
6189     {
6190       DV (g_print ("Processing updates (%s)\n", G_STRLOC));
6191       
6192       if (text_view->left_window)
6193         gdk_window_process_updates (text_view->left_window->bin_window, TRUE);
6194
6195       if (text_view->right_window)
6196         gdk_window_process_updates (text_view->right_window->bin_window, TRUE);
6197
6198       if (text_view->top_window)
6199         gdk_window_process_updates (text_view->top_window->bin_window, TRUE);
6200       
6201       if (text_view->bottom_window)
6202         gdk_window_process_updates (text_view->bottom_window->bin_window, TRUE);
6203   
6204       gdk_window_process_updates (text_view->text_window->bin_window, TRUE);
6205     }
6206
6207   /* If this got installed, get rid of it, it's just a waste of time. */
6208   if (text_view->first_validate_idle != 0)
6209     {
6210       g_source_remove (text_view->first_validate_idle);
6211       text_view->first_validate_idle = 0;
6212     }
6213
6214   gtk_text_view_update_im_spot_location (text_view);
6215   
6216   DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n"));
6217 }
6218
6219 static void
6220 gtk_text_view_commit_handler (GtkIMContext  *context,
6221                               const gchar   *str,
6222                               GtkTextView   *text_view)
6223 {
6224   gtk_text_view_commit_text (text_view, str);
6225 }
6226
6227 static void
6228 gtk_text_view_commit_text (GtkTextView   *text_view,
6229                            const gchar   *str)
6230 {
6231   gboolean had_selection;
6232   
6233   gtk_text_buffer_begin_user_action (get_buffer (text_view));
6234
6235   had_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
6236                                                         NULL, NULL);
6237   
6238   gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
6239                                     text_view->editable);
6240
6241   if (!strcmp (str, "\n"))
6242     {
6243       gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
6244                                                     text_view->editable);
6245     }
6246   else
6247     {
6248       if (!had_selection && text_view->overwrite_mode)
6249         {
6250           GtkTextIter insert;
6251           
6252           gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
6253                                             &insert,
6254                                             gtk_text_buffer_get_mark (get_buffer (text_view),
6255                                                                       "insert"));
6256           if (!gtk_text_iter_ends_line (&insert))
6257             gtk_text_view_delete_from_cursor (text_view, GTK_DELETE_CHARS, 1);
6258         }
6259       gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
6260                                                     text_view->editable);
6261     }
6262
6263   gtk_text_buffer_end_user_action (get_buffer (text_view));
6264
6265   DV(g_print (G_STRLOC": scrolling onscreen\n"));
6266   gtk_text_view_scroll_mark_onscreen (text_view,
6267                                       gtk_text_buffer_get_mark (get_buffer (text_view),
6268                                                                 "insert"));
6269 }
6270
6271 static void
6272 gtk_text_view_preedit_changed_handler (GtkIMContext *context,
6273                                        GtkTextView  *text_view)
6274 {
6275   gchar *str;
6276   PangoAttrList *attrs;
6277   gint cursor_pos;
6278
6279   gtk_im_context_get_preedit_string (context, &str, &attrs, &cursor_pos);
6280   gtk_text_layout_set_preedit_string (text_view->layout, str, attrs, cursor_pos);
6281   pango_attr_list_unref (attrs);
6282   g_free (str);
6283
6284   gtk_text_view_scroll_mark_onscreen (text_view,
6285                                       gtk_text_buffer_get_mark (get_buffer (text_view),
6286                                                                 "insert"));
6287 }
6288
6289 static gboolean
6290 gtk_text_view_retrieve_surrounding_handler (GtkIMContext  *context,
6291                                             GtkTextView   *text_view)
6292 {
6293   GtkTextIter start;
6294   GtkTextIter end;
6295   gint pos;
6296   gchar *text;
6297
6298   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &start,  
6299                                     gtk_text_buffer_get_insert (text_view->buffer));
6300   end = start;
6301
6302   pos = gtk_text_iter_get_line_index (&start);
6303   gtk_text_iter_set_line_offset (&start, 0);
6304   gtk_text_iter_forward_to_line_end (&end);
6305
6306   text = gtk_text_iter_get_slice (&start, &end);
6307   gtk_im_context_set_surrounding (context, text, -1, pos);
6308   g_free (text);
6309
6310   return TRUE;
6311 }
6312
6313 static gboolean
6314 gtk_text_view_delete_surrounding_handler (GtkIMContext  *context,
6315                                           gint           offset,
6316                                           gint           n_chars,
6317                                           GtkTextView   *text_view)
6318 {
6319   GtkTextIter start;
6320   GtkTextIter end;
6321
6322   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &start,  
6323                                     gtk_text_buffer_get_insert (text_view->buffer));
6324   end = start;
6325
6326   gtk_text_iter_forward_chars (&start, offset);
6327   gtk_text_iter_forward_chars (&end, offset + n_chars);
6328
6329   gtk_text_buffer_delete (text_view->buffer, &start, &end);
6330
6331   return TRUE;
6332 }
6333
6334 static void
6335 gtk_text_view_mark_set_handler (GtkTextBuffer     *buffer,
6336                                 const GtkTextIter *location,
6337                                 GtkTextMark       *mark,
6338                                 gpointer           data)
6339 {
6340   GtkTextView *text_view = GTK_TEXT_VIEW (data);
6341   gboolean need_reset = FALSE;
6342
6343   if (mark == gtk_text_buffer_get_insert (buffer))
6344     {
6345       text_view->virtual_cursor_x = -1;
6346       text_view->virtual_cursor_y = -1;
6347       gtk_text_view_update_im_spot_location (text_view);
6348       need_reset = TRUE;
6349     }
6350   else if (mark == gtk_text_buffer_get_selection_bound (buffer))
6351     {
6352       need_reset = TRUE;
6353     }
6354
6355   if (need_reset)
6356     gtk_text_view_reset_im_context (text_view);
6357 }
6358
6359 static void
6360 gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
6361                                       gint        *x,
6362                                       gint        *y)
6363 {
6364   GdkRectangle strong_pos;
6365   GtkTextIter insert;
6366
6367   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
6368                                     gtk_text_buffer_get_mark (get_buffer (text_view),
6369                                                               "insert"));
6370
6371   if ((x && text_view->virtual_cursor_x == -1) ||
6372       (y && text_view->virtual_cursor_y == -1))
6373     gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
6374
6375   if (x)
6376     {
6377       if (text_view->virtual_cursor_x != -1)
6378         *x = text_view->virtual_cursor_x;
6379       else
6380         *x = strong_pos.x;
6381     }
6382
6383   if (y)
6384     {
6385       if (text_view->virtual_cursor_x != -1)
6386         *y = text_view->virtual_cursor_y;
6387       else
6388         *y = strong_pos.y + strong_pos.height / 2;
6389     }
6390 }
6391
6392 static void
6393 gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
6394                                       gint         x,
6395                                       gint         y)
6396 {
6397   GdkRectangle strong_pos;
6398   GtkTextIter insert;
6399
6400   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
6401                                     gtk_text_buffer_get_mark (get_buffer (text_view),
6402                                                               "insert"));
6403
6404   if (x == -1 || y == -1)
6405     gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
6406
6407   text_view->virtual_cursor_x = (x == -1) ? strong_pos.x : x;
6408   text_view->virtual_cursor_y = (y == -1) ? strong_pos.y + strong_pos.height / 2 : y;
6409 }
6410
6411 /* Quick hack of a popup menu
6412  */
6413 static void
6414 activate_cb (GtkWidget   *menuitem,
6415              GtkTextView *text_view)
6416 {
6417   const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
6418   g_signal_emit_by_name (text_view, signal);
6419 }
6420
6421 static void
6422 append_action_signal (GtkTextView  *text_view,
6423                       GtkWidget    *menu,
6424                       const gchar  *stock_id,
6425                       const gchar  *signal,
6426                       gboolean      sensitive)
6427 {
6428   GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
6429
6430   g_object_set_data (G_OBJECT (menuitem), "gtk-signal", (char *)signal);
6431   g_signal_connect (menuitem, "activate",
6432                     G_CALLBACK (activate_cb), text_view);
6433
6434   gtk_widget_set_sensitive (menuitem, sensitive);
6435   
6436   gtk_widget_show (menuitem);
6437   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
6438 }
6439
6440 static void
6441 gtk_text_view_select_all (GtkWidget *widget,
6442                           gboolean select)
6443 {
6444   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
6445   GtkTextBuffer *buffer;
6446   GtkTextIter start_iter, end_iter, insert;
6447
6448   buffer = text_view->buffer;
6449   if (select) 
6450     {
6451       gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
6452       gtk_text_buffer_move_mark_by_name (buffer, "insert", &start_iter);
6453       gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &end_iter);
6454     }
6455   else 
6456     {
6457       gtk_text_buffer_get_iter_at_mark (buffer, &insert,
6458                                         gtk_text_buffer_get_insert (buffer));
6459       gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &insert);
6460     }
6461 }
6462
6463 static void
6464 select_all_cb (GtkWidget   *menuitem,
6465                GtkTextView *text_view)
6466 {
6467   gtk_text_view_select_all (GTK_WIDGET (text_view), TRUE);
6468 }
6469
6470 static void
6471 delete_cb (GtkTextView *text_view)
6472 {
6473   gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
6474                                     text_view->editable);
6475 }
6476
6477 static void
6478 popup_menu_detach (GtkWidget *attach_widget,
6479                    GtkMenu   *menu)
6480 {
6481   GTK_TEXT_VIEW (attach_widget)->popup_menu = NULL;
6482 }
6483
6484 static void
6485 popup_position_func (GtkMenu   *menu,
6486                      gint      *x,
6487                      gint      *y,
6488                      gboolean  *push_in,
6489                      gpointer   user_data)
6490 {
6491   GtkTextView *text_view;
6492   GtkWidget *widget;
6493   GdkRectangle cursor_rect;
6494   GdkRectangle onscreen_rect;
6495   gint root_x, root_y;
6496   GtkTextIter iter;
6497   GtkRequisition req;      
6498   GdkScreen *screen;
6499   
6500   text_view = GTK_TEXT_VIEW (user_data);
6501   widget = GTK_WIDGET (text_view);
6502   
6503   g_return_if_fail (GTK_WIDGET_REALIZED (text_view));
6504   
6505   screen = gtk_widget_get_screen (widget);
6506
6507   gdk_window_get_origin (widget->window, &root_x, &root_y);
6508
6509   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
6510                                     &iter,
6511                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
6512
6513   gtk_text_view_get_iter_location (text_view,
6514                                    &iter,
6515                                    &cursor_rect);
6516
6517   gtk_text_view_get_visible_rect (text_view, &onscreen_rect);
6518   
6519   gtk_widget_size_request (text_view->popup_menu, &req);
6520
6521   /* can't use rectangle_intersect since cursor rect can have 0 width */
6522   if (cursor_rect.x >= onscreen_rect.x &&
6523       cursor_rect.x < onscreen_rect.x + onscreen_rect.width &&
6524       cursor_rect.y >= onscreen_rect.y &&
6525       cursor_rect.y < onscreen_rect.y + onscreen_rect.height)
6526     {    
6527       gtk_text_view_buffer_to_window_coords (text_view,
6528                                              GTK_TEXT_WINDOW_WIDGET,
6529                                              cursor_rect.x, cursor_rect.y,
6530                                              &cursor_rect.x, &cursor_rect.y);
6531
6532       *x = root_x + cursor_rect.x + cursor_rect.width;
6533       *y = root_y + cursor_rect.y + cursor_rect.height;
6534     }
6535   else
6536     {
6537       /* Just center the menu, since cursor is offscreen. */      
6538       *x = root_x + (widget->allocation.width / 2 - req.width / 2);
6539       *y = root_y + (widget->allocation.height / 2 - req.height / 2);      
6540     }
6541
6542   /* Ensure sanity */
6543   *x = CLAMP (*x, root_x, (root_x + widget->allocation.width));
6544   *y = CLAMP (*y, root_y, (root_y + widget->allocation.height));
6545
6546   *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
6547   *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
6548 }
6549
6550 typedef struct
6551 {
6552   GtkTextView *text_view;
6553   gint button;
6554   guint time;
6555 } PopupInfo;
6556
6557 static gboolean
6558 range_contains_editable_text (const GtkTextIter *start,
6559                               const GtkTextIter *end,
6560                               gboolean default_editability)
6561 {
6562   GtkTextIter iter = *start;
6563
6564   while (gtk_text_iter_compare (&iter, end) < 0)
6565     {
6566       if (gtk_text_iter_editable (&iter, default_editability))
6567         return TRUE;
6568       
6569       gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
6570     }
6571
6572   return FALSE;
6573 }                             
6574
6575 static void
6576 unichar_chosen_func (const char *text,
6577                      gpointer    data)
6578 {
6579   GtkTextView *text_view = GTK_TEXT_VIEW (data);
6580
6581   gtk_text_view_commit_text (text_view, text);
6582 }
6583
6584 static void
6585 popup_targets_received (GtkClipboard     *clipboard,
6586                         GtkSelectionData *data,
6587                         gpointer          user_data)
6588 {
6589   PopupInfo *info = user_data;
6590   GtkTextView *text_view = info->text_view;
6591   
6592   if (GTK_WIDGET_REALIZED (text_view))
6593     {
6594       /* We implicitely rely here on the fact that if we are pasting ourself, we'll
6595        * have text targets as well as the private GTK_TEXT_BUFFER_CONTENTS target.
6596        */
6597       gboolean clipboard_contains_text = gtk_selection_data_targets_include_text (data);
6598       GtkWidget *menuitem;
6599       GtkWidget *submenu;
6600       gboolean have_selection;
6601       gboolean can_insert;
6602       GtkTextIter iter;
6603       GtkTextIter sel_start, sel_end;
6604       
6605       if (text_view->popup_menu)
6606         gtk_widget_destroy (text_view->popup_menu);
6607
6608       text_view->popup_menu = gtk_menu_new ();
6609       
6610       gtk_menu_attach_to_widget (GTK_MENU (text_view->popup_menu),
6611                                  GTK_WIDGET (text_view),
6612                                  popup_menu_detach);
6613       
6614       have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
6615                                                              &sel_start, &sel_end);
6616       
6617       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
6618                                         &iter,
6619                                         gtk_text_buffer_get_insert (get_buffer (text_view)));
6620       
6621       can_insert = gtk_text_iter_can_insert (&iter, text_view->editable);
6622       
6623       append_action_signal (text_view, text_view->popup_menu, GTK_STOCK_CUT, "cut_clipboard",
6624                             have_selection &&
6625                             range_contains_editable_text (&sel_start, &sel_end,
6626                                                           text_view->editable));
6627       append_action_signal (text_view, text_view->popup_menu, GTK_STOCK_COPY, "copy_clipboard",
6628                             have_selection);
6629       append_action_signal (text_view, text_view->popup_menu, GTK_STOCK_PASTE, "paste_clipboard",
6630                             can_insert && clipboard_contains_text);
6631       
6632       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
6633       gtk_widget_set_sensitive (menuitem, 
6634                                 have_selection &&
6635                                 range_contains_editable_text (&sel_start, &sel_end,
6636                                                               text_view->editable));
6637       g_signal_connect_swapped (menuitem, "activate",
6638                                 G_CALLBACK (delete_cb), text_view);
6639       gtk_widget_show (menuitem);
6640       gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
6641
6642       menuitem = gtk_separator_menu_item_new ();
6643       gtk_widget_show (menuitem);
6644       gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
6645
6646       menuitem = gtk_menu_item_new_with_mnemonic (_("Select _All"));
6647       g_signal_connect (menuitem, "activate",
6648                         G_CALLBACK (select_all_cb), text_view);
6649       gtk_widget_show (menuitem);
6650       gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
6651
6652       menuitem = gtk_separator_menu_item_new ();
6653       gtk_widget_show (menuitem);
6654       gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
6655       
6656       menuitem = gtk_menu_item_new_with_mnemonic (_("Input _Methods"));
6657       gtk_widget_show (menuitem);
6658       submenu = gtk_menu_new ();
6659       gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
6660       gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
6661       
6662       gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (text_view->im_context),
6663                                             GTK_MENU_SHELL (submenu));
6664
6665       menuitem = gtk_menu_item_new_with_mnemonic (_("_Insert Unicode Control Character"));
6666       gtk_widget_show (menuitem);
6667       gtk_widget_set_sensitive (menuitem, can_insert);
6668       
6669       submenu = gtk_menu_new ();
6670       gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
6671       gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);      
6672
6673       _gtk_text_util_append_special_char_menuitems (GTK_MENU_SHELL (submenu),
6674                                                     unichar_chosen_func,
6675                                                     text_view);
6676       
6677       g_signal_emit (text_view,
6678                      signals[POPULATE_POPUP],
6679                      0,
6680                      text_view->popup_menu);
6681       
6682       if (info->button)
6683         gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
6684                         NULL, NULL,
6685                         info->button, info->time);
6686       else
6687         {
6688           gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
6689                           popup_position_func, text_view,
6690                           0, gtk_get_current_event_time ());
6691           gtk_menu_shell_select_first (GTK_MENU_SHELL (text_view->popup_menu), FALSE);
6692         }
6693     }
6694
6695   g_object_unref (text_view);
6696   g_free (info);
6697 }
6698
6699 static void
6700 gtk_text_view_do_popup (GtkTextView    *text_view,
6701                         GdkEventButton *event)
6702 {
6703   PopupInfo *info = g_new (PopupInfo, 1);
6704
6705   /* should not need this, see http://bugzilla.gnome.org/show_bug.cgi?id=74620 */
6706   gtk_text_view_end_selection_drag (text_view, event);
6707   
6708   /* In order to know what entries we should make sensitive, we
6709    * ask for the current targets of the clipboard, and when
6710    * we get them, then we actually pop up the menu.
6711    */
6712   info->text_view = g_object_ref (text_view);
6713   
6714   if (event)
6715     {
6716       info->button = event->button;
6717       info->time = event->time;
6718     }
6719   else
6720     {
6721       info->button = 0;
6722       info->time = gtk_get_current_event_time ();
6723     }
6724
6725   gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (text_view),
6726                                                             GDK_SELECTION_CLIPBOARD),
6727                                   gdk_atom_intern ("TARGETS", FALSE),
6728                                   popup_targets_received,
6729                                   info);
6730 }
6731
6732 static gboolean
6733 gtk_text_view_popup_menu (GtkWidget *widget)
6734 {
6735   gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL);  
6736   return TRUE;
6737 }
6738
6739 /* Child GdkWindows */
6740
6741
6742 static GtkTextWindow*
6743 text_window_new (GtkTextWindowType  type,
6744                  GtkWidget         *widget,
6745                  gint               width_request,
6746                  gint               height_request)
6747 {
6748   GtkTextWindow *win;
6749
6750   win = g_new (GtkTextWindow, 1);
6751
6752   win->type = type;
6753   win->widget = widget;
6754   win->window = NULL;
6755   win->bin_window = NULL;
6756   win->requisition.width = width_request;
6757   win->requisition.height = height_request;
6758   win->allocation.width = width_request;
6759   win->allocation.height = height_request;
6760   win->allocation.x = 0;
6761   win->allocation.y = 0;
6762
6763   return win;
6764 }
6765
6766 static void
6767 text_window_free (GtkTextWindow *win)
6768 {
6769   if (win->window)
6770     text_window_unrealize (win);
6771
6772   g_free (win);
6773 }
6774
6775 static void
6776 text_window_realize (GtkTextWindow *win,
6777                      GdkWindow     *parent)
6778 {
6779   GdkWindowAttr attributes;
6780   gint attributes_mask;
6781   GdkCursor *cursor;
6782
6783   attributes.window_type = GDK_WINDOW_CHILD;
6784   attributes.x = win->allocation.x;
6785   attributes.y = win->allocation.y;
6786   attributes.width = win->allocation.width;
6787   attributes.height = win->allocation.height;
6788   attributes.wclass = GDK_INPUT_OUTPUT;
6789   attributes.visual = gtk_widget_get_visual (win->widget);
6790   attributes.colormap = gtk_widget_get_colormap (win->widget);
6791   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
6792
6793   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
6794
6795   win->window = gdk_window_new (parent,
6796                                 &attributes,
6797                                 attributes_mask);
6798
6799   gdk_window_show (win->window);
6800   gdk_window_set_user_data (win->window, win->widget);
6801
6802   attributes.x = 0;
6803   attributes.y = 0;
6804   attributes.width = win->allocation.width;
6805   attributes.height = win->allocation.height;
6806   attributes.event_mask = (GDK_EXPOSURE_MASK            |
6807                            GDK_SCROLL_MASK              |
6808                            GDK_KEY_PRESS_MASK           |
6809                            GDK_BUTTON_PRESS_MASK        |
6810                            GDK_BUTTON_RELEASE_MASK      |
6811                            GDK_POINTER_MOTION_MASK      |
6812                            GDK_POINTER_MOTION_HINT_MASK |
6813                            gtk_widget_get_events (win->widget));
6814
6815   win->bin_window = gdk_window_new (win->window,
6816                                     &attributes,
6817                                     attributes_mask);
6818
6819   gdk_window_show (win->bin_window);
6820   gdk_window_set_user_data (win->bin_window, win->widget);
6821
6822   if (win->type == GTK_TEXT_WINDOW_TEXT)
6823     {
6824       /* I-beam cursor */
6825       cursor = gdk_cursor_new_for_display (gdk_drawable_get_display (parent),
6826                                            GDK_XTERM);
6827       gdk_window_set_cursor (win->bin_window, cursor);
6828       gdk_cursor_unref (cursor);
6829
6830       gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->im_context,
6831                                         win->window);
6832
6833
6834       gdk_window_set_background (win->bin_window,
6835                                  &win->widget->style->base[GTK_WIDGET_STATE (win->widget)]);
6836     }
6837   else
6838     {
6839       gdk_window_set_background (win->bin_window,
6840                                  &win->widget->style->bg[GTK_WIDGET_STATE (win->widget)]);
6841     }
6842
6843   g_object_set_qdata (G_OBJECT (win->window),
6844                       g_quark_from_static_string ("gtk-text-view-text-window"),
6845                       win);
6846
6847   g_object_set_qdata (G_OBJECT (win->bin_window),
6848                       g_quark_from_static_string ("gtk-text-view-text-window"),
6849                       win);
6850 }
6851
6852 static void
6853 text_window_unrealize (GtkTextWindow *win)
6854 {
6855   if (win->type == GTK_TEXT_WINDOW_TEXT)
6856     {
6857       gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->im_context,
6858                                         NULL);
6859     }
6860
6861   gdk_window_set_user_data (win->window, NULL);
6862   gdk_window_set_user_data (win->bin_window, NULL);
6863   gdk_window_destroy (win->bin_window);
6864   gdk_window_destroy (win->window);
6865   win->window = NULL;
6866   win->bin_window = NULL;
6867 }
6868
6869 static void
6870 text_window_size_allocate (GtkTextWindow *win,
6871                            GdkRectangle  *rect)
6872 {
6873   win->allocation = *rect;
6874
6875   if (win->window)
6876     {
6877       gdk_window_move_resize (win->window,
6878                               rect->x, rect->y,
6879                               rect->width, rect->height);
6880
6881       gdk_window_resize (win->bin_window,
6882                          rect->width, rect->height);
6883     }
6884 }
6885
6886 static void
6887 text_window_scroll        (GtkTextWindow *win,
6888                            gint           dx,
6889                            gint           dy)
6890 {
6891   if (dx != 0 || dy != 0)
6892     {
6893       gdk_window_scroll (win->bin_window, dx, dy);
6894     }
6895 }
6896
6897 static void
6898 text_window_invalidate_rect (GtkTextWindow *win,
6899                              GdkRectangle  *rect)
6900 {
6901   GdkRectangle window_rect;
6902
6903   gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (win->widget),
6904                                          win->type,
6905                                          rect->x,
6906                                          rect->y,
6907                                          &window_rect.x,
6908                                          &window_rect.y);
6909
6910   window_rect.width = rect->width;
6911   window_rect.height = rect->height;
6912   
6913   /* Adjust the rect as appropriate */
6914   
6915   switch (win->type)
6916     {
6917     case GTK_TEXT_WINDOW_TEXT:
6918       break;
6919
6920     case GTK_TEXT_WINDOW_LEFT:
6921     case GTK_TEXT_WINDOW_RIGHT:
6922       window_rect.x = 0;
6923       window_rect.width = win->allocation.width;
6924       break;
6925
6926     case GTK_TEXT_WINDOW_TOP:
6927     case GTK_TEXT_WINDOW_BOTTOM:
6928       window_rect.y = 0;
6929       window_rect.height = win->allocation.height;
6930       break;
6931
6932     default:
6933       g_warning ("%s: bug!", G_STRLOC);
6934       return;
6935       break;
6936     }
6937           
6938   gdk_window_invalidate_rect (win->bin_window, &window_rect, FALSE);
6939
6940 #if 0
6941   {
6942     GdkColor color = { 0, 65535, 0, 0 };
6943     GdkGC *gc = gdk_gc_new (win->bin_window);
6944     gdk_gc_set_rgb_fg_color (gc, &color);
6945     gdk_draw_rectangle (win->bin_window,
6946                         gc, TRUE, window_rect.x, window_rect.y,
6947                         window_rect.width, window_rect.height);
6948     g_object_unref (gc);
6949   }
6950 #endif
6951 }
6952
6953 static gint
6954 text_window_get_width (GtkTextWindow *win)
6955 {
6956   return win->allocation.width;
6957 }
6958
6959 static gint
6960 text_window_get_height (GtkTextWindow *win)
6961 {
6962   return win->allocation.height;
6963 }
6964
6965 /* Windows */
6966
6967
6968 /**
6969  * gtk_text_view_get_window:
6970  * @text_view: a #GtkTextView
6971  * @win: window to get
6972  *
6973  * Retrieves the #GdkWindow corresponding to an area of the text view;
6974  * possible windows include the overall widget window, child windows
6975  * on the left, right, top, bottom, and the window that displays the
6976  * text buffer. Windows are %NULL and nonexistent if their width or
6977  * height is 0, and are nonexistent before the widget has been
6978  * realized.
6979  *
6980  * Return value: a #GdkWindow, or %NULL
6981  **/
6982 GdkWindow*
6983 gtk_text_view_get_window (GtkTextView *text_view,
6984                           GtkTextWindowType win)
6985 {
6986   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
6987
6988   switch (win)
6989     {
6990     case GTK_TEXT_WINDOW_WIDGET:
6991       return GTK_WIDGET (text_view)->window;
6992       break;
6993
6994     case GTK_TEXT_WINDOW_TEXT:
6995       return text_view->text_window->bin_window;
6996       break;
6997
6998     case GTK_TEXT_WINDOW_LEFT:
6999       if (text_view->left_window)
7000         return text_view->left_window->bin_window;
7001       else
7002         return NULL;
7003       break;
7004
7005     case GTK_TEXT_WINDOW_RIGHT:
7006       if (text_view->right_window)
7007         return text_view->right_window->bin_window;
7008       else
7009         return NULL;
7010       break;
7011
7012     case GTK_TEXT_WINDOW_TOP:
7013       if (text_view->top_window)
7014         return text_view->top_window->bin_window;
7015       else
7016         return NULL;
7017       break;
7018
7019     case GTK_TEXT_WINDOW_BOTTOM:
7020       if (text_view->bottom_window)
7021         return text_view->bottom_window->bin_window;
7022       else
7023         return NULL;
7024       break;
7025
7026     case GTK_TEXT_WINDOW_PRIVATE:
7027       g_warning ("%s: You can't get GTK_TEXT_WINDOW_PRIVATE, it has \"PRIVATE\" in the name because it is private.", G_GNUC_FUNCTION);
7028       return NULL;
7029       break;
7030     }
7031
7032   g_warning ("%s: Unknown GtkTextWindowType", G_GNUC_FUNCTION);
7033   return NULL;
7034 }
7035
7036 /**
7037  * gtk_text_view_get_window_type:
7038  * @text_view: a #GtkTextView
7039  * @window: a window type
7040  *
7041  * Usually used to find out which window an event corresponds to.
7042  * If you connect to an event signal on @text_view, this function
7043  * should be called on <literal>event-&gt;window</literal> to
7044  * see which window it was.
7045  *
7046  * Return value: the window type.
7047  **/
7048 GtkTextWindowType
7049 gtk_text_view_get_window_type (GtkTextView *text_view,
7050                                GdkWindow   *window)
7051 {
7052   GtkTextWindow *win;
7053
7054   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
7055   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
7056
7057   if (window == GTK_WIDGET (text_view)->window)
7058     return GTK_TEXT_WINDOW_WIDGET;
7059
7060   win = g_object_get_qdata (G_OBJECT (window),
7061                             g_quark_try_string ("gtk-text-view-text-window"));
7062
7063   if (win)
7064     return win->type;
7065   else
7066     {
7067       return GTK_TEXT_WINDOW_PRIVATE;
7068     }
7069 }
7070
7071 static void
7072 buffer_to_widget (GtkTextView      *text_view,
7073                   gint              buffer_x,
7074                   gint              buffer_y,
7075                   gint             *window_x,
7076                   gint             *window_y)
7077 {  
7078   if (window_x)
7079     {
7080       *window_x = buffer_x - text_view->xoffset;
7081       *window_x += text_view->text_window->allocation.x;
7082     }
7083
7084   if (window_y)
7085     {
7086       *window_y = buffer_y - text_view->yoffset;
7087       *window_y += text_view->text_window->allocation.y;
7088     }
7089 }
7090
7091 static void
7092 widget_to_text_window (GtkTextWindow *win,
7093                        gint           widget_x,
7094                        gint           widget_y,
7095                        gint          *window_x,
7096                        gint          *window_y)
7097 {
7098   if (window_x)
7099     *window_x = widget_x - win->allocation.x;
7100
7101   if (window_y)
7102     *window_y = widget_y - win->allocation.y;
7103 }
7104
7105 static void
7106 buffer_to_text_window (GtkTextView   *text_view,
7107                        GtkTextWindow *win,
7108                        gint           buffer_x,
7109                        gint           buffer_y,
7110                        gint          *window_x,
7111                        gint          *window_y)
7112 {
7113   if (win == NULL)
7114     {
7115       g_warning ("Attempt to convert text buffer coordinates to coordinates "
7116                  "for a nonexistent or private child window of GtkTextView");
7117       return;
7118     }
7119
7120   buffer_to_widget (text_view,
7121                     buffer_x, buffer_y,
7122                     window_x, window_y);
7123
7124   widget_to_text_window (win,
7125                          window_x ? *window_x : 0,
7126                          window_y ? *window_y : 0,
7127                          window_x,
7128                          window_y);
7129 }
7130
7131 /**
7132  * gtk_text_view_buffer_to_window_coords:
7133  * @text_view: a #GtkTextView
7134  * @win: a #GtkTextWindowType except #GTK_TEXT_WINDOW_PRIVATE
7135  * @buffer_x: buffer x coordinate
7136  * @buffer_y: buffer y coordinate
7137  * @window_x: window x coordinate return location
7138  * @window_y: window y coordinate return location
7139  *
7140  * Converts coordinate (@buffer_x, @buffer_y) to coordinates for the window
7141  * @win, and stores the result in (@window_x, @window_y). 
7142  *
7143  * Note that you can't convert coordinates for a nonexisting window (see 
7144  * gtk_text_view_set_border_window_size()).
7145  **/
7146 void
7147 gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
7148                                        GtkTextWindowType win,
7149                                        gint              buffer_x,
7150                                        gint              buffer_y,
7151                                        gint             *window_x,
7152                                        gint             *window_y)
7153 {
7154   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7155
7156   switch (win)
7157     {
7158     case GTK_TEXT_WINDOW_WIDGET:
7159       buffer_to_widget (text_view,
7160                         buffer_x, buffer_y,
7161                         window_x, window_y);
7162       break;
7163
7164     case GTK_TEXT_WINDOW_TEXT:
7165       if (window_x)
7166         *window_x = buffer_x - text_view->xoffset;
7167       if (window_y)
7168         *window_y = buffer_y - text_view->yoffset;
7169       break;
7170
7171     case GTK_TEXT_WINDOW_LEFT:
7172       buffer_to_text_window (text_view,
7173                              text_view->left_window,
7174                              buffer_x, buffer_y,
7175                              window_x, window_y);
7176       break;
7177
7178     case GTK_TEXT_WINDOW_RIGHT:
7179       buffer_to_text_window (text_view,
7180                              text_view->right_window,
7181                              buffer_x, buffer_y,
7182                              window_x, window_y);
7183       break;
7184
7185     case GTK_TEXT_WINDOW_TOP:
7186       buffer_to_text_window (text_view,
7187                              text_view->top_window,
7188                              buffer_x, buffer_y,
7189                              window_x, window_y);
7190       break;
7191
7192     case GTK_TEXT_WINDOW_BOTTOM:
7193       buffer_to_text_window (text_view,
7194                              text_view->bottom_window,
7195                              buffer_x, buffer_y,
7196                              window_x, window_y);
7197       break;
7198
7199     case GTK_TEXT_WINDOW_PRIVATE:
7200       g_warning ("%s: can't get coords for private windows", G_STRLOC);
7201       break;
7202
7203     default:
7204       g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC);
7205       break;
7206     }
7207 }
7208
7209 static void
7210 widget_to_buffer (GtkTextView *text_view,
7211                   gint         widget_x,
7212                   gint         widget_y,
7213                   gint        *buffer_x,
7214                   gint        *buffer_y)
7215 {  
7216   if (buffer_x)
7217     {
7218       *buffer_x = widget_x + text_view->xoffset;
7219       *buffer_x -= text_view->text_window->allocation.x;
7220     }
7221
7222   if (buffer_y)
7223     {
7224       *buffer_y = widget_y + text_view->yoffset;
7225       *buffer_y -= text_view->text_window->allocation.y;
7226     }
7227 }
7228
7229 static void
7230 text_window_to_widget (GtkTextWindow *win,
7231                        gint           window_x,
7232                        gint           window_y,
7233                        gint          *widget_x,
7234                        gint          *widget_y)
7235 {
7236   if (widget_x)
7237     *widget_x = window_x + win->allocation.x;
7238
7239   if (widget_y)
7240     *widget_y = window_y + win->allocation.y;
7241 }
7242
7243 static void
7244 text_window_to_buffer (GtkTextView   *text_view,
7245                        GtkTextWindow *win,
7246                        gint           window_x,
7247                        gint           window_y,
7248                        gint          *buffer_x,
7249                        gint          *buffer_y)
7250 {
7251   if (win == NULL)
7252     {
7253       g_warning ("Attempt to convert GtkTextView buffer coordinates into "
7254                  "coordinates for a nonexistent child window.");
7255       return;
7256     }
7257
7258   text_window_to_widget (win,
7259                          window_x,
7260                          window_y,
7261                          buffer_x,
7262                          buffer_y);
7263
7264   widget_to_buffer (text_view,
7265                     buffer_x ? *buffer_x : 0,
7266                     buffer_y ? *buffer_y : 0,
7267                     buffer_x,
7268                     buffer_y);
7269 }
7270
7271 /**
7272  * gtk_text_view_window_to_buffer_coords:
7273  * @text_view: a #GtkTextView
7274  * @win: a #GtkTextWindowType except #GTK_TEXT_WINDOW_PRIVATE
7275  * @window_x: window x coordinate
7276  * @window_y: window y coordinate
7277  * @buffer_x: buffer x coordinate return location
7278  * @buffer_y: buffer y coordinate return location
7279  *
7280  * Converts coordinates on the window identified by @win to buffer
7281  * coordinates, storing the result in (@buffer_x,@buffer_y).
7282  *
7283  * Note that you can't convert coordinates for a nonexisting window (see 
7284  * gtk_text_view_set_border_window_size()).
7285  **/
7286 void
7287 gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
7288                                        GtkTextWindowType win,
7289                                        gint              window_x,
7290                                        gint              window_y,
7291                                        gint             *buffer_x,
7292                                        gint             *buffer_y)
7293 {
7294   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7295
7296   switch (win)
7297     {
7298     case GTK_TEXT_WINDOW_WIDGET:
7299       widget_to_buffer (text_view,
7300                         window_x, window_y,
7301                         buffer_x, buffer_y);
7302       break;
7303
7304     case GTK_TEXT_WINDOW_TEXT:
7305       if (buffer_x)
7306         *buffer_x = window_x + text_view->xoffset;
7307       if (buffer_y)
7308         *buffer_y = window_y + text_view->yoffset;
7309       break;
7310
7311     case GTK_TEXT_WINDOW_LEFT:
7312       text_window_to_buffer (text_view,
7313                              text_view->left_window,
7314                              window_x, window_y,
7315                              buffer_x, buffer_y);
7316       break;
7317
7318     case GTK_TEXT_WINDOW_RIGHT:
7319       text_window_to_buffer (text_view,
7320                              text_view->right_window,
7321                              window_x, window_y,
7322                              buffer_x, buffer_y);
7323       break;
7324
7325     case GTK_TEXT_WINDOW_TOP:
7326       text_window_to_buffer (text_view,
7327                              text_view->top_window,
7328                              window_x, window_y,
7329                              buffer_x, buffer_y);
7330       break;
7331
7332     case GTK_TEXT_WINDOW_BOTTOM:
7333       text_window_to_buffer (text_view,
7334                              text_view->bottom_window,
7335                              window_x, window_y,
7336                              buffer_x, buffer_y);
7337       break;
7338
7339     case GTK_TEXT_WINDOW_PRIVATE:
7340       g_warning ("%s: can't get coords for private windows", G_STRLOC);
7341       break;
7342
7343     default:
7344       g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC);
7345       break;
7346     }
7347 }
7348
7349 static void
7350 set_window_width (GtkTextView      *text_view,
7351                   gint              width,
7352                   GtkTextWindowType type,
7353                   GtkTextWindow   **winp)
7354 {
7355   if (width == 0)
7356     {
7357       if (*winp)
7358         {
7359           text_window_free (*winp);
7360           *winp = NULL;
7361           gtk_widget_queue_resize (GTK_WIDGET (text_view));
7362         }
7363     }
7364   else
7365     {
7366       if (*winp == NULL)
7367         {
7368           *winp = text_window_new (type,
7369                                    GTK_WIDGET (text_view),
7370                                    width, 0);
7371           /* if the widget is already realized we need to realize the child manually */
7372           if (GTK_WIDGET_REALIZED (text_view))
7373             text_window_realize (*winp, GTK_WIDGET (text_view)->window);
7374         }
7375       else
7376         {
7377           if ((*winp)->requisition.width == width)
7378             return;
7379
7380           (*winp)->requisition.width = width;
7381         }
7382
7383       gtk_widget_queue_resize (GTK_WIDGET (text_view));
7384     }
7385 }
7386
7387
7388 static void
7389 set_window_height (GtkTextView      *text_view,
7390                    gint              height,
7391                    GtkTextWindowType type,
7392                    GtkTextWindow   **winp)
7393 {
7394   if (height == 0)
7395     {
7396       if (*winp)
7397         {
7398           text_window_free (*winp);
7399           *winp = NULL;
7400           gtk_widget_queue_resize (GTK_WIDGET (text_view));
7401         }
7402     }
7403   else
7404     {
7405       if (*winp == NULL)
7406         {
7407           *winp = text_window_new (type,
7408                                    GTK_WIDGET (text_view),
7409                                    0, height);
7410
7411           /* if the widget is already realized we need to realize the child manually */
7412           if (GTK_WIDGET_REALIZED (text_view))
7413             text_window_realize (*winp, GTK_WIDGET (text_view)->window);
7414         }
7415       else
7416         {
7417           if ((*winp)->requisition.height == height)
7418             return;
7419
7420           (*winp)->requisition.height = height;
7421         }
7422
7423       gtk_widget_queue_resize (GTK_WIDGET (text_view));
7424     }
7425 }
7426
7427 /**
7428  * gtk_text_view_set_border_window_size:
7429  * @text_view: a #GtkTextView
7430  * @type: window to affect
7431  * @size: width or height of the window
7432  *
7433  * Sets the width of %GTK_TEXT_WINDOW_LEFT or %GTK_TEXT_WINDOW_RIGHT,
7434  * or the height of %GTK_TEXT_WINDOW_TOP or %GTK_TEXT_WINDOW_BOTTOM.
7435  * Automatically destroys the corresponding window if the size is set
7436  * to 0, and creates the window if the size is set to non-zero.  This
7437  * function can only be used for the "border windows," it doesn't work
7438  * with #GTK_TEXT_WINDOW_WIDGET, #GTK_TEXT_WINDOW_TEXT, or
7439  * #GTK_TEXT_WINDOW_PRIVATE.
7440  **/
7441 void
7442 gtk_text_view_set_border_window_size (GtkTextView      *text_view,
7443                                       GtkTextWindowType type,
7444                                       gint              size)
7445
7446 {
7447   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7448   g_return_if_fail (size >= 0);
7449
7450   switch (type)
7451     {
7452     case GTK_TEXT_WINDOW_LEFT:
7453       set_window_width (text_view, size, GTK_TEXT_WINDOW_LEFT,
7454                         &text_view->left_window);
7455       break;
7456
7457     case GTK_TEXT_WINDOW_RIGHT:
7458       set_window_width (text_view, size, GTK_TEXT_WINDOW_RIGHT,
7459                         &text_view->right_window);
7460       break;
7461
7462     case GTK_TEXT_WINDOW_TOP:
7463       set_window_height (text_view, size, GTK_TEXT_WINDOW_TOP,
7464                          &text_view->top_window);
7465       break;
7466
7467     case GTK_TEXT_WINDOW_BOTTOM:
7468       set_window_height (text_view, size, GTK_TEXT_WINDOW_BOTTOM,
7469                          &text_view->bottom_window);
7470       break;
7471
7472     default:
7473       g_warning ("Can only set size of left/right/top/bottom border windows with gtk_text_view_set_border_window_size()");
7474       break;
7475     }
7476 }
7477
7478 /**
7479  * gtk_text_view_get_border_window_size:
7480  * @text_view: a #GtkTextView
7481  * @type: window to return size from
7482  *
7483  * Gets the width of the specified border window. See
7484  * gtk_text_view_set_border_window_size().
7485  *
7486  * Return value: width of window
7487  **/
7488 gint
7489 gtk_text_view_get_border_window_size (GtkTextView       *text_view,
7490                                       GtkTextWindowType  type)
7491 {
7492   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
7493   
7494   switch (type)
7495     {
7496     case GTK_TEXT_WINDOW_LEFT:
7497       if (text_view->left_window)
7498         return text_view->left_window->requisition.width;
7499       
7500     case GTK_TEXT_WINDOW_RIGHT:
7501       if (text_view->right_window)
7502         return text_view->right_window->requisition.width;
7503       
7504     case GTK_TEXT_WINDOW_TOP:
7505       if (text_view->top_window)
7506         return text_view->top_window->requisition.height;
7507
7508     case GTK_TEXT_WINDOW_BOTTOM:
7509       if (text_view->bottom_window)
7510         return text_view->bottom_window->requisition.height;
7511       
7512     default:
7513       g_warning ("Can only get size of left/right/top/bottom border windows with gtk_text_view_get_border_window_size()");
7514       break;
7515     }
7516
7517   return 0;
7518 }
7519
7520 /*
7521  * Child widgets
7522  */
7523
7524 static GtkTextViewChild*
7525 text_view_child_new_anchored (GtkWidget          *child,
7526                               GtkTextChildAnchor *anchor,
7527                               GtkTextLayout      *layout)
7528 {
7529   GtkTextViewChild *vc;
7530
7531   vc = g_new (GtkTextViewChild, 1);
7532
7533   vc->type = GTK_TEXT_WINDOW_PRIVATE;
7534   vc->widget = child;
7535   vc->anchor = anchor;
7536
7537   vc->from_top_of_line = 0;
7538   vc->from_left_of_buffer = 0;
7539   
7540   g_object_ref (vc->widget);
7541   g_object_ref (vc->anchor);
7542
7543   g_object_set_data (G_OBJECT (child),
7544                      "gtk-text-view-child",
7545                      vc);
7546
7547   gtk_text_child_anchor_register_child (anchor, child, layout);
7548   
7549   return vc;
7550 }
7551
7552 static GtkTextViewChild*
7553 text_view_child_new_window (GtkWidget          *child,
7554                             GtkTextWindowType   type,
7555                             gint                x,
7556                             gint                y)
7557 {
7558   GtkTextViewChild *vc;
7559
7560   vc = g_new (GtkTextViewChild, 1);
7561
7562   vc->widget = child;
7563   vc->anchor = NULL;
7564
7565   vc->from_top_of_line = 0;
7566   vc->from_left_of_buffer = 0;
7567  
7568   g_object_ref (vc->widget);
7569
7570   vc->type = type;
7571   vc->x = x;
7572   vc->y = y;
7573
7574   g_object_set_data (G_OBJECT (child),
7575                      "gtk-text-view-child",
7576                      vc);
7577   
7578   return vc;
7579 }
7580
7581 static void
7582 text_view_child_free (GtkTextViewChild *child)
7583 {
7584   g_object_set_data (G_OBJECT (child->widget),
7585                      "gtk-text-view-child", NULL);
7586
7587   if (child->anchor)
7588     {
7589       gtk_text_child_anchor_unregister_child (child->anchor,
7590                                               child->widget);
7591       g_object_unref (child->anchor);
7592     }
7593
7594   g_object_unref (child->widget);
7595
7596   g_free (child);
7597 }
7598
7599 static void
7600 text_view_child_set_parent_window (GtkTextView      *text_view,
7601                                    GtkTextViewChild *vc)
7602 {
7603   if (vc->anchor)
7604     gtk_widget_set_parent_window (vc->widget,
7605                                   text_view->text_window->bin_window);
7606   else
7607     {
7608       GdkWindow *window;
7609       window = gtk_text_view_get_window (text_view,
7610                                          vc->type);
7611       gtk_widget_set_parent_window (vc->widget, window);
7612     }
7613 }
7614
7615 static void
7616 add_child (GtkTextView      *text_view,
7617            GtkTextViewChild *vc)
7618 {
7619   text_view->children = g_slist_prepend (text_view->children,
7620                                          vc);
7621
7622   if (GTK_WIDGET_REALIZED (text_view))
7623     text_view_child_set_parent_window (text_view, vc);
7624   
7625   gtk_widget_set_parent (vc->widget, GTK_WIDGET (text_view));
7626 }
7627
7628 /**
7629  * gtk_text_view_add_child_at_anchor:
7630  * @text_view: a #GtkTextView
7631  * @child: a #GtkWidget
7632  * @anchor: a #GtkTextChildAnchor in the #GtkTextBuffer for @text_view
7633  * 
7634  * Adds a child widget in the text buffer, at the given @anchor.
7635  * 
7636  **/
7637 void
7638 gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
7639                                    GtkWidget            *child,
7640                                    GtkTextChildAnchor   *anchor)
7641 {
7642   GtkTextViewChild *vc;
7643
7644   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7645   g_return_if_fail (GTK_IS_WIDGET (child));
7646   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
7647   g_return_if_fail (child->parent == NULL);
7648
7649   gtk_text_view_ensure_layout (text_view);
7650
7651   vc = text_view_child_new_anchored (child, anchor,
7652                                      text_view->layout);
7653
7654   add_child (text_view, vc);
7655
7656   g_assert (vc->widget == child);
7657   g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
7658 }
7659
7660 /**
7661  * gtk_text_view_add_child_in_window:
7662  * @text_view: a #GtkTextView
7663  * @child: a #GtkWidget
7664  * @which_window: which window the child should appear in
7665  * @xpos: X position of child in window coordinates
7666  * @ypos: Y position of child in window coordinates
7667  *
7668  * Adds a child at fixed coordinates in one of the text widget's
7669  * windows.  The window must have nonzero size (see
7670  * gtk_text_view_set_border_window_size()).  Note that the child
7671  * coordinates are given relative to the #GdkWindow in question, and
7672  * that these coordinates have no sane relationship to scrolling. When
7673  * placing a child in #GTK_TEXT_WINDOW_WIDGET, scrolling is
7674  * irrelevant, the child floats above all scrollable areas. But when
7675  * placing a child in one of the scrollable windows (border windows or
7676  * text window), you'll need to compute the child's correct position
7677  * in buffer coordinates any time scrolling occurs or buffer changes
7678  * occur, and then call gtk_text_view_move_child() to update the
7679  * child's position. Unfortunately there's no good way to detect that
7680  * scrolling has occurred, using the current API; a possible hack
7681  * would be to update all child positions when the scroll adjustments
7682  * change or the text buffer changes. See bug 64518 on
7683  * bugzilla.gnome.org for status of fixing this issue.
7684  *
7685  **/
7686 void
7687 gtk_text_view_add_child_in_window (GtkTextView          *text_view,
7688                                    GtkWidget            *child,
7689                                    GtkTextWindowType     which_window,
7690                                    gint                  xpos,
7691                                    gint                  ypos)
7692 {
7693   GtkTextViewChild *vc;
7694
7695   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7696   g_return_if_fail (GTK_IS_WIDGET (child));
7697   g_return_if_fail (child->parent == NULL);
7698
7699   vc = text_view_child_new_window (child, which_window,
7700                                    xpos, ypos);
7701
7702   add_child (text_view, vc);
7703
7704   g_assert (vc->widget == child);
7705   g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
7706 }
7707
7708 /**
7709  * gtk_text_view_move_child:
7710  * @text_view: a #GtkTextView
7711  * @child: child widget already added to the text view
7712  * @xpos: new X position in window coordinates
7713  * @ypos: new Y position in window coordinates
7714  *
7715  * Updates the position of a child, as for gtk_text_view_add_child_in_window().
7716  * 
7717  **/
7718 void
7719 gtk_text_view_move_child          (GtkTextView          *text_view,
7720                                    GtkWidget            *child,
7721                                    gint                  xpos,
7722                                    gint                  ypos)
7723 {
7724   GtkTextViewChild *vc;
7725
7726   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
7727   g_return_if_fail (GTK_IS_WIDGET (child));
7728   g_return_if_fail (child->parent == (GtkWidget*) text_view);
7729
7730   vc = g_object_get_data (G_OBJECT (child),
7731                           "gtk-text-view-child");
7732
7733   g_assert (vc != NULL);
7734
7735   if (vc->x == xpos &&
7736       vc->y == ypos)
7737     return;
7738   
7739   vc->x = xpos;
7740   vc->y = ypos;
7741
7742   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (text_view))
7743     gtk_widget_queue_resize (child);
7744 }
7745
7746
7747 /* Iterator operations */
7748
7749 /**
7750  * gtk_text_view_forward_display_line:
7751  * @text_view: a #GtkTextView
7752  * @iter: a #GtkTextIter
7753  * 
7754  * Moves the given @iter forward by one display (wrapped) line.  A
7755  * display line is different from a paragraph. Paragraphs are
7756  * separated by newlines or other paragraph separator characters.
7757  * Display lines are created by line-wrapping a paragraph.  If
7758  * wrapping is turned off, display lines and paragraphs will be the
7759  * same. Display lines are divided differently for each view, since
7760  * they depend on the view's width; paragraphs are the same in all
7761  * views, since they depend on the contents of the #GtkTextBuffer.
7762  * 
7763  * Return value: %TRUE if @iter was moved and is not on the end iterator
7764  **/
7765 gboolean
7766 gtk_text_view_forward_display_line (GtkTextView *text_view,
7767                                     GtkTextIter *iter)
7768 {
7769   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7770   g_return_val_if_fail (iter != NULL, FALSE);
7771
7772   gtk_text_view_ensure_layout (text_view);
7773
7774   return gtk_text_layout_move_iter_to_next_line (text_view->layout, iter);
7775 }
7776
7777 /**
7778  * gtk_text_view_backward_display_line:
7779  * @text_view: a #GtkTextView
7780  * @iter: a #GtkTextIter
7781  * 
7782  * Moves the given @iter backward by one display (wrapped) line.  A
7783  * display line is different from a paragraph. Paragraphs are
7784  * separated by newlines or other paragraph separator characters.
7785  * Display lines are created by line-wrapping a paragraph.  If
7786  * wrapping is turned off, display lines and paragraphs will be the
7787  * same. Display lines are divided differently for each view, since
7788  * they depend on the view's width; paragraphs are the same in all
7789  * views, since they depend on the contents of the #GtkTextBuffer.
7790  * 
7791  * Return value: %TRUE if @iter was moved and is not on the end iterator
7792  **/
7793 gboolean
7794 gtk_text_view_backward_display_line (GtkTextView *text_view,
7795                                      GtkTextIter *iter)
7796 {
7797   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7798   g_return_val_if_fail (iter != NULL, FALSE);
7799
7800   gtk_text_view_ensure_layout (text_view);
7801
7802   return gtk_text_layout_move_iter_to_previous_line (text_view->layout, iter);
7803 }
7804
7805 /**
7806  * gtk_text_view_forward_display_line_end:
7807  * @text_view: a #GtkTextView
7808  * @iter: a #GtkTextIter
7809  * 
7810  * Moves the given @iter forward to the next display line end.  A
7811  * display line is different from a paragraph. Paragraphs are
7812  * separated by newlines or other paragraph separator characters.
7813  * Display lines are created by line-wrapping a paragraph.  If
7814  * wrapping is turned off, display lines and paragraphs will be the
7815  * same. Display lines are divided differently for each view, since
7816  * they depend on the view's width; paragraphs are the same in all
7817  * views, since they depend on the contents of the #GtkTextBuffer.
7818  * 
7819  * Return value: %TRUE if @iter was moved and is not on the end iterator
7820  **/
7821 gboolean
7822 gtk_text_view_forward_display_line_end (GtkTextView *text_view,
7823                                         GtkTextIter *iter)
7824 {
7825   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7826   g_return_val_if_fail (iter != NULL, FALSE);
7827
7828   gtk_text_view_ensure_layout (text_view);
7829
7830   return gtk_text_layout_move_iter_to_line_end (text_view->layout, iter, 1);
7831 }
7832
7833 /**
7834  * gtk_text_view_backward_display_line_start:
7835  * @text_view: a #GtkTextView
7836  * @iter: a #GtkTextIter
7837  * 
7838  * Moves the given @iter backward to the next display line start.  A
7839  * display line is different from a paragraph. Paragraphs are
7840  * separated by newlines or other paragraph separator characters.
7841  * Display lines are created by line-wrapping a paragraph.  If
7842  * wrapping is turned off, display lines and paragraphs will be the
7843  * same. Display lines are divided differently for each view, since
7844  * they depend on the view's width; paragraphs are the same in all
7845  * views, since they depend on the contents of the #GtkTextBuffer.
7846  * 
7847  * Return value: %TRUE if @iter was moved and is not on the end iterator
7848  **/
7849 gboolean
7850 gtk_text_view_backward_display_line_start (GtkTextView *text_view,
7851                                            GtkTextIter *iter)
7852 {
7853   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7854   g_return_val_if_fail (iter != NULL, FALSE);
7855
7856   gtk_text_view_ensure_layout (text_view);
7857
7858   return gtk_text_layout_move_iter_to_line_end (text_view->layout, iter, -1);
7859 }
7860
7861 /**
7862  * gtk_text_view_starts_display_line:
7863  * @text_view: a #GtkTextView
7864  * @iter: a #GtkTextIter
7865  * 
7866  * Determines whether @iter is at the start of a display line.
7867  * See gtk_text_view_forward_display_line() for an explanation of
7868  * display lines vs. paragraphs.
7869  * 
7870  * Return value: %TRUE if @iter begins a wrapped line
7871  **/
7872 gboolean
7873 gtk_text_view_starts_display_line (GtkTextView       *text_view,
7874                                    const GtkTextIter *iter)
7875 {
7876   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7877   g_return_val_if_fail (iter != NULL, FALSE);
7878
7879   gtk_text_view_ensure_layout (text_view);
7880
7881   return gtk_text_layout_iter_starts_line (text_view->layout, iter);
7882 }
7883
7884 /**
7885  * gtk_text_view_move_visually:
7886  * @text_view: a #GtkTextView
7887  * @iter: a #GtkTextIter
7888  * @count:   number of characters to move (negative moves left, positive moves right)
7889  *
7890  * Move the iterator a given number of characters visually, treating
7891  * it as the strong cursor position. If @count is positive, then the
7892  * new strong cursor position will be @count positions to the right of
7893  * the old cursor position. If @count is negative then the new strong
7894  * cursor position will be @count positions to the left of the old
7895  * cursor position.
7896  *
7897  * In the presence of bidirection text, the correspondence
7898  * between logical and visual order will depend on the direction
7899  * of the current run, and there may be jumps when the cursor
7900  * is moved off of the end of a run.
7901  * 
7902  * Return value: %TRUE if @iter moved and is not on the end iterator
7903  **/
7904 gboolean
7905 gtk_text_view_move_visually (GtkTextView *text_view,
7906                              GtkTextIter *iter,
7907                              gint         count)
7908 {
7909   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
7910   g_return_val_if_fail (iter != NULL, FALSE);
7911
7912   gtk_text_view_ensure_layout (text_view);
7913
7914   return gtk_text_layout_move_iter_visually (text_view->layout, iter, count);
7915 }