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