]> Pileus Git - ~andy/gtk/blob - gtk/gtktext.c
handle -1 argument in order to remove a workaround in Nautilus that forced
[~andy/gtk] / gtk / gtktext.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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 <ctype.h>
28 #include <string.h>
29 #include "gdk/gdkkeysyms.h"
30 #include "gdk/gdki18n.h"
31 #include "gtkmain.h"
32 #include "gtkselection.h"
33 #include "gtksignal.h"
34 #define GTK_ENABLE_BROKEN
35 #include "gtktext.h"
36 #include "line-wrap.xbm"
37 #include "line-arrow.xbm"
38 #include "gtkintl.h"
39
40
41 #define INITIAL_BUFFER_SIZE      1024
42 #define INITIAL_LINE_CACHE_SIZE  256
43 #define MIN_GAP_SIZE             256
44 #define LINE_DELIM               '\n'
45 #define MIN_TEXT_WIDTH_LINES     20
46 #define MIN_TEXT_HEIGHT_LINES    10
47 #define TEXT_BORDER_ROOM         1
48 #define LINE_WRAP_ROOM           8           /* The bitmaps are 6 wide. */
49 #define DEFAULT_TAB_STOP_WIDTH   4
50 #define SCROLL_PIXELS            5
51 #define KEY_SCROLL_PIXELS        10
52 #define SCROLL_TIME              100
53 #define FREEZE_LENGTH            1024        
54 /* Freeze text when inserting or deleting more than this many characters */
55
56 #define SET_PROPERTY_MARK(m, p, o)  do {                   \
57                                       (m)->property = (p); \
58                                       (m)->offset = (o);   \
59                                     } while (0)
60 #define MARK_CURRENT_PROPERTY(mark) ((TextProperty*)(mark)->property->data)
61 #define MARK_NEXT_PROPERTY(mark)    ((TextProperty*)(mark)->property->next->data)
62 #define MARK_PREV_PROPERTY(mark)    ((TextProperty*)((mark)->property->prev ?     \
63                                                      (mark)->property->prev->data \
64                                                      : NULL))
65 #define MARK_PREV_LIST_PTR(mark)    ((mark)->property->prev)
66 #define MARK_LIST_PTR(mark)         ((mark)->property)
67 #define MARK_NEXT_LIST_PTR(mark)    ((mark)->property->next)
68 #define MARK_OFFSET(mark)           ((mark)->offset)
69 #define MARK_PROPERTY_LENGTH(mark)  (MARK_CURRENT_PROPERTY(mark)->length)
70
71
72 #define MARK_CURRENT_FONT(text, mark) \
73   ((MARK_CURRENT_PROPERTY(mark)->flags & PROPERTY_FONT) ? \
74          MARK_CURRENT_PROPERTY(mark)->font->gdk_font : \
75          gtk_style_get_font (GTK_WIDGET (text)->style))
76 #define MARK_CURRENT_FORE(text, mark) \
77   ((MARK_CURRENT_PROPERTY(mark)->flags & PROPERTY_FOREGROUND) ? \
78          &MARK_CURRENT_PROPERTY(mark)->fore_color : \
79          &((GtkWidget *)text)->style->text[((GtkWidget *)text)->state])
80 #define MARK_CURRENT_BACK(text, mark) \
81   ((MARK_CURRENT_PROPERTY(mark)->flags & PROPERTY_BACKGROUND) ? \
82          &MARK_CURRENT_PROPERTY(mark)->back_color : \
83          &((GtkWidget *)text)->style->base[((GtkWidget *)text)->state])
84 #define MARK_CURRENT_TEXT_FONT(text, mark) \
85   ((MARK_CURRENT_PROPERTY(mark)->flags & PROPERTY_FONT) ? \
86          MARK_CURRENT_PROPERTY(mark)->font : \
87          text->current_font)
88
89 #define TEXT_LENGTH(t)              ((t)->text_end - (t)->gap_size)
90 #define FONT_HEIGHT(f)              ((f)->ascent + (f)->descent)
91 #define LINE_HEIGHT(l)              ((l).font_ascent + (l).font_descent)
92 #define LINE_CONTAINS(l, i)         ((l).start.index <= (i) && (l).end.index >= (i))
93 #define LINE_STARTS_AT(l, i)        ((l).start.index == (i))
94 #define LINE_START_PIXEL(l)         ((l).tab_cont.pixel_offset)
95 #define LAST_INDEX(t, m)            ((m).index == TEXT_LENGTH(t))
96 #define CACHE_DATA(c)               (*(LineParams*)(c)->data)
97
98 enum {
99   PROP_0,
100   PROP_HADJUSTMENT,
101   PROP_VADJUSTMENT,
102   PROP_LINE_WRAP,
103   PROP_WORD_WRAP
104 };
105
106 typedef struct _TextProperty          TextProperty;
107 typedef struct _TabStopMark           TabStopMark;
108 typedef struct _PrevTabCont           PrevTabCont;
109 typedef struct _FetchLinesData        FetchLinesData;
110 typedef struct _LineParams            LineParams;
111 typedef struct _SetVerticalScrollData SetVerticalScrollData;
112
113 typedef gint (*LineIteratorFunction) (GtkText* text, LineParams* lp, void* data);
114
115 typedef enum
116 {
117   FetchLinesPixels,
118   FetchLinesCount
119 } FLType;
120
121 struct _SetVerticalScrollData {
122   gint pixel_height;
123   gint last_didnt_wrap;
124   gint last_line_start;
125   GtkPropertyMark mark;
126 };
127
128 struct _GtkTextFont
129 {
130   /* The actual font. */
131   GdkFont *gdk_font;
132   guint ref_count;
133
134   gint16 char_widths[256];
135 };
136
137 typedef enum {
138   PROPERTY_FONT =       1 << 0,
139   PROPERTY_FOREGROUND = 1 << 1,
140   PROPERTY_BACKGROUND = 1 << 2
141 } TextPropertyFlags;
142
143 struct _TextProperty
144 {
145   /* Font. */
146   GtkTextFont* font;
147
148   /* Background Color. */
149   GdkColor back_color;
150   
151   /* Foreground Color. */
152   GdkColor fore_color;
153
154   /* Show which properties are set */
155   TextPropertyFlags flags;
156
157   /* Length of this property. */
158   guint length;
159 };
160
161 struct _TabStopMark
162 {
163   GList* tab_stops; /* Index into list containing the next tab position.  If
164                      * NULL, using default widths. */
165   gint to_next_tab;
166 };
167
168 struct _PrevTabCont
169 {
170   guint pixel_offset;
171   TabStopMark tab_start;
172 };
173
174 struct _FetchLinesData
175 {
176   GList* new_lines;
177   FLType fl_type;
178   gint data;
179   gint data_max;
180 };
181
182 struct _LineParams
183 {
184   guint font_ascent;
185   guint font_descent;
186   guint pixel_width;
187   guint displayable_chars;
188   guint wraps : 1;
189   
190   PrevTabCont tab_cont;
191   PrevTabCont tab_cont_next;
192   
193   GtkPropertyMark start;
194   GtkPropertyMark end;
195 };
196
197
198 static void  gtk_text_class_init     (GtkTextClass   *klass);
199 static void  gtk_text_set_property   (GObject         *object,
200                                       guint            prop_id,
201                                       const GValue    *value,
202                                       GParamSpec      *pspec);
203 static void  gtk_text_get_property   (GObject         *object,
204                                       guint            prop_id,
205                                       GValue          *value,
206                                       GParamSpec      *pspec);
207 static void  gtk_text_editable_init  (GtkEditableClass *iface);
208 static void  gtk_text_init           (GtkText        *text);
209 static void  gtk_text_destroy        (GtkObject      *object);
210 static void  gtk_text_finalize       (GObject        *object);
211 static void  gtk_text_realize        (GtkWidget      *widget);
212 static void  gtk_text_unrealize      (GtkWidget      *widget);
213 static void  gtk_text_style_set      (GtkWidget      *widget,
214                                       GtkStyle       *previous_style);
215 static void  gtk_text_state_changed  (GtkWidget      *widget,
216                                       GtkStateType    previous_state);
217 static void  gtk_text_draw_focus     (GtkWidget      *widget);
218 static void  gtk_text_size_request   (GtkWidget      *widget,
219                                       GtkRequisition *requisition);
220 static void  gtk_text_size_allocate  (GtkWidget      *widget,
221                                       GtkAllocation  *allocation);
222 static void  gtk_text_adjustment     (GtkAdjustment  *adjustment,
223                                       GtkText        *text);
224 static void   gtk_text_insert_text       (GtkEditable    *editable,
225                                           const gchar    *new_text,
226                                           gint            new_text_length,
227                                           gint           *position);
228 static void   gtk_text_delete_text       (GtkEditable    *editable,
229                                           gint            start_pos,
230                                           gint            end_pos);
231 static void   gtk_text_update_text       (GtkOldEditable *old_editable,
232                                           gint            start_pos,
233                                           gint            end_pos);
234 static gchar *gtk_text_get_chars         (GtkOldEditable *old_editable,
235                                           gint            start,
236                                           gint            end);
237 static void   gtk_text_set_selection     (GtkOldEditable *old_editable,
238                                           gint            start,
239                                           gint            end);
240 static void   gtk_text_real_set_editable (GtkOldEditable *old_editable,
241                                           gboolean        is_editable);
242
243 static void  gtk_text_adjustment_destroyed (GtkAdjustment  *adjustment,
244                                             GtkText        *text);
245
246 /* Event handlers */
247 static gint  gtk_text_expose            (GtkWidget         *widget,
248                                          GdkEventExpose    *event);
249 static gint  gtk_text_button_press      (GtkWidget         *widget,
250                                          GdkEventButton    *event);
251 static gint  gtk_text_button_release    (GtkWidget         *widget,
252                                          GdkEventButton    *event);
253 static gint  gtk_text_motion_notify     (GtkWidget         *widget,
254                                          GdkEventMotion    *event);
255 static gint  gtk_text_key_press         (GtkWidget         *widget,
256                                          GdkEventKey       *event);
257 static gint  gtk_text_focus_in          (GtkWidget         *widget,
258                                          GdkEventFocus     *event);
259 static gint  gtk_text_focus_out         (GtkWidget         *widget,
260                                          GdkEventFocus     *event);
261
262 static void move_gap (GtkText* text, guint index);
263 static void make_forward_space (GtkText* text, guint len);
264
265 /* Property management */
266 static GtkTextFont* get_text_font (GdkFont* gfont);
267 static void         text_font_unref (GtkTextFont *text_font);
268
269 static void insert_text_property (GtkText* text, GdkFont* font,
270                                   GdkColor *fore, GdkColor* back, guint len);
271 static TextProperty* new_text_property (GtkText *text, GdkFont* font, 
272                                         GdkColor* fore, GdkColor* back, guint length);
273 static void destroy_text_property (TextProperty *prop);
274 static void init_properties      (GtkText *text);
275 static void realize_property     (GtkText *text, TextProperty *prop);
276 static void realize_properties   (GtkText *text);
277 static void unrealize_property   (GtkText *text, TextProperty *prop);
278 static void unrealize_properties (GtkText *text);
279
280 static void delete_text_property (GtkText* text, guint len);
281
282 static guint pixel_height_of (GtkText* text, GList* cache_line);
283
284 /* Property Movement and Size Computations */
285 static void advance_mark (GtkPropertyMark* mark);
286 static void decrement_mark (GtkPropertyMark* mark);
287 static void advance_mark_n (GtkPropertyMark* mark, gint n);
288 static void decrement_mark_n (GtkPropertyMark* mark, gint n);
289 static void move_mark_n (GtkPropertyMark* mark, gint n);
290 static GtkPropertyMark find_mark (GtkText* text, guint mark_position);
291 static GtkPropertyMark find_mark_near (GtkText* text, guint mark_position, const GtkPropertyMark* near);
292 static void find_line_containing_point (GtkText* text, guint point,
293                                         gboolean scroll);
294
295 /* Display */
296 static void compute_lines_pixels (GtkText* text, guint char_count,
297                                   guint *lines, guint *pixels);
298
299 static gint total_line_height (GtkText* text,
300                                GList* line,
301                                gint line_count);
302 static LineParams find_line_params (GtkText* text,
303                                     const GtkPropertyMark *mark,
304                                     const PrevTabCont *tab_cont,
305                                     PrevTabCont *next_cont);
306 static void recompute_geometry (GtkText* text);
307 static void insert_expose (GtkText* text, guint old_pixels, gint nchars, guint new_line_count);
308 static void delete_expose (GtkText* text,
309                            guint nchars,
310                            guint old_lines, 
311                            guint old_pixels);
312 static GdkGC *create_bg_gc (GtkText *text);
313 static void clear_area (GtkText *text, GdkRectangle *area);
314 static void draw_line (GtkText* text,
315                        gint pixel_height,
316                        LineParams* lp);
317 static void draw_line_wrap (GtkText* text,
318                             guint height);
319 static void draw_cursor (GtkText* text, gint absolute);
320 static void undraw_cursor (GtkText* text, gint absolute);
321 static gint drawn_cursor_min (GtkText* text);
322 static gint drawn_cursor_max (GtkText* text);
323 static void expose_text (GtkText* text, GdkRectangle *area, gboolean cursor);
324
325 /* Search and Placement. */
326 static void find_cursor (GtkText* text,
327                          gboolean scroll);
328 static void find_cursor_at_line (GtkText* text,
329                                  const LineParams* start_line,
330                                  gint pixel_height);
331 static void find_mouse_cursor (GtkText* text, gint x, gint y);
332
333 /* Scrolling. */
334 static void adjust_adj  (GtkText* text, GtkAdjustment* adj);
335 static void scroll_up   (GtkText* text, gint diff);
336 static void scroll_down (GtkText* text, gint diff);
337 static void scroll_int  (GtkText* text, gint diff);
338
339 static void process_exposes (GtkText *text);
340
341 /* Cache Management. */
342 static void   free_cache        (GtkText* text);
343 static GList* remove_cache_line (GtkText* text, GList* list);
344
345 /* Key Motion. */
346 static void move_cursor_buffer_ver (GtkText *text, int dir);
347 static void move_cursor_page_ver (GtkText *text, int dir);
348 static void move_cursor_ver (GtkText *text, int count);
349 static void move_cursor_hor (GtkText *text, int count);
350
351 /* Binding actions */
352 static void gtk_text_move_cursor    (GtkOldEditable *old_editable,
353                                      gint            x,
354                                      gint            y);
355 static void gtk_text_move_word      (GtkOldEditable *old_editable,
356                                      gint            n);
357 static void gtk_text_move_page      (GtkOldEditable *old_editable,
358                                      gint            x,
359                                      gint            y);
360 static void gtk_text_move_to_row    (GtkOldEditable *old_editable,
361                                      gint            row);
362 static void gtk_text_move_to_column (GtkOldEditable *old_editable,
363                                      gint            row);
364 static void gtk_text_kill_char      (GtkOldEditable *old_editable,
365                                      gint            direction);
366 static void gtk_text_kill_word      (GtkOldEditable *old_editable,
367                                      gint            direction);
368 static void gtk_text_kill_line      (GtkOldEditable *old_editable,
369                                      gint            direction);
370
371 /* To be removed */
372 static void gtk_text_move_forward_character    (GtkText          *text);
373 static void gtk_text_move_backward_character   (GtkText          *text);
374 static void gtk_text_move_forward_word         (GtkText          *text);
375 static void gtk_text_move_backward_word        (GtkText          *text);
376 static void gtk_text_move_beginning_of_line    (GtkText          *text);
377 static void gtk_text_move_end_of_line          (GtkText          *text);
378 static void gtk_text_move_next_line            (GtkText          *text);
379 static void gtk_text_move_previous_line        (GtkText          *text);
380
381 static void gtk_text_delete_forward_character  (GtkText          *text);
382 static void gtk_text_delete_backward_character (GtkText          *text);
383 static void gtk_text_delete_forward_word       (GtkText          *text);
384 static void gtk_text_delete_backward_word      (GtkText          *text);
385 static void gtk_text_delete_line               (GtkText          *text);
386 static void gtk_text_delete_to_line_end        (GtkText          *text);
387 static void gtk_text_select_word               (GtkText          *text,
388                                                 guint32           time);
389 static void gtk_text_select_line               (GtkText          *text,
390                                                 guint32           time);
391
392 static void gtk_text_set_position (GtkOldEditable *old_editable,
393                                    gint            position);
394
395 /* #define DEBUG_GTK_TEXT */
396
397 #if defined(DEBUG_GTK_TEXT) && defined(__GNUC__)
398 /* Debugging utilities. */
399 static void gtk_text_assert_mark (GtkText         *text,
400                                   GtkPropertyMark *mark,
401                                   GtkPropertyMark *before,
402                                   GtkPropertyMark *after,
403                                   const gchar     *msg,
404                                   const gchar     *where,
405                                   gint             line);
406
407 static void gtk_text_assert (GtkText         *text,
408                              const gchar     *msg,
409                              gint             line);
410 static void gtk_text_show_cache_line (GtkText *text, GList *cache,
411                                       const char* what, const char* func, gint line);
412 static void gtk_text_show_cache (GtkText *text, const char* func, gint line);
413 static void gtk_text_show_adj (GtkText *text,
414                                GtkAdjustment *adj,
415                                const char* what,
416                                const char* func,
417                                gint line);
418 static void gtk_text_show_props (GtkText* test,
419                                  const char* func,
420                                  int line);
421
422 #define TDEBUG(args) g_message args
423 #define TEXT_ASSERT(text) gtk_text_assert (text,__PRETTY_FUNCTION__,__LINE__)
424 #define TEXT_ASSERT_MARK(text,mark,msg) gtk_text_assert_mark (text,mark, \
425                                            __PRETTY_FUNCTION__,msg,__LINE__)
426 #define TEXT_SHOW(text) gtk_text_show_cache (text, __PRETTY_FUNCTION__,__LINE__)
427 #define TEXT_SHOW_LINE(text,line,msg) gtk_text_show_cache_line (text,line,msg,\
428                                            __PRETTY_FUNCTION__,__LINE__)
429 #define TEXT_SHOW_ADJ(text,adj,msg) gtk_text_show_adj (text,adj,msg, \
430                                           __PRETTY_FUNCTION__,__LINE__)
431 #else
432 #define TDEBUG(args)
433 #define TEXT_ASSERT(text)
434 #define TEXT_ASSERT_MARK(text,mark,msg)
435 #define TEXT_SHOW(text)
436 #define TEXT_SHOW_LINE(text,line,msg)
437 #define TEXT_SHOW_ADJ(text,adj,msg)
438 #endif
439
440 /* Memory Management. */
441 static GMemChunk  *params_mem_chunk    = NULL;
442 static GMemChunk  *text_property_chunk = NULL;
443
444 static GtkWidgetClass *parent_class = NULL;
445
446
447 static const GtkTextFunction control_keys[26] =
448 {
449   (GtkTextFunction)gtk_text_move_beginning_of_line,    /* a */
450   (GtkTextFunction)gtk_text_move_backward_character,   /* b */
451   (GtkTextFunction)gtk_editable_copy_clipboard,        /* c */
452   (GtkTextFunction)gtk_text_delete_forward_character,  /* d */
453   (GtkTextFunction)gtk_text_move_end_of_line,          /* e */
454   (GtkTextFunction)gtk_text_move_forward_character,    /* f */
455   NULL,                                                /* g */
456   (GtkTextFunction)gtk_text_delete_backward_character, /* h */
457   NULL,                                                /* i */
458   NULL,                                                /* j */
459   (GtkTextFunction)gtk_text_delete_to_line_end,        /* k */
460   NULL,                                                /* l */
461   NULL,                                                /* m */
462   (GtkTextFunction)gtk_text_move_next_line,            /* n */
463   NULL,                                                /* o */
464   (GtkTextFunction)gtk_text_move_previous_line,        /* p */
465   NULL,                                                /* q */
466   NULL,                                                /* r */
467   NULL,                                                /* s */
468   NULL,                                                /* t */
469   (GtkTextFunction)gtk_text_delete_line,               /* u */
470   (GtkTextFunction)gtk_editable_paste_clipboard,       /* v */
471   (GtkTextFunction)gtk_text_delete_backward_word,      /* w */
472   (GtkTextFunction)gtk_editable_cut_clipboard,         /* x */
473   NULL,                                                /* y */
474   NULL,                                                /* z */
475 };
476
477 static const GtkTextFunction alt_keys[26] =
478 {
479   NULL,                                                /* a */
480   (GtkTextFunction)gtk_text_move_backward_word,        /* b */
481   NULL,                                                /* c */
482   (GtkTextFunction)gtk_text_delete_forward_word,       /* d */
483   NULL,                                           /* e */
484   (GtkTextFunction)gtk_text_move_forward_word,         /* f */
485   NULL,                                           /* g */
486   NULL,                                           /* h */
487   NULL,                                           /* i */
488   NULL,                                           /* j */
489   NULL,                                           /* k */
490   NULL,                                           /* l */
491   NULL,                                           /* m */
492   NULL,                                           /* n */
493   NULL,                                           /* o */
494   NULL,                                           /* p */
495   NULL,                                           /* q */
496   NULL,                                           /* r */
497   NULL,                                           /* s */
498   NULL,                                           /* t */
499   NULL,                                           /* u */
500   NULL,                                           /* v */
501   NULL,                                           /* w */
502   NULL,                                           /* x */
503   NULL,                                           /* y */
504   NULL,                                           /* z */
505 };
506
507
508 /**********************************************************************/
509 /*                              Widget Crap                           */
510 /**********************************************************************/
511
512 GtkType
513 gtk_text_get_type (void)
514 {
515   static GtkType text_type = 0;
516   
517   if (!text_type)
518     {
519       static const GtkTypeInfo text_info =
520       {
521         "GtkText",
522         sizeof (GtkText),
523         sizeof (GtkTextClass),
524         (GtkClassInitFunc) gtk_text_class_init,
525         (GtkObjectInitFunc) gtk_text_init,
526         /* reserved_1 */ NULL,
527         /* reserved_2 */ NULL,
528         (GtkClassInitFunc) NULL,
529       };
530
531       static const GInterfaceInfo editable_info =
532       {
533         (GInterfaceInitFunc) gtk_text_editable_init, /* interface_init */
534         NULL, /* interface_finalize */
535         NULL  /* interface_data */
536       };
537       
538       text_type = gtk_type_unique (GTK_TYPE_OLD_EDITABLE, &text_info);
539       g_type_add_interface_static (text_type,
540                                    GTK_TYPE_EDITABLE,
541                                    &editable_info);
542     }
543   
544   return text_type;
545 }
546
547 static void
548 gtk_text_class_init (GtkTextClass *class)
549 {
550   GObjectClass *gobject_class;
551   GtkObjectClass *object_class;
552   GtkWidgetClass *widget_class;
553   GtkOldEditableClass *old_editable_class;
554
555   gobject_class = G_OBJECT_CLASS (class);
556   object_class = (GtkObjectClass*) class;
557   widget_class = (GtkWidgetClass*) class;
558   old_editable_class = (GtkOldEditableClass*) class;
559   parent_class = gtk_type_class (GTK_TYPE_OLD_EDITABLE);
560
561   gobject_class->finalize = gtk_text_finalize;
562   gobject_class->set_property = gtk_text_set_property;
563   gobject_class->get_property = gtk_text_get_property;
564   
565   object_class->destroy = gtk_text_destroy;
566   
567   widget_class->realize = gtk_text_realize;
568   widget_class->unrealize = gtk_text_unrealize;
569   widget_class->style_set = gtk_text_style_set;
570   widget_class->state_changed = gtk_text_state_changed;
571   widget_class->size_request = gtk_text_size_request;
572   widget_class->size_allocate = gtk_text_size_allocate;
573   widget_class->expose_event = gtk_text_expose;
574   widget_class->button_press_event = gtk_text_button_press;
575   widget_class->button_release_event = gtk_text_button_release;
576   widget_class->motion_notify_event = gtk_text_motion_notify;
577   widget_class->key_press_event = gtk_text_key_press;
578   widget_class->focus_in_event = gtk_text_focus_in;
579   widget_class->focus_out_event = gtk_text_focus_out;
580
581   old_editable_class->set_editable = gtk_text_real_set_editable;
582   
583   old_editable_class->move_cursor = gtk_text_move_cursor;
584   old_editable_class->move_word = gtk_text_move_word;
585   old_editable_class->move_page = gtk_text_move_page;
586   old_editable_class->move_to_row = gtk_text_move_to_row;
587   old_editable_class->move_to_column = gtk_text_move_to_column;
588   
589   old_editable_class->kill_char = gtk_text_kill_char;
590   old_editable_class->kill_word = gtk_text_kill_word;
591   old_editable_class->kill_line = gtk_text_kill_line;
592   
593   old_editable_class->update_text = gtk_text_update_text;
594   old_editable_class->get_chars   = gtk_text_get_chars;
595   old_editable_class->set_selection = gtk_text_set_selection;
596   old_editable_class->set_position = gtk_text_set_position;
597
598   class->set_scroll_adjustments = gtk_text_set_adjustments;
599
600   g_object_class_install_property (gobject_class,
601                                    PROP_HADJUSTMENT,
602                                    g_param_spec_object ("hadjustment",
603                                                         _("Horizontal Adjustment"),
604                                                         _("Horizontal adjustment for the text widget"),
605                                                         GTK_TYPE_ADJUSTMENT,
606                                                         G_PARAM_READWRITE));
607
608   g_object_class_install_property (gobject_class,
609                                    PROP_VADJUSTMENT,
610                                    g_param_spec_object ("vadjustment",
611                                                         _("Vertical Adjustment"),
612                                                         _("Vertical adjustment for the text widget"),
613                                                         GTK_TYPE_ADJUSTMENT,
614                                                         G_PARAM_READWRITE));
615
616   g_object_class_install_property (gobject_class,
617                                    PROP_LINE_WRAP,
618                                    g_param_spec_boolean ("line_wrap",
619                                                          _("Line Wrap"),
620                                                          _("Whether lines are wrapped at widget edges"),
621                                                          TRUE,
622                                                          G_PARAM_READWRITE));
623
624   g_object_class_install_property (gobject_class,
625                                    PROP_WORD_WRAP,
626                                    g_param_spec_boolean ("word_wrap",
627                                                          _("Word Wrap"),
628                                                          _("Whether words are wrapped at widget edges"),
629                                                          FALSE,
630                                                          G_PARAM_READWRITE));
631
632   widget_class->set_scroll_adjustments_signal =
633     gtk_signal_new ("set_scroll_adjustments",
634                     GTK_RUN_LAST,
635                     GTK_CLASS_TYPE (object_class),
636                     GTK_SIGNAL_OFFSET (GtkTextClass, set_scroll_adjustments),
637                     gtk_marshal_VOID__OBJECT_OBJECT,
638                     GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
639 }
640
641 static void
642 gtk_text_set_property (GObject         *object,
643                        guint            prop_id,
644                        const GValue    *value,
645                        GParamSpec      *pspec)
646 {
647   GtkText *text;
648   
649   text = GTK_TEXT (object);
650   
651   switch (prop_id)
652     {
653     case PROP_HADJUSTMENT:
654       gtk_text_set_adjustments (text,
655                                 g_value_get_object (value),
656                                 text->vadj);
657       break;
658     case PROP_VADJUSTMENT:
659       gtk_text_set_adjustments (text,
660                                 text->hadj,
661                                 g_value_get_object (value));
662       break;
663     case PROP_LINE_WRAP:
664       gtk_text_set_line_wrap (text, g_value_get_boolean (value));
665       break;
666     case PROP_WORD_WRAP:
667       gtk_text_set_word_wrap (text, g_value_get_boolean (value));
668       break;
669     default:
670       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
671       break;
672     }
673 }
674
675 static void
676 gtk_text_get_property (GObject         *object,
677                        guint            prop_id,
678                        GValue          *value,
679                        GParamSpec      *pspec)
680 {
681   GtkText *text;
682   
683   text = GTK_TEXT (object);
684   
685   switch (prop_id)
686     {
687     case PROP_HADJUSTMENT:
688       g_value_set_object (value, text->hadj);
689       break;
690     case PROP_VADJUSTMENT:
691       g_value_set_object (value, text->vadj);
692       break;
693     case PROP_LINE_WRAP:
694       g_value_set_boolean (value, text->line_wrap);
695       break;
696     case PROP_WORD_WRAP:
697       g_value_set_boolean (value, text->word_wrap);
698       break;
699     default:
700       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
701       break;
702     }
703 }
704
705 static void
706 gtk_text_editable_init (GtkEditableClass *iface)
707 {
708   iface->insert_text = gtk_text_insert_text;
709   iface->delete_text = gtk_text_delete_text;
710 }
711
712 static void
713 gtk_text_init (GtkText *text)
714 {
715   GTK_WIDGET_SET_FLAGS (text, GTK_CAN_FOCUS);
716
717   text->text_area = NULL;
718   text->hadj = NULL;
719   text->vadj = NULL;
720   text->gc = NULL;
721   text->bg_gc = NULL;
722   text->line_wrap_bitmap = NULL;
723   text->line_arrow_bitmap = NULL;
724   
725   text->use_wchar = FALSE;
726   text->text.ch = g_new (guchar, INITIAL_BUFFER_SIZE);
727   text->text_len = INITIAL_BUFFER_SIZE;
728  
729   text->scratch_buffer.ch = NULL;
730   text->scratch_buffer_len = 0;
731  
732   text->freeze_count = 0;
733   
734   if (!params_mem_chunk)
735     params_mem_chunk = g_mem_chunk_new ("LineParams",
736                                         sizeof (LineParams),
737                                         256 * sizeof (LineParams),
738                                         G_ALLOC_AND_FREE);
739   
740   text->default_tab_width = 4;
741   text->tab_stops = NULL;
742   
743   text->tab_stops = g_list_prepend (text->tab_stops, (void*)8);
744   text->tab_stops = g_list_prepend (text->tab_stops, (void*)8);
745   
746   text->line_start_cache = NULL;
747   text->first_cut_pixels = 0;
748   
749   text->line_wrap = TRUE;
750   text->word_wrap = FALSE;
751   
752   text->timer = 0;
753   text->button = 0;
754   
755   text->current_font = NULL;
756   
757   init_properties (text);
758   
759   GTK_OLD_EDITABLE (text)->editable = FALSE;
760   
761   gtk_text_set_adjustments (text, NULL, NULL);
762   gtk_editable_set_position (GTK_EDITABLE (text), 0);
763 }
764
765 GtkWidget*
766 gtk_text_new (GtkAdjustment *hadj,
767               GtkAdjustment *vadj)
768 {
769   GtkWidget *text;
770
771   if (hadj)
772     g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadj), NULL);
773   if (vadj)
774     g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadj), NULL);
775
776   text = gtk_widget_new (GTK_TYPE_TEXT,
777                          "hadjustment", hadj,
778                          "vadjustment", vadj,
779                          NULL);
780
781   return text;
782 }
783
784 void
785 gtk_text_set_word_wrap (GtkText *text,
786                         gboolean word_wrap)
787 {
788   g_return_if_fail (GTK_IS_TEXT (text));
789   
790   text->word_wrap = (word_wrap != FALSE);
791   
792   if (GTK_WIDGET_REALIZED (text))
793     {
794       recompute_geometry (text);
795       gtk_widget_queue_draw (GTK_WIDGET (text));
796     }
797   
798   g_object_notify (G_OBJECT (text), "word_wrap");
799 }
800
801 void
802 gtk_text_set_line_wrap (GtkText *text,
803                         gboolean line_wrap)
804 {
805   g_return_if_fail (GTK_IS_TEXT (text));
806   
807   text->line_wrap = (line_wrap != FALSE);
808   
809   if (GTK_WIDGET_REALIZED (text))
810     {
811       recompute_geometry (text);
812       gtk_widget_queue_draw (GTK_WIDGET (text));
813     }
814   
815   g_object_notify (G_OBJECT (text), "line_wrap");
816 }
817
818 void
819 gtk_text_set_editable (GtkText *text,
820                        gboolean is_editable)
821 {
822   g_return_if_fail (GTK_IS_TEXT (text));
823   
824   gtk_editable_set_editable (GTK_EDITABLE (text), is_editable);
825 }
826
827 static void
828 gtk_text_real_set_editable (GtkOldEditable *old_editable,
829                             gboolean        is_editable)
830 {
831   GtkText *text;
832   
833   g_return_if_fail (GTK_IS_TEXT (old_editable));
834   
835   text = GTK_TEXT (old_editable);
836   
837   old_editable->editable = (is_editable != FALSE);
838   
839   if (is_editable)
840     draw_cursor (text, TRUE);
841   else
842     undraw_cursor (text, TRUE);
843 }
844
845 void
846 gtk_text_set_adjustments (GtkText       *text,
847                           GtkAdjustment *hadj,
848                           GtkAdjustment *vadj)
849 {
850   g_return_if_fail (GTK_IS_TEXT (text));
851   if (hadj)
852     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
853   else
854     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
855   if (vadj)
856     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
857   else
858     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
859   
860   if (text->hadj && (text->hadj != hadj))
861     {
862       gtk_signal_disconnect_by_data (GTK_OBJECT (text->hadj), text);
863       gtk_object_unref (GTK_OBJECT (text->hadj));
864     }
865   
866   if (text->vadj && (text->vadj != vadj))
867     {
868       gtk_signal_disconnect_by_data (GTK_OBJECT (text->vadj), text);
869       gtk_object_unref (GTK_OBJECT (text->vadj));
870     }
871   
872   if (text->hadj != hadj)
873     {
874       text->hadj = hadj;
875       gtk_object_ref (GTK_OBJECT (text->hadj));
876       gtk_object_sink (GTK_OBJECT (text->hadj));
877       
878       gtk_signal_connect (GTK_OBJECT (text->hadj), "changed",
879                           (GtkSignalFunc) gtk_text_adjustment,
880                           text);
881       gtk_signal_connect (GTK_OBJECT (text->hadj), "value_changed",
882                           (GtkSignalFunc) gtk_text_adjustment,
883                           text);
884       gtk_signal_connect (GTK_OBJECT (text->hadj), "destroy",
885                           (GtkSignalFunc) gtk_text_adjustment_destroyed,
886                           text);
887       gtk_text_adjustment (hadj, text);
888
889       g_object_notify (G_OBJECT (text), "hadjustment");
890     }
891   
892   if (text->vadj != vadj)
893     {
894       text->vadj = vadj;
895       gtk_object_ref (GTK_OBJECT (text->vadj));
896       gtk_object_sink (GTK_OBJECT (text->vadj));
897       
898       gtk_signal_connect (GTK_OBJECT (text->vadj), "changed",
899                           (GtkSignalFunc) gtk_text_adjustment,
900                           text);
901       gtk_signal_connect (GTK_OBJECT (text->vadj), "value_changed",
902                           (GtkSignalFunc) gtk_text_adjustment,
903                           text);
904       gtk_signal_connect (GTK_OBJECT (text->vadj), "destroy",
905                           (GtkSignalFunc) gtk_text_adjustment_destroyed,
906                           text);
907       gtk_text_adjustment (vadj, text);
908
909       g_object_notify (G_OBJECT (text), "vadjustment");
910     }
911 }
912
913 void
914 gtk_text_set_point (GtkText *text,
915                     guint    index)
916 {
917   g_return_if_fail (GTK_IS_TEXT (text));
918   g_return_if_fail (index <= TEXT_LENGTH (text));
919   
920   text->point = find_mark (text, index);
921 }
922
923 guint
924 gtk_text_get_point (GtkText *text)
925 {
926   g_return_val_if_fail (GTK_IS_TEXT (text), 0);
927   
928   return text->point.index;
929 }
930
931 guint
932 gtk_text_get_length (GtkText *text)
933 {
934   g_return_val_if_fail (GTK_IS_TEXT (text), 0);
935   
936   return TEXT_LENGTH (text);
937 }
938
939 void
940 gtk_text_freeze (GtkText *text)
941 {
942   g_return_if_fail (GTK_IS_TEXT (text));
943   
944   text->freeze_count++;
945 }
946
947 void
948 gtk_text_thaw (GtkText *text)
949 {
950   g_return_if_fail (GTK_IS_TEXT (text));
951   
952   if (text->freeze_count)
953     if (!(--text->freeze_count) && GTK_WIDGET_REALIZED (text))
954       {
955         recompute_geometry (text);
956         gtk_widget_queue_draw (GTK_WIDGET (text));
957       }
958 }
959
960 void
961 gtk_text_insert (GtkText    *text,
962                  GdkFont    *font,
963                  GdkColor   *fore,
964                  GdkColor   *back,
965                  const char *chars,
966                  gint        nchars)
967 {
968   GtkOldEditable *old_editable = GTK_OLD_EDITABLE (text);
969   gboolean frozen = FALSE;
970   
971   gint new_line_count = 1;
972   guint old_height = 0;
973   guint length;
974   guint i;
975   gint numwcs;
976   
977   g_return_if_fail (GTK_IS_TEXT (text));
978
979   if (nchars < 0)
980     length = strlen (chars);
981   else
982     length = nchars;
983   
984   if (length == 0)
985     return;
986   
987   if (!text->freeze_count && (length > FREEZE_LENGTH))
988     {
989       gtk_text_freeze (text);
990       frozen = TRUE;
991     }
992   
993   if (!text->freeze_count && (text->line_start_cache != NULL))
994     {
995       find_line_containing_point (text, text->point.index, TRUE);
996       old_height = total_line_height (text, text->current_line, 1);
997     }
998   
999   if ((TEXT_LENGTH (text) == 0) && (text->use_wchar == FALSE))
1000     {
1001       GtkWidget *widget;
1002       widget = GTK_WIDGET (text);
1003       gtk_widget_ensure_style (widget);
1004       if ((widget->style) && (gtk_style_get_font (widget->style)->type == GDK_FONT_FONTSET))
1005         {
1006           text->use_wchar = TRUE;
1007           g_free (text->text.ch);
1008           text->text.wc = g_new (GdkWChar, INITIAL_BUFFER_SIZE);
1009           text->text_len = INITIAL_BUFFER_SIZE;
1010           if (text->scratch_buffer.ch)
1011             g_free (text->scratch_buffer.ch);
1012           text->scratch_buffer.wc = NULL;
1013           text->scratch_buffer_len = 0;
1014         }
1015     }
1016  
1017   move_gap (text, text->point.index);
1018   make_forward_space (text, length);
1019  
1020   if (text->use_wchar)
1021     {
1022       char *chars_nt = (char *)chars;
1023       if (nchars > 0)
1024         {
1025           chars_nt = g_new (char, length+1);
1026           memcpy (chars_nt, chars, length);
1027           chars_nt[length] = 0;
1028         }
1029       numwcs = gdk_mbstowcs (text->text.wc + text->gap_position, chars_nt,
1030                              length);
1031       if (chars_nt != chars)
1032         g_free(chars_nt);
1033       if (numwcs < 0)
1034         numwcs = 0;
1035     }
1036   else
1037     {
1038       numwcs = length;
1039       memcpy(text->text.ch + text->gap_position, chars, length);
1040     }
1041  
1042   if (!text->freeze_count && (text->line_start_cache != NULL))
1043     {
1044       if (text->use_wchar)
1045         {
1046           for (i=0; i<numwcs; i++)
1047             if (text->text.wc[text->gap_position + i] == '\n')
1048               new_line_count++;
1049         }
1050       else
1051         {
1052           for (i=0; i<numwcs; i++)
1053             if (text->text.ch[text->gap_position + i] == '\n')
1054               new_line_count++;
1055         }
1056     }
1057  
1058   if (numwcs > 0)
1059     {
1060       insert_text_property (text, font, fore, back, numwcs);
1061    
1062       text->gap_size -= numwcs;
1063       text->gap_position += numwcs;
1064    
1065       if (text->point.index < text->first_line_start_index)
1066         text->first_line_start_index += numwcs;
1067       if (text->point.index < old_editable->selection_start_pos)
1068         old_editable->selection_start_pos += numwcs;
1069       if (text->point.index < old_editable->selection_end_pos)
1070         old_editable->selection_end_pos += numwcs;
1071       /* We'll reset the cursor later anyways if we aren't frozen */
1072       if (text->point.index < text->cursor_mark.index)
1073         text->cursor_mark.index += numwcs;
1074   
1075       advance_mark_n (&text->point, numwcs);
1076   
1077       if (!text->freeze_count && (text->line_start_cache != NULL))
1078         insert_expose (text, old_height, numwcs, new_line_count);
1079     }
1080
1081   if (frozen)
1082     gtk_text_thaw (text);
1083 }
1084
1085 gboolean
1086 gtk_text_backward_delete (GtkText *text,
1087                           guint    nchars)
1088 {
1089   g_return_val_if_fail (GTK_IS_TEXT (text), FALSE);
1090   
1091   if (nchars > text->point.index || nchars <= 0)
1092     return FALSE;
1093   
1094   gtk_text_set_point (text, text->point.index - nchars);
1095   
1096   return gtk_text_forward_delete (text, nchars);
1097 }
1098
1099 gboolean
1100 gtk_text_forward_delete (GtkText *text,
1101                          guint    nchars)
1102 {
1103   guint old_lines, old_height;
1104   GtkOldEditable *old_editable = GTK_OLD_EDITABLE (text);
1105   gboolean frozen = FALSE;
1106   
1107   g_return_val_if_fail (GTK_IS_TEXT (text), FALSE);
1108   
1109   if (text->point.index + nchars > TEXT_LENGTH (text) || nchars <= 0)
1110     return FALSE;
1111   
1112   if (!text->freeze_count && nchars > FREEZE_LENGTH)
1113     {
1114       gtk_text_freeze (text);
1115       frozen = TRUE;
1116     }
1117   
1118   if (!text->freeze_count && text->line_start_cache != NULL)
1119     {
1120       /* We need to undraw the cursor here, since we may later
1121        * delete the cursor's property
1122        */
1123       undraw_cursor (text, FALSE);
1124       find_line_containing_point (text, text->point.index, TRUE);
1125       compute_lines_pixels (text, nchars, &old_lines, &old_height);
1126     }
1127   
1128   /* FIXME, or resizing after deleting will be odd */
1129   if (text->point.index < text->first_line_start_index)
1130     {
1131       if (text->point.index + nchars >= text->first_line_start_index)
1132         {
1133           text->first_line_start_index = text->point.index;
1134           while ((text->first_line_start_index > 0) &&
1135                  (GTK_TEXT_INDEX (text, text->first_line_start_index - 1)
1136                   != LINE_DELIM))
1137             text->first_line_start_index -= 1;
1138           
1139         }
1140       else
1141         text->first_line_start_index -= nchars;
1142     }
1143   
1144   if (text->point.index < old_editable->selection_start_pos)
1145     old_editable->selection_start_pos -= 
1146       MIN(nchars, old_editable->selection_start_pos - text->point.index);
1147   if (text->point.index < old_editable->selection_end_pos)
1148     old_editable->selection_end_pos -= 
1149       MIN(nchars, old_editable->selection_end_pos - text->point.index);
1150   /* We'll reset the cursor later anyways if we aren't frozen */
1151   if (text->point.index < text->cursor_mark.index)
1152     move_mark_n (&text->cursor_mark, 
1153                  -MIN(nchars, text->cursor_mark.index - text->point.index));
1154   
1155   move_gap (text, text->point.index);
1156   
1157   text->gap_size += nchars;
1158   
1159   delete_text_property (text, nchars);
1160   
1161   if (!text->freeze_count && (text->line_start_cache != NULL))
1162     {
1163       delete_expose (text, nchars, old_lines, old_height);
1164       draw_cursor (text, FALSE);
1165     }
1166   
1167   if (frozen)
1168     gtk_text_thaw (text);
1169   
1170   return TRUE;
1171 }
1172
1173 static void
1174 gtk_text_set_position (GtkOldEditable *old_editable,
1175                        gint            position)
1176 {
1177   GtkText *text = (GtkText *) old_editable;
1178
1179   if (position < 0)
1180     position = gtk_text_get_length (text);                                    
1181   
1182   undraw_cursor (text, FALSE);
1183   text->cursor_mark = find_mark (text, position);
1184   find_cursor (text, TRUE);
1185   draw_cursor (text, FALSE);
1186   gtk_editable_select_region (GTK_EDITABLE (old_editable), 0, 0);
1187 }
1188
1189 static gchar *    
1190 gtk_text_get_chars (GtkOldEditable *old_editable,
1191                     gint            start_pos,
1192                     gint            end_pos)
1193 {
1194   GtkText *text;
1195
1196   gchar *retval;
1197   
1198   g_return_val_if_fail (GTK_IS_TEXT (old_editable), NULL);
1199   text = GTK_TEXT (old_editable);
1200   
1201   if (end_pos < 0)
1202     end_pos = TEXT_LENGTH (text);
1203   
1204   if ((start_pos < 0) || 
1205       (end_pos > TEXT_LENGTH (text)) || 
1206       (end_pos < start_pos))
1207     return NULL;
1208   
1209   move_gap (text, TEXT_LENGTH (text));
1210   make_forward_space (text, 1);
1211
1212   if (text->use_wchar)
1213     {
1214       GdkWChar ch;
1215       ch = text->text.wc[end_pos];
1216       text->text.wc[end_pos] = 0;
1217       retval = gdk_wcstombs (text->text.wc + start_pos);
1218       text->text.wc[end_pos] = ch;
1219     }
1220   else
1221     {
1222       guchar ch;
1223       ch = text->text.ch[end_pos];
1224       text->text.ch[end_pos] = 0;
1225       retval = g_strdup (text->text.ch + start_pos);
1226       text->text.ch[end_pos] = ch;
1227     }
1228
1229   return retval;
1230 }
1231
1232
1233 static void
1234 gtk_text_destroy (GtkObject *object)
1235 {
1236   GtkText *text;
1237   
1238   g_return_if_fail (GTK_IS_TEXT (object));
1239   
1240   text = GTK_TEXT (object);
1241
1242   if (text->hadj)
1243     {
1244       gtk_signal_disconnect_by_data (GTK_OBJECT (text->hadj), text);
1245       gtk_object_unref (GTK_OBJECT (text->hadj));
1246       text->hadj = NULL;
1247     }
1248   if (text->vadj)
1249     {
1250       gtk_signal_disconnect_by_data (GTK_OBJECT (text->vadj), text);
1251       gtk_object_unref (GTK_OBJECT (text->vadj));
1252       text->vadj = NULL;
1253     }
1254
1255   if (text->timer)
1256     {
1257       gtk_timeout_remove (text->timer);
1258       text->timer = 0;
1259     }
1260   
1261   GTK_OBJECT_CLASS(parent_class)->destroy (object);
1262 }
1263
1264 static void
1265 gtk_text_finalize (GObject *object)
1266 {
1267   GtkText *text;
1268   GList *tmp_list;
1269   
1270   g_return_if_fail (GTK_IS_TEXT (object));
1271   
1272   text = GTK_TEXT (object);
1273
1274   /* Clean up the internal structures */
1275   if (text->use_wchar)
1276     g_free (text->text.wc);
1277   else
1278     g_free (text->text.ch);
1279   
1280   tmp_list = text->text_properties;
1281   while (tmp_list)
1282     {
1283       destroy_text_property (tmp_list->data);
1284       tmp_list = tmp_list->next;
1285     }
1286
1287   if (text->current_font)
1288     text_font_unref (text->current_font);
1289   
1290   g_list_free (text->text_properties);
1291   
1292   if (text->use_wchar)
1293     {
1294       if (text->scratch_buffer.wc)
1295         g_free (text->scratch_buffer.wc);
1296     }
1297   else
1298     {
1299       if (text->scratch_buffer.ch)
1300         g_free (text->scratch_buffer.ch);
1301     }
1302   
1303   g_list_free (text->tab_stops);
1304   
1305   G_OBJECT_CLASS (parent_class)->finalize (object);
1306 }
1307
1308 static void
1309 gtk_text_realize (GtkWidget *widget)
1310 {
1311   GtkText *text;
1312   GtkOldEditable *old_editable;
1313   GdkWindowAttr attributes;
1314   gint attributes_mask;
1315   
1316   g_return_if_fail (GTK_IS_TEXT (widget));
1317   
1318   text = GTK_TEXT (widget);
1319   old_editable = GTK_OLD_EDITABLE (widget);
1320   GTK_WIDGET_SET_FLAGS (text, GTK_REALIZED);
1321   
1322   attributes.window_type = GDK_WINDOW_CHILD;
1323   attributes.x = widget->allocation.x;
1324   attributes.y = widget->allocation.y;
1325   attributes.width = widget->allocation.width;
1326   attributes.height = widget->allocation.height;
1327   attributes.wclass = GDK_INPUT_OUTPUT;
1328   attributes.visual = gtk_widget_get_visual (widget);
1329   attributes.colormap = gtk_widget_get_colormap (widget);
1330   attributes.event_mask = gtk_widget_get_events (widget);
1331   attributes.event_mask |= (GDK_EXPOSURE_MASK |
1332                             GDK_BUTTON_PRESS_MASK |
1333                             GDK_BUTTON_RELEASE_MASK |
1334                             GDK_BUTTON_MOTION_MASK |
1335                             GDK_ENTER_NOTIFY_MASK |
1336                             GDK_LEAVE_NOTIFY_MASK |
1337                             GDK_KEY_PRESS_MASK);
1338   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1339   
1340   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1341   gdk_window_set_user_data (widget->window, text);
1342   
1343   attributes.x = (widget->style->xthickness + TEXT_BORDER_ROOM);
1344   attributes.y = (widget->style->ythickness + TEXT_BORDER_ROOM);
1345   attributes.width = MAX (1, (gint)widget->allocation.width - (gint)attributes.x * 2);
1346   attributes.height = MAX (1, (gint)widget->allocation.height - (gint)attributes.y * 2);
1347
1348   attributes.cursor = gdk_cursor_new (GDK_XTERM);
1349   attributes_mask |= GDK_WA_CURSOR;
1350   
1351   text->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
1352   gdk_window_set_user_data (text->text_area, text);
1353
1354   gdk_cursor_destroy (attributes.cursor); /* The X server will keep it around as long as necessary */
1355   
1356   widget->style = gtk_style_attach (widget->style, widget->window);
1357   
1358   /* Can't call gtk_style_set_background here because it's handled specially */
1359   gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1360   gdk_window_set_background (text->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1361
1362   if (widget->style->bg_pixmap[GTK_STATE_NORMAL])
1363     text->bg_gc = create_bg_gc (text);
1364   
1365   text->line_wrap_bitmap = gdk_bitmap_create_from_data (text->text_area,
1366                                                         (gchar*) line_wrap_bits,
1367                                                         line_wrap_width,
1368                                                         line_wrap_height);
1369   
1370   text->line_arrow_bitmap = gdk_bitmap_create_from_data (text->text_area,
1371                                                          (gchar*) line_arrow_bits,
1372                                                          line_arrow_width,
1373                                                          line_arrow_height);
1374   
1375   text->gc = gdk_gc_new (text->text_area);
1376   gdk_gc_set_exposures (text->gc, TRUE);
1377   gdk_gc_set_foreground (text->gc, &widget->style->text[GTK_STATE_NORMAL]);
1378   
1379   realize_properties (text);
1380   gdk_window_show (text->text_area);
1381   init_properties (text);
1382
1383   if (old_editable->selection_start_pos != old_editable->selection_end_pos)
1384     gtk_old_editable_claim_selection (old_editable, TRUE, GDK_CURRENT_TIME);
1385   
1386   recompute_geometry (text);
1387 }
1388
1389 static void 
1390 gtk_text_style_set (GtkWidget *widget,
1391                     GtkStyle  *previous_style)
1392 {
1393   GtkText *text = GTK_TEXT (widget);
1394
1395   if (GTK_WIDGET_REALIZED (widget))
1396     {
1397       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1398       gdk_window_set_background (text->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1399       
1400       if (text->bg_gc)
1401         {
1402           gdk_gc_destroy (text->bg_gc);
1403           text->bg_gc = NULL;
1404         }
1405
1406       if (widget->style->bg_pixmap[GTK_STATE_NORMAL])
1407         text->bg_gc = create_bg_gc (text);
1408
1409       recompute_geometry (text);
1410     }
1411
1412   if (text->current_font)
1413     text_font_unref (text->current_font);
1414   text->current_font = get_text_font (gtk_style_get_font (widget->style));
1415 }
1416
1417 static void
1418 gtk_text_state_changed (GtkWidget   *widget,
1419                         GtkStateType previous_state)
1420 {
1421   GtkText *text = GTK_TEXT (widget);
1422   
1423   if (GTK_WIDGET_REALIZED (widget))
1424     {
1425       gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1426       gdk_window_set_background (text->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1427     }
1428 }
1429
1430 static void
1431 gtk_text_unrealize (GtkWidget *widget)
1432 {
1433   GtkText *text;
1434   
1435   g_return_if_fail (GTK_IS_TEXT (widget));
1436   
1437   text = GTK_TEXT (widget);
1438
1439   gdk_window_set_user_data (text->text_area, NULL);
1440   gdk_window_destroy (text->text_area);
1441   text->text_area = NULL;
1442   
1443   gdk_gc_destroy (text->gc);
1444   text->gc = NULL;
1445
1446   if (text->bg_gc)
1447     {
1448       gdk_gc_destroy (text->bg_gc);
1449       text->bg_gc = NULL;
1450     }
1451   
1452   gdk_pixmap_unref (text->line_wrap_bitmap);
1453   gdk_pixmap_unref (text->line_arrow_bitmap);
1454
1455   unrealize_properties (text);
1456
1457   free_cache (text);
1458
1459   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1460     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1461 }
1462
1463 static void
1464 clear_focus_area (GtkText *text, gint area_x, gint area_y, gint area_width, gint area_height)
1465 {
1466   GtkWidget *widget = GTK_WIDGET (text);
1467   GdkGC *gc;
1468  
1469   gint ythick = TEXT_BORDER_ROOM + widget->style->ythickness;
1470   gint xthick = TEXT_BORDER_ROOM + widget->style->xthickness;
1471   
1472   gint width, height;
1473   
1474   if (area_width == 0 || area_height == 0)
1475     return;
1476     
1477   if (widget->style->bg_pixmap[GTK_STATE_NORMAL])
1478     {
1479       gdk_window_get_size (widget->style->bg_pixmap[GTK_STATE_NORMAL], &width, &height);
1480   
1481       gdk_gc_set_ts_origin (text->bg_gc,
1482                             (- text->first_onscreen_hor_pixel + xthick) % width,
1483                             (- text->first_onscreen_ver_pixel + ythick) % height);
1484       
1485        gc = text->bg_gc;
1486     }
1487   else
1488     gc = widget->style->base_gc[widget->state];
1489
1490   gdk_draw_rectangle (GTK_WIDGET (text)->window, gc, TRUE,
1491                       area_x, area_y, area_width, area_height);
1492 }
1493
1494 static void
1495 gtk_text_draw_focus (GtkWidget *widget)
1496 {
1497   GtkText *text;
1498   gint width, height;
1499   gint x, y;
1500   
1501   g_return_if_fail (GTK_IS_TEXT (widget));
1502   
1503   text = GTK_TEXT (widget);
1504   
1505   if (GTK_WIDGET_DRAWABLE (widget))
1506     {
1507       gint ythick = widget->style->ythickness;
1508       gint xthick = widget->style->xthickness;
1509       gint xextra = TEXT_BORDER_ROOM;
1510       gint yextra = TEXT_BORDER_ROOM;
1511       
1512       TDEBUG (("in gtk_text_draw_focus\n"));
1513       
1514       x = 0;
1515       y = 0;
1516       width = widget->allocation.width;
1517       height = widget->allocation.height;
1518       
1519       if (GTK_WIDGET_HAS_FOCUS (widget))
1520         {
1521           x += 1;
1522           y += 1;
1523           width -=  2;
1524           height -= 2;
1525           xextra -= 1;
1526           yextra -= 1;
1527
1528           gtk_paint_focus (widget->style, widget->window,
1529                            NULL, widget, "text",
1530                            0, 0,
1531                            widget->allocation.width - 1,
1532                            widget->allocation.height - 1);
1533         }
1534
1535       gtk_paint_shadow (widget->style, widget->window,
1536                         GTK_STATE_NORMAL, GTK_SHADOW_IN,
1537                         NULL, widget, "text",
1538                         x, y, width, height);
1539
1540       x += xthick; 
1541       y += ythick;
1542       width -= 2 * xthick;
1543       height -= 2 * ythick;
1544       
1545       /* top rect */
1546       clear_focus_area (text, x, y, width, yextra);
1547       /* left rect */
1548       clear_focus_area (text, x, y + yextra, 
1549                         xextra, y + height - 2 * yextra);
1550       /* right rect */
1551       clear_focus_area (text, x + width - xextra, y + yextra, 
1552                         xextra, height - 2 * ythick);
1553       /* bottom rect */
1554       clear_focus_area (text, x, x + height - yextra, width, yextra);
1555     }
1556   else
1557     {
1558       TDEBUG (("in gtk_text_draw_focus (undrawable !!!)\n"));
1559     }
1560 }
1561
1562 static void
1563 gtk_text_size_request (GtkWidget      *widget,
1564                        GtkRequisition *requisition)
1565 {
1566   GdkFont *font;
1567   gint xthickness;
1568   gint ythickness;
1569   gint char_height;
1570   gint char_width;
1571   
1572   g_return_if_fail (GTK_IS_TEXT (widget));
1573   g_return_if_fail (requisition != NULL);
1574   
1575   xthickness = widget->style->xthickness + TEXT_BORDER_ROOM;
1576   ythickness = widget->style->ythickness + TEXT_BORDER_ROOM;
1577
1578   font = gtk_style_get_font (widget->style);
1579   
1580   char_height = MIN_TEXT_HEIGHT_LINES * (font->ascent +
1581                                          font->descent);
1582   
1583   char_width = MIN_TEXT_WIDTH_LINES * (gdk_text_width (font,
1584                                                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
1585                                                        26)
1586                                        / 26);
1587   
1588   requisition->width  = char_width  + xthickness * 2;
1589   requisition->height = char_height + ythickness * 2;
1590 }
1591
1592 static void
1593 gtk_text_size_allocate (GtkWidget     *widget,
1594                         GtkAllocation *allocation)
1595 {
1596   GtkText *text;
1597   GtkOldEditable *old_editable;
1598   
1599   g_return_if_fail (GTK_IS_TEXT (widget));
1600   g_return_if_fail (allocation != NULL);
1601   
1602   text = GTK_TEXT (widget);
1603   old_editable = GTK_OLD_EDITABLE (widget);
1604   
1605   widget->allocation = *allocation;
1606   if (GTK_WIDGET_REALIZED (widget))
1607     {
1608       gdk_window_move_resize (widget->window,
1609                               allocation->x, allocation->y,
1610                               allocation->width, allocation->height);
1611       
1612       gdk_window_move_resize (text->text_area,
1613                               widget->style->xthickness + TEXT_BORDER_ROOM,
1614                               widget->style->ythickness + TEXT_BORDER_ROOM,
1615                               MAX (1, (gint)widget->allocation.width - (gint)(widget->style->xthickness +
1616                                                           (gint)TEXT_BORDER_ROOM) * 2),
1617                               MAX (1, (gint)widget->allocation.height - (gint)(widget->style->ythickness +
1618                                                            (gint)TEXT_BORDER_ROOM) * 2));
1619       
1620       recompute_geometry (text);
1621     }
1622 }
1623
1624 static gint
1625 gtk_text_expose (GtkWidget      *widget,
1626                  GdkEventExpose *event)
1627 {
1628   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
1629   g_return_val_if_fail (event != NULL, FALSE);
1630   
1631   if (event->window == GTK_TEXT (widget)->text_area)
1632     {
1633       TDEBUG (("in gtk_text_expose (expose)\n"));
1634       expose_text (GTK_TEXT (widget), &event->area, TRUE);
1635     }
1636   else if (event->count == 0)
1637     {
1638       TDEBUG (("in gtk_text_expose (focus)\n"));
1639       gtk_text_draw_focus (widget);
1640     }
1641   
1642   return FALSE;
1643 }
1644
1645 static gint
1646 gtk_text_scroll_timeout (gpointer data)
1647 {
1648   GtkText *text;
1649   GdkEventMotion event;
1650   gint x, y;
1651   GdkModifierType mask;
1652   
1653   GDK_THREADS_ENTER ();
1654
1655   text = GTK_TEXT (data);
1656   
1657   text->timer = 0;
1658   gdk_window_get_pointer (text->text_area, &x, &y, &mask);
1659   
1660   if (mask & (GDK_BUTTON1_MASK | GDK_BUTTON3_MASK))
1661     {
1662       event.is_hint = 0;
1663       event.x = x;
1664       event.y = y;
1665       event.state = mask;
1666       
1667       gtk_text_motion_notify (GTK_WIDGET (text), &event);
1668     }
1669
1670   GDK_THREADS_LEAVE ();
1671   
1672   return FALSE;
1673 }
1674
1675 static gint
1676 gtk_text_button_press (GtkWidget      *widget,
1677                        GdkEventButton *event)
1678 {
1679   GtkText *text;
1680   GtkOldEditable *old_editable;
1681   
1682   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
1683   g_return_val_if_fail (event != NULL, FALSE);
1684   
1685   text = GTK_TEXT (widget);
1686   old_editable = GTK_OLD_EDITABLE (widget);
1687   
1688   if (text->button && (event->button != text->button))
1689     return FALSE;
1690   
1691   text->button = event->button;
1692   
1693   if (!GTK_WIDGET_HAS_FOCUS (widget))
1694     gtk_widget_grab_focus (widget);
1695   
1696   if (event->button == 1)
1697     {
1698       switch (event->type)
1699         {
1700         case GDK_BUTTON_PRESS:
1701           gtk_grab_add (widget);
1702           
1703           undraw_cursor (text, FALSE);
1704           find_mouse_cursor (text, (gint)event->x, (gint)event->y);
1705           draw_cursor (text, FALSE);
1706           
1707           /* Set it now, so we display things right. We'll unset it
1708            * later if things don't work out */
1709           old_editable->has_selection = TRUE;
1710           gtk_text_set_selection (GTK_OLD_EDITABLE (text),
1711                                   text->cursor_mark.index,
1712                                   text->cursor_mark.index);
1713           
1714           break;
1715           
1716         case GDK_2BUTTON_PRESS:
1717           gtk_text_select_word (text, event->time);
1718           break;
1719           
1720         case GDK_3BUTTON_PRESS:
1721           gtk_text_select_line (text, event->time);
1722           break;
1723           
1724         default:
1725           break;
1726         }
1727     }
1728   else if (event->type == GDK_BUTTON_PRESS)
1729     {
1730       if ((event->button == 2) && old_editable->editable)
1731         {
1732           if (old_editable->selection_start_pos == old_editable->selection_end_pos ||
1733               old_editable->has_selection)
1734             {
1735               undraw_cursor (text, FALSE);
1736               find_mouse_cursor (text, (gint)event->x, (gint)event->y);
1737               draw_cursor (text, FALSE);
1738               
1739             }
1740           
1741           gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
1742                                  gdk_atom_intern ("UTF8_STRING", FALSE),
1743                                  event->time);
1744         }
1745       else
1746         {
1747           gtk_grab_add (widget);
1748           
1749           undraw_cursor (text, FALSE);
1750           find_mouse_cursor (text, event->x, event->y);
1751           draw_cursor (text, FALSE);
1752           
1753           gtk_text_set_selection (GTK_OLD_EDITABLE (text),
1754                                   text->cursor_mark.index,
1755                                   text->cursor_mark.index);
1756           
1757           old_editable->has_selection = FALSE;
1758           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
1759             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
1760         }
1761     }
1762   
1763   return TRUE;
1764 }
1765
1766 static gint
1767 gtk_text_button_release (GtkWidget      *widget,
1768                          GdkEventButton *event)
1769 {
1770   GtkText *text;
1771   GtkOldEditable *old_editable;
1772   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
1773   g_return_val_if_fail (event != NULL, FALSE);
1774   
1775   text = GTK_TEXT (widget);
1776   
1777   gtk_grab_remove (widget);
1778   
1779   if (text->button != event->button)
1780     return FALSE;
1781   
1782   text->button = 0;
1783   
1784   if (text->timer)
1785     {
1786       gtk_timeout_remove (text->timer);
1787       text->timer = 0;
1788     }
1789   
1790   if (event->button == 1)
1791     {
1792       text = GTK_TEXT (widget);
1793       old_editable = GTK_OLD_EDITABLE (widget);
1794       
1795       gtk_grab_remove (widget);
1796       
1797       old_editable->has_selection = FALSE;
1798       if (old_editable->selection_start_pos != old_editable->selection_end_pos)
1799         {
1800           if (gtk_selection_owner_set (widget,
1801                                        GDK_SELECTION_PRIMARY,
1802                                        event->time))
1803             old_editable->has_selection = TRUE;
1804           else
1805             gtk_text_update_text (old_editable, old_editable->selection_start_pos,
1806                                   old_editable->selection_end_pos);
1807         }
1808       else
1809         {
1810           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
1811             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
1812         }
1813     }
1814   else if (event->button == 3)
1815     {
1816       gtk_grab_remove (widget);
1817     }
1818   
1819   undraw_cursor (text, FALSE);
1820   find_cursor (text, TRUE);
1821   draw_cursor (text, FALSE);
1822   
1823   return TRUE;
1824 }
1825
1826 static gint
1827 gtk_text_motion_notify (GtkWidget      *widget,
1828                         GdkEventMotion *event)
1829 {
1830   GtkText *text;
1831   gint x, y;
1832   gint height;
1833   GdkModifierType mask;
1834   
1835   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
1836   g_return_val_if_fail (event != NULL, FALSE);
1837   
1838   text = GTK_TEXT (widget);
1839   
1840   x = event->x;
1841   y = event->y;
1842   mask = event->state;
1843   if (event->is_hint || (text->text_area != event->window))
1844     {
1845       gdk_window_get_pointer (text->text_area, &x, &y, &mask);
1846     }
1847   
1848   if ((text->button == 0) ||
1849       !(mask & (GDK_BUTTON1_MASK | GDK_BUTTON3_MASK)))
1850     return FALSE;
1851   
1852   gdk_window_get_size (text->text_area, NULL, &height);
1853   
1854   if ((y < 0) || (y > height))
1855     {
1856       if (text->timer == 0)
1857         {
1858           text->timer = gtk_timeout_add (SCROLL_TIME, 
1859                                          gtk_text_scroll_timeout,
1860                                          text);
1861           
1862           if (y < 0)
1863             scroll_int (text, y/2);
1864           else
1865             scroll_int (text, (y - height)/2);
1866         }
1867       else
1868         return FALSE;
1869     }
1870   
1871   undraw_cursor (GTK_TEXT (widget), FALSE);
1872   find_mouse_cursor (GTK_TEXT (widget), x, y);
1873   draw_cursor (GTK_TEXT (widget), FALSE);
1874   
1875   gtk_text_set_selection (GTK_OLD_EDITABLE (text), 
1876                           GTK_OLD_EDITABLE (text)->selection_start_pos,
1877                           text->cursor_mark.index);
1878   
1879   return FALSE;
1880 }
1881
1882 static void 
1883 gtk_text_insert_text    (GtkEditable       *editable,
1884                          const gchar       *new_text,
1885                          gint               new_text_length,
1886                          gint              *position)
1887 {
1888   GtkText *text = GTK_TEXT (editable);
1889   GdkFont *font;
1890   GdkColor *fore, *back;
1891
1892   TextProperty *property;
1893
1894   gtk_text_set_point (text, *position);
1895
1896   property = MARK_CURRENT_PROPERTY (&text->point);
1897   font = property->flags & PROPERTY_FONT ? property->font->gdk_font : NULL; 
1898   fore = property->flags & PROPERTY_FOREGROUND ? &property->fore_color : NULL; 
1899   back = property->flags & PROPERTY_BACKGROUND ? &property->back_color : NULL; 
1900   
1901   gtk_text_insert (text, font, fore, back, new_text, new_text_length);
1902
1903   *position = text->point.index;
1904 }
1905
1906 static void 
1907 gtk_text_delete_text    (GtkEditable       *editable,
1908                          gint               start_pos,
1909                          gint               end_pos)
1910 {
1911   GtkText *text;
1912   
1913   g_return_if_fail (start_pos >= 0);
1914   
1915   text = GTK_TEXT (editable);
1916   
1917   gtk_text_set_point (text, start_pos);
1918   if (end_pos < 0)
1919     end_pos = TEXT_LENGTH (text);
1920   
1921   if (end_pos > start_pos)
1922     gtk_text_forward_delete (text, end_pos - start_pos);
1923 }
1924
1925 static gint
1926 gtk_text_key_press (GtkWidget   *widget,
1927                     GdkEventKey *event)
1928 {
1929   GtkText *text;
1930   GtkOldEditable *old_editable;
1931   gchar key;
1932   gint return_val;
1933   gint position;
1934   
1935   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
1936   g_return_val_if_fail (event != NULL, FALSE);
1937   
1938   text = GTK_TEXT (widget);
1939   old_editable = GTK_OLD_EDITABLE (widget);
1940   
1941   key = event->keyval;
1942   return_val = TRUE;
1943   
1944   if ((GTK_OLD_EDITABLE(text)->editable == FALSE))
1945     {
1946       switch (event->keyval)
1947         {
1948         case GDK_Home:
1949         case GDK_KP_Home:
1950           if (event->state & GDK_CONTROL_MASK)
1951             scroll_int (text, -text->vadj->value);
1952           else
1953             return_val = FALSE;
1954           break;
1955         case GDK_End:
1956         case GDK_KP_End:
1957           if (event->state & GDK_CONTROL_MASK)
1958             scroll_int (text, +text->vadj->upper); 
1959           else
1960             return_val = FALSE;
1961           break;
1962         case GDK_KP_Page_Up:
1963         case GDK_Page_Up:   scroll_int (text, -text->vadj->page_increment); break;
1964         case GDK_KP_Page_Down:
1965         case GDK_Page_Down: scroll_int (text, +text->vadj->page_increment); break;
1966         case GDK_KP_Up:
1967         case GDK_Up:        scroll_int (text, -KEY_SCROLL_PIXELS); break;
1968         case GDK_KP_Down:
1969         case GDK_Down:      scroll_int (text, +KEY_SCROLL_PIXELS); break;
1970         case GDK_Return:
1971         case GDK_KP_Enter:
1972           if (event->state & GDK_CONTROL_MASK)
1973             gtk_signal_emit_by_name (GTK_OBJECT (text), "activate");
1974           else
1975             return_val = FALSE;
1976           break;
1977         default:
1978           return_val = FALSE;
1979           break;
1980         }
1981     }
1982   else
1983     {
1984       gint extend_selection;
1985       gint extend_start;
1986       guint initial_pos = old_editable->current_pos;
1987       
1988       text->point = find_mark (text, text->cursor_mark.index);
1989       
1990       extend_selection = event->state & GDK_SHIFT_MASK;
1991       extend_start = FALSE;
1992       
1993       if (extend_selection)
1994         {
1995           old_editable->has_selection = TRUE;
1996           
1997           if (old_editable->selection_start_pos == old_editable->selection_end_pos)
1998             {
1999               old_editable->selection_start_pos = text->point.index;
2000               old_editable->selection_end_pos = text->point.index;
2001             }
2002           
2003           extend_start = (text->point.index == old_editable->selection_start_pos);
2004         }
2005       
2006       switch (event->keyval)
2007         {
2008         case GDK_KP_Home:
2009         case GDK_Home:
2010           if (event->state & GDK_CONTROL_MASK)
2011             move_cursor_buffer_ver (text, -1);
2012           else
2013             gtk_text_move_beginning_of_line (text);
2014           break;
2015         case GDK_KP_End:
2016         case GDK_End:
2017           if (event->state & GDK_CONTROL_MASK)
2018             move_cursor_buffer_ver (text, +1);
2019           else
2020             gtk_text_move_end_of_line (text);
2021           break;
2022         case GDK_KP_Page_Up:
2023         case GDK_Page_Up:   move_cursor_page_ver (text, -1); break;
2024         case GDK_KP_Page_Down:
2025         case GDK_Page_Down: move_cursor_page_ver (text, +1); break;
2026           /* CUA has Ctrl-Up/Ctrl-Down as paragraph up down */
2027         case GDK_KP_Up:
2028         case GDK_Up:        move_cursor_ver (text, -1); break;
2029         case GDK_KP_Down:
2030         case GDK_Down:      move_cursor_ver (text, +1); break;
2031         case GDK_KP_Left:
2032         case GDK_Left:
2033           if (event->state & GDK_CONTROL_MASK)
2034             gtk_text_move_backward_word (text);
2035           else
2036             move_cursor_hor (text, -1); 
2037           break;
2038         case GDK_KP_Right:
2039         case GDK_Right:     
2040           if (event->state & GDK_CONTROL_MASK)
2041             gtk_text_move_forward_word (text);
2042           else
2043             move_cursor_hor (text, +1); 
2044           break;
2045           
2046         case GDK_BackSpace:
2047           if (event->state & GDK_CONTROL_MASK)
2048             gtk_text_delete_backward_word (text);
2049           else
2050             gtk_text_delete_backward_character (text);
2051           break;
2052         case GDK_Clear:
2053           gtk_text_delete_line (text);
2054           break;
2055         case GDK_KP_Insert:
2056         case GDK_Insert:
2057           if (event->state & GDK_SHIFT_MASK)
2058             {
2059               extend_selection = FALSE;
2060               gtk_editable_paste_clipboard (GTK_EDITABLE (old_editable));
2061             }
2062           else if (event->state & GDK_CONTROL_MASK)
2063             {
2064               gtk_editable_copy_clipboard (GTK_EDITABLE (old_editable));
2065             }
2066           else
2067             {
2068               /* gtk_toggle_insert(text) -- IMPLEMENT */
2069             }
2070           break;
2071         case GDK_Delete:
2072         case GDK_KP_Delete:
2073           if (event->state & GDK_CONTROL_MASK)
2074             gtk_text_delete_forward_word (text);
2075           else if (event->state & GDK_SHIFT_MASK)
2076             {
2077               extend_selection = FALSE;
2078               gtk_editable_cut_clipboard (GTK_EDITABLE (old_editable));
2079             }
2080           else
2081             gtk_text_delete_forward_character (text);
2082           break;
2083         case GDK_Tab:
2084         case GDK_ISO_Left_Tab:
2085         case GDK_KP_Tab:
2086           position = text->point.index;
2087           gtk_editable_insert_text (GTK_EDITABLE (old_editable), "\t", 1, &position);
2088           break;
2089         case GDK_KP_Enter:
2090         case GDK_Return:
2091           if (event->state & GDK_CONTROL_MASK)
2092             gtk_signal_emit_by_name (GTK_OBJECT (text), "activate");
2093           else
2094             {
2095               position = text->point.index;
2096               gtk_editable_insert_text (GTK_EDITABLE (old_editable), "\n", 1, &position);
2097             }
2098           break;
2099         case GDK_Escape:
2100           /* Don't insert literally */
2101           return_val = FALSE;
2102           break;
2103           
2104         default:
2105           return_val = FALSE;
2106           
2107           if (event->state & GDK_CONTROL_MASK)
2108             {
2109               if ((key >= 'A') && (key <= 'Z'))
2110                 key -= 'A' - 'a';
2111               
2112               if ((key >= 'a') && (key <= 'z') && control_keys[(int) (key - 'a')])
2113                 {
2114                   (* control_keys[(int) (key - 'a')]) (old_editable, event->time);
2115                   return_val = TRUE;
2116                 }
2117               
2118               break;
2119             }
2120           else if (event->state & GDK_MOD1_MASK)
2121             {
2122               if ((key >= 'A') && (key <= 'Z'))
2123                 key -= 'A' - 'a';
2124               
2125               if ((key >= 'a') && (key <= 'z') && alt_keys[(int) (key - 'a')])
2126                 {
2127                   (* alt_keys[(int) (key - 'a')]) (old_editable, event->time);
2128                   return_val = TRUE;
2129                 }
2130               
2131               break;
2132             }
2133           else if (event->length > 0)
2134             {
2135               extend_selection = FALSE;
2136               
2137               gtk_editable_delete_selection (GTK_EDITABLE (old_editable));
2138               position = text->point.index;
2139               gtk_editable_insert_text (GTK_EDITABLE (old_editable), event->string, event->length, &position);
2140               
2141               return_val = TRUE;
2142             }
2143         }
2144       
2145       if (return_val && (old_editable->current_pos != initial_pos))
2146         {
2147           if (extend_selection)
2148             {
2149               if (old_editable->current_pos < old_editable->selection_start_pos)
2150                 gtk_text_set_selection (old_editable, old_editable->current_pos,
2151                                         old_editable->selection_end_pos);
2152               else if (old_editable->current_pos > old_editable->selection_end_pos)
2153                 gtk_text_set_selection (old_editable, old_editable->selection_start_pos,
2154                                         old_editable->current_pos);
2155               else
2156                 {
2157                   if (extend_start)
2158                     gtk_text_set_selection (old_editable, old_editable->current_pos,
2159                                             old_editable->selection_end_pos);
2160                   else
2161                     gtk_text_set_selection (old_editable, old_editable->selection_start_pos,
2162                                             old_editable->current_pos);
2163                 }
2164             }
2165           else
2166             gtk_text_set_selection (old_editable, 0, 0);
2167           
2168           gtk_old_editable_claim_selection (old_editable,
2169                                             old_editable->selection_start_pos != old_editable->selection_end_pos,
2170                                             event->time);
2171         }
2172     }
2173   
2174   return return_val;
2175 }
2176
2177 static gint
2178 gtk_text_focus_in (GtkWidget     *widget,
2179                    GdkEventFocus *event)
2180 {
2181   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
2182   g_return_val_if_fail (event != NULL, FALSE);
2183   
2184   TDEBUG (("in gtk_text_focus_in\n"));
2185   
2186   return (* GTK_WIDGET_CLASS (parent_class)->focus_in_event) (widget, event);
2187 }
2188
2189 static gint
2190 gtk_text_focus_out (GtkWidget     *widget,
2191                     GdkEventFocus *event)
2192 {
2193   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
2194   g_return_val_if_fail (event != NULL, FALSE);
2195   
2196   TDEBUG (("in gtk_text_focus_out\n"));
2197   
2198   return (* GTK_WIDGET_CLASS (parent_class)->focus_out_event) (widget, event);
2199 }
2200
2201 static void
2202 gtk_text_adjustment (GtkAdjustment *adjustment,
2203                      GtkText       *text)
2204 {
2205   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
2206   g_return_if_fail (GTK_IS_TEXT (text));
2207   
2208   /* Just ignore it if we haven't been size-allocated and realized yet */
2209   if (text->line_start_cache == NULL) 
2210     return;
2211   
2212   if (adjustment == text->hadj)
2213     {
2214       g_warning ("horizontal scrolling not implemented");
2215     }
2216   else
2217     {
2218       gint diff = ((gint)adjustment->value) - text->last_ver_value;
2219       
2220       if (diff != 0)
2221         {
2222           undraw_cursor (text, FALSE);
2223           
2224           if (diff > 0)
2225             scroll_down (text, diff);
2226           else /* if (diff < 0) */
2227             scroll_up (text, diff);
2228           
2229           draw_cursor (text, FALSE);
2230           
2231           text->last_ver_value = adjustment->value;
2232         }
2233     }
2234 }
2235
2236 static void
2237 gtk_text_adjustment_destroyed (GtkAdjustment *adjustment,
2238                                GtkText       *text)
2239 {
2240   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
2241   g_return_if_fail (GTK_IS_TEXT (text));
2242
2243   if (adjustment == text->hadj)
2244     gtk_text_set_adjustments (text, NULL, text->vadj);
2245   if (adjustment == text->vadj)
2246     gtk_text_set_adjustments (text, text->hadj, NULL);
2247 }
2248
2249
2250 static GtkPropertyMark
2251 find_this_line_start_mark (GtkText* text, guint point_position, const GtkPropertyMark* near)
2252 {
2253   GtkPropertyMark mark;
2254   
2255   mark = find_mark_near (text, point_position, near);
2256   
2257   while (mark.index > 0 &&
2258          GTK_TEXT_INDEX (text, mark.index - 1) != LINE_DELIM)
2259     decrement_mark (&mark);
2260   
2261   return mark;
2262 }
2263
2264 static void
2265 init_tab_cont (GtkText* text, PrevTabCont* tab_cont)
2266 {
2267   tab_cont->pixel_offset          = 0;
2268   tab_cont->tab_start.tab_stops   = text->tab_stops;
2269   tab_cont->tab_start.to_next_tab = (gulong) text->tab_stops->data;
2270   
2271   if (!tab_cont->tab_start.to_next_tab)
2272     tab_cont->tab_start.to_next_tab = text->default_tab_width;
2273 }
2274
2275 static void
2276 line_params_iterate (GtkText* text,
2277                      const GtkPropertyMark* mark0,
2278                      const PrevTabCont* tab_mark0,
2279                      gint8 alloc,
2280                      void* data,
2281                      LineIteratorFunction iter)
2282      /* mark0 MUST be a real line start.  if ALLOC, allocate line params
2283       * from a mem chunk.  DATA is passed to ITER_CALL, which is called
2284       * for each line following MARK, iteration continues unless ITER_CALL
2285       * returns TRUE. */
2286 {
2287   GtkPropertyMark mark = *mark0;
2288   PrevTabCont  tab_conts[2];
2289   LineParams   *lp, lpbuf;
2290   gint         tab_cont_index = 0;
2291   
2292   if (tab_mark0)
2293     tab_conts[0] = *tab_mark0;
2294   else
2295     init_tab_cont (text, tab_conts);
2296   
2297   for (;;)
2298     {
2299       if (alloc)
2300         lp = g_chunk_new (LineParams, params_mem_chunk);
2301       else
2302         lp = &lpbuf;
2303       
2304       *lp = find_line_params (text, &mark, tab_conts + tab_cont_index,
2305                               tab_conts + (tab_cont_index + 1) % 2);
2306       
2307       if ((*iter) (text, lp, data))
2308         return;
2309       
2310       if (LAST_INDEX (text, lp->end))
2311         break;
2312       
2313       mark = lp->end;
2314       advance_mark (&mark);
2315       tab_cont_index = (tab_cont_index + 1) % 2;
2316     }
2317 }
2318
2319 static gint
2320 fetch_lines_iterator (GtkText* text, LineParams* lp, void* data)
2321 {
2322   FetchLinesData *fldata = (FetchLinesData*) data;
2323   
2324   fldata->new_lines = g_list_prepend (fldata->new_lines, lp);
2325   
2326   switch (fldata->fl_type)
2327     {
2328     case FetchLinesCount:
2329       if (!text->line_wrap || !lp->wraps)
2330         fldata->data += 1;
2331       
2332       if (fldata->data >= fldata->data_max)
2333         return TRUE;
2334       
2335       break;
2336     case FetchLinesPixels:
2337       
2338       fldata->data += LINE_HEIGHT(*lp);
2339       
2340       if (fldata->data >= fldata->data_max)
2341         return TRUE;
2342       
2343       break;
2344     }
2345   
2346   return FALSE;
2347 }
2348
2349 static GList*
2350 fetch_lines (GtkText* text,
2351              const GtkPropertyMark* mark0,
2352              const PrevTabCont* tab_cont0,
2353              FLType fl_type,
2354              gint data)
2355 {
2356   FetchLinesData fl_data;
2357   
2358   fl_data.new_lines = NULL;
2359   fl_data.data      = 0;
2360   fl_data.data_max  = data;
2361   fl_data.fl_type   = fl_type;
2362   
2363   line_params_iterate (text, mark0, tab_cont0, TRUE, &fl_data, fetch_lines_iterator);
2364   
2365   return g_list_reverse (fl_data.new_lines);
2366 }
2367
2368 static void
2369 fetch_lines_backward (GtkText* text)
2370 {
2371   GList* new_lines = NULL, *new_line_start;
2372   GtkPropertyMark mark;
2373   
2374   if (CACHE_DATA(text->line_start_cache).start.index == 0)
2375     return;
2376   
2377   mark = find_this_line_start_mark (text,
2378                                     CACHE_DATA(text->line_start_cache).start.index - 1,
2379                                     &CACHE_DATA(text->line_start_cache).start);
2380   
2381   new_line_start = new_lines = fetch_lines (text, &mark, NULL, FetchLinesCount, 1);
2382   
2383   while (new_line_start->next)
2384     new_line_start = new_line_start->next;
2385   
2386   new_line_start->next = text->line_start_cache;
2387   text->line_start_cache->prev = new_line_start;
2388 }
2389
2390 static void
2391 fetch_lines_forward (GtkText* text, gint line_count)
2392 {
2393   GtkPropertyMark mark;
2394   GList* line = text->line_start_cache;
2395   
2396   while(line->next)
2397     line = line->next;
2398   
2399   mark = CACHE_DATA(line).end;
2400   
2401   if (LAST_INDEX (text, mark))
2402     return;
2403   
2404   advance_mark(&mark);
2405   
2406   line->next = fetch_lines (text, &mark, &CACHE_DATA(line).tab_cont_next, FetchLinesCount, line_count);
2407   
2408   if (line->next)
2409     line->next->prev = line;
2410 }
2411
2412 /* Compute the number of lines, and vertical pixels for n characters
2413  * starting from the point 
2414  */
2415 static void
2416 compute_lines_pixels (GtkText* text, guint char_count,
2417                       guint *lines, guint *pixels)
2418 {
2419   GList *line = text->current_line;
2420   gint chars_left = char_count;
2421   
2422   *lines = 0;
2423   *pixels = 0;
2424   
2425   /* If chars_left == 0, that means we're joining two lines in a
2426    * deletion, so add in the values for the next line as well 
2427    */
2428   for (; line && chars_left >= 0; line = line->next)
2429     {
2430       *pixels += LINE_HEIGHT(CACHE_DATA(line));
2431       
2432       if (line == text->current_line)
2433         chars_left -= CACHE_DATA(line).end.index - text->point.index + 1;
2434       else
2435         chars_left -= CACHE_DATA(line).end.index - CACHE_DATA(line).start.index + 1;
2436       
2437       if (!text->line_wrap || !CACHE_DATA(line).wraps)
2438         *lines += 1;
2439       else
2440         if (chars_left < 0)
2441           chars_left = 0;       /* force another loop */
2442       
2443       if (!line->next)
2444         fetch_lines_forward (text, 1);
2445     }
2446 }
2447
2448 static gint
2449 total_line_height (GtkText* text, GList* line, gint line_count)
2450 {
2451   gint height = 0;
2452   
2453   for (; line && line_count > 0; line = line->next)
2454     {
2455       height += LINE_HEIGHT(CACHE_DATA(line));
2456       
2457       if (!text->line_wrap || !CACHE_DATA(line).wraps)
2458         line_count -= 1;
2459       
2460       if (!line->next)
2461         fetch_lines_forward (text, line_count);
2462     }
2463   
2464   return height;
2465 }
2466
2467 static void
2468 swap_lines (GtkText* text, GList* old, GList* new, guint old_line_count)
2469 {
2470   if (old == text->line_start_cache)
2471     {
2472       GList* last;
2473       
2474       for (; old_line_count > 0; old_line_count -= 1)
2475         {
2476           while (text->line_start_cache &&
2477                  text->line_wrap &&
2478                  CACHE_DATA(text->line_start_cache).wraps)
2479             remove_cache_line(text, text->line_start_cache);
2480           
2481           remove_cache_line(text, text->line_start_cache);
2482         }
2483       
2484       last = g_list_last (new);
2485       
2486       last->next = text->line_start_cache;
2487       
2488       if (text->line_start_cache)
2489         text->line_start_cache->prev = last;
2490       
2491       text->line_start_cache = new;
2492     }
2493   else
2494     {
2495       GList *last;
2496       
2497       g_assert (old->prev);
2498       
2499       last = old->prev;
2500       
2501       for (; old_line_count > 0; old_line_count -= 1)
2502         {
2503           while (old && text->line_wrap && CACHE_DATA(old).wraps)
2504             old = remove_cache_line (text, old);
2505           
2506           old = remove_cache_line (text, old);
2507         }
2508       
2509       last->next = new;
2510       new->prev = last;
2511       
2512       last = g_list_last (new);
2513       
2514       last->next = old;
2515       
2516       if (old)
2517         old->prev = last;
2518     }
2519 }
2520
2521 static void
2522 correct_cache_delete (GtkText* text, gint nchars, gint lines)
2523 {
2524   GList* cache = text->current_line;
2525   gint i;
2526   
2527   for (i = 0; cache && i < lines; i += 1, cache = cache->next)
2528     /* nothing */;
2529   
2530   for (; cache; cache = cache->next)
2531     {
2532       GtkPropertyMark *start = &CACHE_DATA(cache).start;
2533       GtkPropertyMark *end = &CACHE_DATA(cache).end;
2534       
2535       start->index -= nchars;
2536       end->index -= nchars;
2537       
2538       if (LAST_INDEX (text, text->point) &&
2539           start->index == text->point.index)
2540         *start = text->point;
2541       else if (start->property == text->point.property)
2542         start->offset = start->index - (text->point.index - text->point.offset);
2543       
2544       if (LAST_INDEX (text, text->point) &&
2545           end->index == text->point.index)
2546         *end = text->point;
2547       if (end->property == text->point.property)
2548         end->offset = end->index - (text->point.index - text->point.offset);
2549       
2550       /*TEXT_ASSERT_MARK(text, start, "start");*/
2551       /*TEXT_ASSERT_MARK(text, end, "end");*/
2552     }
2553 }
2554
2555 static void
2556 delete_expose (GtkText* text, guint nchars, guint old_lines, guint old_pixels)
2557 {
2558   GtkWidget *widget = GTK_WIDGET (text);
2559   
2560   gint pixel_height;
2561   guint new_pixels = 0;
2562   GdkRectangle rect;
2563   GList* new_line = NULL;
2564   gint width, height;
2565   
2566   text->cursor_virtual_x = 0;
2567   
2568   correct_cache_delete (text, nchars, old_lines);
2569   
2570   pixel_height = pixel_height_of(text, text->current_line) -
2571     LINE_HEIGHT(CACHE_DATA(text->current_line));
2572   
2573   if (CACHE_DATA(text->current_line).start.index == text->point.index)
2574     CACHE_DATA(text->current_line).start = text->point;
2575   
2576   new_line = fetch_lines (text,
2577                           &CACHE_DATA(text->current_line).start,
2578                           &CACHE_DATA(text->current_line).tab_cont,
2579                           FetchLinesCount,
2580                           1);
2581   
2582   swap_lines (text, text->current_line, new_line, old_lines);
2583   
2584   text->current_line = new_line;
2585   
2586   new_pixels = total_line_height (text, new_line, 1);
2587   
2588   gdk_window_get_size (text->text_area, &width, &height);
2589   
2590   if (old_pixels != new_pixels)
2591     {
2592       if (!widget->style->bg_pixmap[GTK_STATE_NORMAL])
2593         {
2594           gdk_draw_pixmap (text->text_area,
2595                            text->gc,
2596                            text->text_area,
2597                            0,
2598                            pixel_height + old_pixels,
2599                            0,
2600                            pixel_height + new_pixels,
2601                            width,
2602                            height);
2603         }
2604       text->vadj->upper += new_pixels;
2605       text->vadj->upper -= old_pixels;
2606       adjust_adj (text, text->vadj);
2607     }
2608   
2609   rect.x = 0;
2610   rect.y = pixel_height;
2611   rect.width = width;
2612   rect.height = new_pixels;
2613   
2614   expose_text (text, &rect, FALSE);
2615   gtk_text_draw_focus ( (GtkWidget *) text);
2616   
2617   text->cursor_mark = text->point;
2618   
2619   find_cursor (text, TRUE);
2620   
2621   if (old_pixels != new_pixels)
2622     {
2623       if (widget->style->bg_pixmap[GTK_STATE_NORMAL])
2624         {
2625           rect.x = 0;
2626           rect.y = pixel_height + new_pixels;
2627           rect.width = width;
2628           rect.height = height - rect.y;
2629           
2630           expose_text (text, &rect, FALSE);
2631         }
2632       else
2633         process_exposes (text);
2634     }
2635   
2636   TEXT_ASSERT (text);
2637   TEXT_SHOW(text);
2638 }
2639
2640 /* note, the point has already been moved forward */
2641 static void
2642 correct_cache_insert (GtkText* text, gint nchars)
2643 {
2644   GList *cache;
2645   GtkPropertyMark *start;
2646   GtkPropertyMark *end;
2647   gboolean was_split = FALSE;
2648   
2649   /* We need to distinguish whether the property was split in the
2650    * insert or not, so we check if the point (which points after
2651    * the insertion here), points to the same character as the
2652    * point before. Ugh.
2653    */
2654   if (nchars > 0)
2655     {
2656       GtkPropertyMark tmp_mark = text->point;
2657       move_mark_n (&tmp_mark, -1);
2658       
2659       if (tmp_mark.property != text->point.property)
2660         was_split = TRUE;
2661     }
2662   
2663   /* If we inserted a property exactly at the beginning of the
2664    * line, we have to correct here, or fetch_lines will
2665    * fetch junk.
2666    */
2667   start = &CACHE_DATA(text->current_line).start;
2668
2669   /* Check if if we split exactly at the beginning of the line:
2670    * (was_split won't be set if we are inserting at the end of the text, 
2671    *  so we don't check)
2672    */
2673   if (start->offset ==  MARK_CURRENT_PROPERTY (start)->length)
2674     SET_PROPERTY_MARK (start, start->property->next, 0);
2675   /* Check if we inserted a property at the beginning of the text: */
2676   else if (was_split &&
2677            (start->property == text->point.property) &&
2678            (start->index == text->point.index - nchars))
2679     SET_PROPERTY_MARK (start, start->property->prev, 0);
2680
2681   /* Now correct the offsets, and check for start or end marks that
2682    * are after the point, yet point to a property before the point's
2683    * property. This indicates that they are meant to point to the
2684    * second half of a property we split in insert_text_property(), so
2685    * we fix them up that way.  
2686    */
2687   cache = text->current_line->next;
2688   
2689   for (; cache; cache = cache->next)
2690     {
2691       start = &CACHE_DATA(cache).start;
2692       end = &CACHE_DATA(cache).end;
2693       
2694       if (LAST_INDEX (text, text->point) &&
2695           start->index == text->point.index)
2696         *start = text->point;
2697       else if (start->index >= text->point.index - nchars)
2698         {
2699           if (!was_split && start->property == text->point.property)
2700             move_mark_n(start, nchars);
2701           else
2702             {
2703               if (start->property->next &&
2704                   (start->property->next->next == text->point.property))
2705                 {
2706                   g_assert (start->offset >=  MARK_CURRENT_PROPERTY (start)->length);
2707                   start->offset -= MARK_CURRENT_PROPERTY (start)->length;
2708                   start->property = text->point.property;
2709                 }
2710               start->index += nchars;
2711             }
2712         }
2713       
2714       if (LAST_INDEX (text, text->point) &&
2715           end->index == text->point.index)
2716         *end = text->point;
2717       if (end->index >= text->point.index - nchars)
2718         {
2719           if (!was_split && end->property == text->point.property)
2720             move_mark_n(end, nchars);
2721           else 
2722             {
2723               if (end->property->next &&
2724                   (end->property->next->next == text->point.property))
2725                 {
2726                   g_assert (end->offset >=  MARK_CURRENT_PROPERTY (end)->length);
2727                   end->offset -= MARK_CURRENT_PROPERTY (end)->length;
2728                   end->property = text->point.property;
2729                 }
2730               end->index += nchars;
2731             }
2732         }
2733       
2734       /*TEXT_ASSERT_MARK(text, start, "start");*/
2735       /*TEXT_ASSERT_MARK(text, end, "end");*/
2736     }
2737 }
2738
2739
2740 static void
2741 insert_expose (GtkText* text, guint old_pixels, gint nchars,
2742                guint new_line_count)
2743 {
2744   GtkWidget *widget = GTK_WIDGET (text);
2745   
2746   gint pixel_height;
2747   guint new_pixels = 0;
2748   GdkRectangle rect;
2749   GList* new_lines = NULL;
2750   gint width, height;
2751   
2752   text->cursor_virtual_x = 0;
2753   
2754   undraw_cursor (text, FALSE);
2755   
2756   correct_cache_insert (text, nchars);
2757   
2758   TEXT_SHOW_ADJ (text, text->vadj, "vadj");
2759   
2760   pixel_height = pixel_height_of(text, text->current_line) -
2761     LINE_HEIGHT(CACHE_DATA(text->current_line));
2762   
2763   new_lines = fetch_lines (text,
2764                            &CACHE_DATA(text->current_line).start,
2765                            &CACHE_DATA(text->current_line).tab_cont,
2766                            FetchLinesCount,
2767                            new_line_count);
2768   
2769   swap_lines (text, text->current_line, new_lines, 1);
2770   
2771   text->current_line = new_lines;
2772   
2773   new_pixels = total_line_height (text, new_lines, new_line_count);
2774   
2775   gdk_window_get_size (text->text_area, &width, &height);
2776   
2777   if (old_pixels != new_pixels)
2778     {
2779       if (!widget->style->bg_pixmap[GTK_STATE_NORMAL])
2780         {
2781           gdk_draw_pixmap (text->text_area,
2782                            text->gc,
2783                            text->text_area,
2784                            0,
2785                            pixel_height + old_pixels,
2786                            0,
2787                            pixel_height + new_pixels,
2788                            width,
2789                            height + (old_pixels - new_pixels) - pixel_height);
2790           
2791         }
2792       text->vadj->upper += new_pixels;
2793       text->vadj->upper -= old_pixels;
2794       adjust_adj (text, text->vadj);
2795     }
2796   
2797   rect.x = 0;
2798   rect.y = pixel_height;
2799   rect.width = width;
2800   rect.height = new_pixels;
2801   
2802   expose_text (text, &rect, FALSE);
2803   gtk_text_draw_focus ( (GtkWidget *) text);
2804   
2805   text->cursor_mark = text->point;
2806   
2807   find_cursor (text, TRUE);
2808   
2809   draw_cursor (text, FALSE);
2810   
2811   if (old_pixels != new_pixels)
2812     {
2813       if (widget->style->bg_pixmap[GTK_STATE_NORMAL])
2814         {
2815           rect.x = 0;
2816           rect.y = pixel_height + new_pixels;
2817           rect.width = width;
2818           rect.height = height - rect.y;
2819           
2820           expose_text (text, &rect, FALSE);
2821         }
2822       else
2823         process_exposes (text);
2824     }
2825   
2826   TEXT_SHOW_ADJ (text, text->vadj, "vadj");
2827   TEXT_ASSERT (text);
2828   TEXT_SHOW(text);
2829 }
2830
2831 /* Text property functions */
2832
2833 static guint
2834 font_hash (gconstpointer font)
2835 {
2836   return gdk_font_id ((const GdkFont*) font);
2837 }
2838
2839 static GHashTable *font_cache_table = NULL;
2840
2841 static GtkTextFont*
2842 get_text_font (GdkFont* gfont)
2843 {
2844   GtkTextFont* tf;
2845   gint i;
2846   
2847   if (!font_cache_table)
2848     font_cache_table = g_hash_table_new (font_hash, (GEqualFunc) gdk_font_equal);
2849   
2850   tf = g_hash_table_lookup (font_cache_table, gfont);
2851   
2852   if (tf)
2853     {
2854       tf->ref_count++;
2855       return tf;
2856     }
2857
2858   tf = g_new (GtkTextFont, 1);
2859   tf->ref_count = 1;
2860
2861   tf->gdk_font = gfont;
2862   gdk_font_ref (gfont);
2863   
2864   for(i = 0; i < 256; i += 1)
2865     tf->char_widths[i] = gdk_char_width (gfont, (char)i);
2866   
2867   g_hash_table_insert (font_cache_table, gfont, tf);
2868   
2869   return tf;
2870 }
2871
2872 static void
2873 text_font_unref (GtkTextFont *text_font)
2874 {
2875   text_font->ref_count--;
2876   if (text_font->ref_count == 0)
2877     {
2878       g_hash_table_remove (font_cache_table, text_font->gdk_font);
2879       gdk_font_unref (text_font->gdk_font);
2880       g_free (text_font);
2881     }
2882 }
2883
2884 static gint
2885 text_properties_equal (TextProperty* prop, GdkFont* font, GdkColor *fore, GdkColor *back)
2886 {
2887   if (prop->flags & PROPERTY_FONT)
2888     {
2889       gboolean retval;
2890       GtkTextFont *text_font;
2891
2892       if (!font)
2893         return FALSE;
2894
2895       text_font = get_text_font (font);
2896
2897       retval = (prop->font == text_font);
2898       text_font_unref (text_font);
2899       
2900       if (!retval)
2901         return FALSE;
2902     }
2903   else
2904     if (font != NULL)
2905       return FALSE;
2906
2907   if (prop->flags & PROPERTY_FOREGROUND)
2908     {
2909       if (!fore || !gdk_color_equal (&prop->fore_color, fore))
2910         return FALSE;
2911     }
2912   else
2913     if (fore != NULL)
2914       return FALSE;
2915
2916   if (prop->flags & PROPERTY_BACKGROUND)
2917     {
2918       if (!back || !gdk_color_equal (&prop->back_color, back))
2919         return FALSE;
2920     }
2921   else
2922     if (back != NULL)
2923       return FALSE;
2924   
2925   return TRUE;
2926 }
2927
2928 static void
2929 realize_property (GtkText *text, TextProperty *prop)
2930 {
2931   GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (text));
2932
2933   if (prop->flags & PROPERTY_FOREGROUND)
2934     gdk_colormap_alloc_color (colormap, &prop->fore_color, FALSE, FALSE);
2935   
2936   if (prop->flags & PROPERTY_BACKGROUND)
2937     gdk_colormap_alloc_color (colormap, &prop->back_color, FALSE, FALSE);
2938 }
2939
2940 static void
2941 realize_properties (GtkText *text)
2942 {
2943   GList *tmp_list = text->text_properties;
2944
2945   while (tmp_list)
2946     {
2947       realize_property (text, tmp_list->data);
2948       
2949       tmp_list = tmp_list->next;
2950     }
2951 }
2952
2953 static void
2954 unrealize_property (GtkText *text, TextProperty *prop)
2955 {
2956   GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (text));
2957
2958   if (prop->flags & PROPERTY_FOREGROUND)
2959     gdk_colormap_free_colors (colormap, &prop->fore_color, 1);
2960   
2961   if (prop->flags & PROPERTY_BACKGROUND)
2962     gdk_colormap_free_colors (colormap, &prop->back_color, 1);
2963 }
2964
2965 static void
2966 unrealize_properties (GtkText *text)
2967 {
2968   GList *tmp_list = text->text_properties;
2969
2970   while (tmp_list)
2971     {
2972       unrealize_property (text, tmp_list->data);
2973
2974       tmp_list = tmp_list->next;
2975     }
2976 }
2977
2978 static TextProperty*
2979 new_text_property (GtkText *text, GdkFont *font, GdkColor* fore, 
2980                    GdkColor* back, guint length)
2981 {
2982   TextProperty *prop;
2983   
2984   if (text_property_chunk == NULL)
2985     {
2986       text_property_chunk = g_mem_chunk_new ("text property mem chunk",
2987                                              sizeof(TextProperty),
2988                                              1024*sizeof(TextProperty),
2989                                              G_ALLOC_AND_FREE);
2990     }
2991   
2992   prop = g_chunk_new(TextProperty, text_property_chunk);
2993
2994   prop->flags = 0;
2995   if (font)
2996     {
2997       prop->flags |= PROPERTY_FONT;
2998       prop->font = get_text_font (font);
2999     }
3000   else
3001     prop->font = NULL;
3002   
3003   if (fore)
3004     {
3005       prop->flags |= PROPERTY_FOREGROUND;
3006       prop->fore_color = *fore;
3007     }
3008       
3009   if (back)
3010     {
3011       prop->flags |= PROPERTY_BACKGROUND;
3012       prop->back_color = *back;
3013     }
3014
3015   prop->length = length;
3016
3017   if (GTK_WIDGET_REALIZED (text))
3018     realize_property (text, prop);
3019
3020   return prop;
3021 }
3022
3023 static void
3024 destroy_text_property (TextProperty *prop)
3025 {
3026   if (prop->font)
3027     text_font_unref (prop->font);
3028   
3029   g_mem_chunk_free (text_property_chunk, prop);
3030 }
3031
3032 /* Flop the memory between the point and the gap around like a
3033  * dead fish. */
3034 static void
3035 move_gap (GtkText* text, guint index)
3036 {
3037   if (text->gap_position < index)
3038     {
3039       gint diff = index - text->gap_position;
3040       
3041       if (text->use_wchar)
3042         g_memmove (text->text.wc + text->gap_position,
3043                    text->text.wc + text->gap_position + text->gap_size,
3044                    diff*sizeof (GdkWChar));
3045       else
3046         g_memmove (text->text.ch + text->gap_position,
3047                    text->text.ch + text->gap_position + text->gap_size,
3048                    diff);
3049       
3050       text->gap_position = index;
3051     }
3052   else if (text->gap_position > index)
3053     {
3054       gint diff = text->gap_position - index;
3055       
3056       if (text->use_wchar)
3057         g_memmove (text->text.wc + index + text->gap_size,
3058                    text->text.wc + index,
3059                    diff*sizeof (GdkWChar));
3060       else
3061         g_memmove (text->text.ch + index + text->gap_size,
3062                    text->text.ch + index,
3063                    diff);
3064       
3065       text->gap_position = index;
3066     }
3067 }
3068
3069 /* Increase the gap size. */
3070 static void
3071 make_forward_space (GtkText* text, guint len)
3072 {
3073   if (text->gap_size < len)
3074     {
3075       guint sum = MAX(2*len, MIN_GAP_SIZE) + text->text_end;
3076       
3077       if (sum >= text->text_len)
3078         {
3079           guint i = 1;
3080           
3081           while (i <= sum) i <<= 1;
3082           
3083           if (text->use_wchar)
3084             text->text.wc = (GdkWChar *)g_realloc(text->text.wc,
3085                                                   i*sizeof(GdkWChar));
3086           else
3087             text->text.ch = (guchar *)g_realloc(text->text.ch, i);
3088           text->text_len = i;
3089         }
3090       
3091       if (text->use_wchar)
3092         g_memmove (text->text.wc + text->gap_position + text->gap_size + 2*len,
3093                    text->text.wc + text->gap_position + text->gap_size,
3094                    (text->text_end - (text->gap_position + text->gap_size))
3095                    *sizeof(GdkWChar));
3096       else
3097         g_memmove (text->text.ch + text->gap_position + text->gap_size + 2*len,
3098                    text->text.ch + text->gap_position + text->gap_size,
3099                    text->text_end - (text->gap_position + text->gap_size));
3100       
3101       text->text_end += len*2;
3102       text->gap_size += len*2;
3103     }
3104 }
3105
3106 /* Inserts into the text property list a list element that guarantees
3107  * that for len characters following the point, text has the correct
3108  * property.  does not move point.  adjusts text_properties_point and
3109  * text_properties_point_offset relative to the current value of
3110  * point. */
3111 static void
3112 insert_text_property (GtkText* text, GdkFont* font,
3113                       GdkColor *fore, GdkColor* back, guint len)
3114 {
3115   GtkPropertyMark *mark = &text->point;
3116   TextProperty* forward_prop = MARK_CURRENT_PROPERTY(mark);
3117   TextProperty* backward_prop = MARK_PREV_PROPERTY(mark);
3118   
3119   if (MARK_OFFSET(mark) == 0)
3120     {
3121       /* Point is on the boundary of two properties.
3122        * If it is the same as either, grow, else insert
3123        * a new one. */
3124       
3125       if (text_properties_equal(forward_prop, font, fore, back))
3126         {
3127           /* Grow the property in front of us. */
3128           
3129           MARK_PROPERTY_LENGTH(mark) += len;
3130         }
3131       else if (backward_prop &&
3132                text_properties_equal(backward_prop, font, fore, back))
3133         {
3134           /* Grow property behind us, point property and offset
3135            * change. */
3136           
3137           SET_PROPERTY_MARK (&text->point,
3138                              MARK_PREV_LIST_PTR (mark),
3139                              backward_prop->length);
3140           
3141           backward_prop->length += len;
3142         }
3143       else if ((MARK_NEXT_LIST_PTR(mark) == NULL) &&
3144                (forward_prop->length == 1))
3145         {
3146           /* Next property just has last position, take it over */
3147
3148           if (GTK_WIDGET_REALIZED (text))
3149             unrealize_property (text, forward_prop);
3150
3151           forward_prop->flags = 0;
3152           if (font)
3153             {
3154               forward_prop->flags |= PROPERTY_FONT;
3155               forward_prop->font = get_text_font (font);
3156             }
3157           else
3158             forward_prop->font = NULL;
3159             
3160           if (fore)
3161             {
3162               forward_prop->flags |= PROPERTY_FOREGROUND;
3163               forward_prop->fore_color = *fore;
3164             }
3165           if (back)
3166             {
3167               forward_prop->flags |= PROPERTY_BACKGROUND;
3168               forward_prop->back_color = *back;
3169             }
3170           forward_prop->length += len;
3171
3172           if (GTK_WIDGET_REALIZED (text))
3173             realize_property (text, forward_prop);
3174         }
3175       else
3176         {
3177           /* Splice a new property into the list. */
3178           
3179           GList* new_prop = g_list_alloc();
3180           
3181           new_prop->next = MARK_LIST_PTR(mark);
3182           new_prop->prev = MARK_PREV_LIST_PTR(mark);
3183           new_prop->next->prev = new_prop;
3184           
3185           if (new_prop->prev)
3186             new_prop->prev->next = new_prop;
3187
3188           new_prop->data = new_text_property (text, font, fore, back, len);
3189
3190           SET_PROPERTY_MARK (mark, new_prop, 0);
3191         }
3192     }
3193   else
3194     {
3195       /* The following will screw up the line_start cache,
3196        * we'll fix it up in correct_cache_insert
3197        */
3198       
3199       /* In the middle of forward_prop, if properties are equal,
3200        * just add to its length, else split it into two and splice
3201        * in a new one. */
3202       if (text_properties_equal (forward_prop, font, fore, back))
3203         {
3204           forward_prop->length += len;
3205         }
3206       else if ((MARK_NEXT_LIST_PTR(mark) == NULL) &&
3207                (MARK_OFFSET(mark) + 1 == forward_prop->length))
3208         {
3209           /* Inserting before only the last position in the text */
3210           
3211           GList* new_prop;
3212           forward_prop->length -= 1;
3213           
3214           new_prop = g_list_alloc();
3215           new_prop->data = new_text_property (text, font, fore, back, len+1);
3216           new_prop->prev = MARK_LIST_PTR(mark);
3217           new_prop->next = NULL;
3218           MARK_NEXT_LIST_PTR(mark) = new_prop;
3219           
3220           SET_PROPERTY_MARK (mark, new_prop, 0);
3221         }
3222       else
3223         {
3224           GList* new_prop = g_list_alloc();
3225           GList* new_prop_forward = g_list_alloc();
3226           gint old_length = forward_prop->length;
3227           GList* next = MARK_NEXT_LIST_PTR(mark);
3228           
3229           /* Set the new lengths according to where they are split.  Construct
3230            * two new properties. */
3231           forward_prop->length = MARK_OFFSET(mark);
3232
3233           new_prop_forward->data = 
3234             new_text_property(text,
3235                               forward_prop->flags & PROPERTY_FONT ? 
3236                                      forward_prop->font->gdk_font : NULL,
3237                               forward_prop->flags & PROPERTY_FOREGROUND ? 
3238                                      &forward_prop->fore_color : NULL,
3239                               forward_prop->flags & PROPERTY_BACKGROUND ? 
3240                                      &forward_prop->back_color : NULL,
3241                               old_length - forward_prop->length);
3242
3243           new_prop->data = new_text_property(text, font, fore, back, len);
3244
3245           /* Now splice things in. */
3246           MARK_NEXT_LIST_PTR(mark) = new_prop;
3247           new_prop->prev = MARK_LIST_PTR(mark);
3248           
3249           new_prop->next = new_prop_forward;
3250           new_prop_forward->prev = new_prop;
3251           
3252           new_prop_forward->next = next;
3253           
3254           if (next)
3255             next->prev = new_prop_forward;
3256           
3257           SET_PROPERTY_MARK (mark, new_prop, 0);
3258         }
3259     }
3260   
3261   while (text->text_properties_end->next)
3262     text->text_properties_end = text->text_properties_end->next;
3263   
3264   while (text->text_properties->prev)
3265     text->text_properties = text->text_properties->prev;
3266 }
3267
3268 static void
3269 delete_text_property (GtkText* text, guint nchars)
3270 {
3271   /* Delete nchars forward from point. */
3272   
3273   /* Deleting text properties is problematical, because we
3274    * might be storing around marks pointing to a property.
3275    *
3276    * The marks in question and how we handle them are:
3277    *
3278    *  point: We know the new value, since it will be at the
3279    *         end of the deleted text, and we move it there
3280    *         first.
3281    *  cursor: We just remove the mark and set it equal to the
3282    *         point after the operation.
3283    *  line-start cache: We replace most affected lines.
3284    *         The current line gets used to fetch the new
3285    *         lines so, if necessary, (delete at the beginning
3286    *         of a line) we fix it up by setting it equal to the
3287    *         point.
3288    */
3289   
3290   TextProperty *prop;
3291   GList        *tmp;
3292   gint          is_first;
3293   
3294   for(; nchars; nchars -= 1)
3295     {
3296       prop = MARK_CURRENT_PROPERTY(&text->point);
3297       
3298       prop->length -= 1;
3299       
3300       if (prop->length == 0)
3301         {
3302           tmp = MARK_LIST_PTR (&text->point);
3303           
3304           is_first = tmp == text->text_properties;
3305           
3306           MARK_LIST_PTR (&text->point) = g_list_remove_link (tmp, tmp);
3307           text->point.offset = 0;
3308
3309           if (GTK_WIDGET_REALIZED (text))
3310             unrealize_property (text, prop);
3311
3312           destroy_text_property (prop);
3313           g_list_free_1 (tmp);
3314           
3315           prop = MARK_CURRENT_PROPERTY (&text->point);
3316           
3317           if (is_first)
3318             text->text_properties = MARK_LIST_PTR (&text->point);
3319           
3320           g_assert (prop->length != 0);
3321         }
3322       else if (prop->length == text->point.offset)
3323         {
3324           MARK_LIST_PTR (&text->point) = MARK_NEXT_LIST_PTR (&text->point);
3325           text->point.offset = 0;
3326         }
3327     }
3328   
3329   /* Check to see if we have just the single final position remaining
3330    * along in a property; if so, combine it with the previous property
3331    */
3332   if (LAST_INDEX (text, text->point) && 
3333       (MARK_OFFSET (&text->point) == 0) &&
3334       (MARK_PREV_LIST_PTR(&text->point) != NULL))
3335     {
3336       tmp = MARK_LIST_PTR (&text->point);
3337       prop = MARK_CURRENT_PROPERTY(&text->point);
3338       
3339       MARK_LIST_PTR (&text->point) = MARK_PREV_LIST_PTR (&text->point);
3340       MARK_CURRENT_PROPERTY(&text->point)->length += 1;
3341       MARK_NEXT_LIST_PTR(&text->point) = NULL;
3342       
3343       text->point.offset = MARK_CURRENT_PROPERTY(&text->point)->length - 1;
3344       
3345       if (GTK_WIDGET_REALIZED (text))
3346         unrealize_property (text, prop);
3347
3348       destroy_text_property (prop);
3349       g_list_free_1 (tmp);
3350     }
3351 }
3352
3353 static void
3354 init_properties (GtkText *text)
3355 {
3356   if (!text->text_properties)
3357     {
3358       text->text_properties = g_list_alloc();
3359       text->text_properties->next = NULL;
3360       text->text_properties->prev = NULL;
3361       text->text_properties->data = new_text_property (text, NULL, NULL, NULL, 1);
3362       text->text_properties_end = text->text_properties;
3363       
3364       SET_PROPERTY_MARK (&text->point, text->text_properties, 0);
3365       
3366       text->point.index = 0;
3367     }
3368 }
3369
3370
3371 /**********************************************************************/
3372 /*                         Property Movement                          */
3373 /**********************************************************************/
3374
3375 static void
3376 move_mark_n (GtkPropertyMark* mark, gint n)
3377 {
3378   if (n > 0)
3379     advance_mark_n(mark, n);
3380   else if (n < 0)
3381     decrement_mark_n(mark, -n);
3382 }
3383
3384 static void
3385 advance_mark (GtkPropertyMark* mark)
3386 {
3387   TextProperty* prop = MARK_CURRENT_PROPERTY (mark);
3388   
3389   mark->index += 1;
3390   
3391   if (prop->length > mark->offset + 1)
3392     mark->offset += 1;
3393   else
3394     {
3395       mark->property = MARK_NEXT_LIST_PTR (mark);
3396       mark->offset   = 0;
3397     }
3398 }
3399
3400 static void
3401 advance_mark_n (GtkPropertyMark* mark, gint n)
3402 {
3403   gint i;
3404   TextProperty* prop;
3405
3406   g_assert (n > 0);
3407
3408   i = 0;                        /* otherwise it migth not be init. */
3409   prop = MARK_CURRENT_PROPERTY(mark);
3410
3411   if ((prop->length - mark->offset - 1) < n) { /* if we need to change prop. */
3412     /* to make it easier */
3413     n += (mark->offset);
3414     mark->index -= mark->offset;
3415     mark->offset = 0;
3416     /* first we take seven-mile-leaps to get to the right text
3417      * property. */
3418     while ((n-i) > prop->length - 1) {
3419       i += prop->length;
3420       mark->index += prop->length;
3421       mark->property = MARK_NEXT_LIST_PTR (mark);
3422       prop = MARK_CURRENT_PROPERTY (mark);
3423     }
3424   }
3425
3426   /* and then the rest */
3427   mark->index += n - i;
3428   mark->offset += n - i;
3429 }
3430
3431 static void
3432 decrement_mark (GtkPropertyMark* mark)
3433 {
3434   mark->index -= 1;
3435   
3436   if (mark->offset > 0)
3437     mark->offset -= 1;
3438   else
3439     {
3440       mark->property = MARK_PREV_LIST_PTR (mark);
3441       mark->offset   = MARK_CURRENT_PROPERTY (mark)->length - 1;
3442     }
3443 }
3444
3445 static void
3446 decrement_mark_n (GtkPropertyMark* mark, gint n)
3447 {
3448   g_assert (n > 0);
3449
3450   while (mark->offset < n) {
3451     /* jump to end of prev */
3452     n -= mark->offset + 1;
3453     mark->index -= mark->offset + 1;
3454     mark->property = MARK_PREV_LIST_PTR (mark);
3455     mark->offset = MARK_CURRENT_PROPERTY (mark)->length - 1;
3456   }
3457
3458   /* and the rest */
3459   mark->index -= n;
3460   mark->offset -= n;
3461 }
3462  
3463 static GtkPropertyMark
3464 find_mark (GtkText* text, guint mark_position)
3465 {
3466   return find_mark_near (text, mark_position, &text->point);
3467 }
3468
3469 /*
3470  * You can also start from the end, what a drag.
3471  */
3472 static GtkPropertyMark
3473 find_mark_near (GtkText* text, guint mark_position, const GtkPropertyMark* near)
3474 {
3475   gint diffa;
3476   gint diffb;
3477   
3478   GtkPropertyMark mark;
3479   
3480   if (!near)
3481     diffa = mark_position + 1;
3482   else
3483     diffa = mark_position - near->index;
3484   
3485   diffb = mark_position;
3486   
3487   if (diffa < 0)
3488     diffa = -diffa;
3489   
3490   if (diffa <= diffb)
3491     {
3492       mark = *near;
3493     }
3494   else
3495     {
3496       mark.index = 0;
3497       mark.property = text->text_properties;
3498       mark.offset = 0;
3499     }
3500
3501   move_mark_n (&mark, mark_position - mark.index);
3502    
3503   return mark;
3504 }
3505
3506 /* This routine must be called with scroll == FALSE, only when
3507  * point is at least partially on screen
3508  */
3509
3510 static void
3511 find_line_containing_point (GtkText* text, guint point,
3512                             gboolean scroll)
3513 {
3514   GList* cache;
3515   gint height;
3516   
3517   text->current_line = NULL;
3518
3519   TEXT_SHOW (text);
3520
3521   /* Scroll backwards until the point is on screen
3522    */
3523   while (CACHE_DATA(text->line_start_cache).start.index > point)
3524     scroll_int (text, - LINE_HEIGHT(CACHE_DATA(text->line_start_cache)));
3525
3526   /* Now additionally try to make sure that the point is fully on screen
3527    */
3528   if (scroll)
3529     {
3530       while (text->first_cut_pixels != 0 && 
3531              text->line_start_cache->next &&
3532              CACHE_DATA(text->line_start_cache->next).start.index > point)
3533         scroll_int (text, - LINE_HEIGHT(CACHE_DATA(text->line_start_cache->next)));
3534     }
3535
3536   gdk_window_get_size (text->text_area, NULL, &height);
3537   
3538   for (cache = text->line_start_cache; cache; cache = cache->next)
3539     {
3540       guint lph;
3541       
3542       if (CACHE_DATA(cache).end.index >= point ||
3543           LAST_INDEX(text, CACHE_DATA(cache).end))
3544         {
3545           text->current_line = cache; /* LOOK HERE, this proc has an
3546                                        * important side effect. */
3547           return;
3548         }
3549       
3550       TEXT_SHOW_LINE (text, cache, "cache");
3551       
3552       if (cache->next == NULL)
3553         fetch_lines_forward (text, 1);
3554       
3555       if (scroll)
3556         {
3557           lph = pixel_height_of (text, cache->next);
3558           
3559           /* Scroll the bottom of the line is on screen, or until
3560            * the line is the first onscreen line.
3561            */
3562           while (cache->next != text->line_start_cache && lph > height)
3563             {
3564               TEXT_SHOW_LINE (text, cache, "cache");
3565               TEXT_SHOW_LINE (text, cache->next, "cache->next");
3566               scroll_int (text, LINE_HEIGHT(CACHE_DATA(cache->next)));
3567               lph = pixel_height_of (text, cache->next);
3568             }
3569         }
3570     }
3571   
3572   g_assert_not_reached (); /* Must set text->current_line here */
3573 }
3574
3575 static guint
3576 pixel_height_of (GtkText* text, GList* cache_line)
3577 {
3578   gint pixels = - text->first_cut_pixels;
3579   GList *cache = text->line_start_cache;
3580   
3581   while (TRUE) {
3582     pixels += LINE_HEIGHT (CACHE_DATA(cache));
3583     
3584     if (cache->data == cache_line->data)
3585       break;
3586     
3587     cache = cache->next;
3588   }
3589   
3590   return pixels;
3591 }
3592
3593 /**********************************************************************/
3594 /*                      Search and Placement                          */
3595 /**********************************************************************/
3596
3597 static gint
3598 find_char_width (GtkText* text, const GtkPropertyMark *mark, const TabStopMark *tab_mark)
3599 {
3600   GdkWChar ch;
3601   gint16* char_widths;
3602   
3603   if (LAST_INDEX (text, *mark))
3604     return 0;
3605   
3606   ch = GTK_TEXT_INDEX (text, mark->index);
3607   char_widths = MARK_CURRENT_TEXT_FONT (text, mark)->char_widths;
3608
3609   if (ch == '\t')
3610     {
3611       return tab_mark->to_next_tab * char_widths[' '];
3612     }
3613   else if (ch < 256)
3614     {
3615       return char_widths[ch];
3616     }
3617   else
3618     {
3619       return gdk_char_width_wc(MARK_CURRENT_TEXT_FONT(text, mark)->gdk_font, ch);
3620     }
3621 }
3622
3623 static void
3624 advance_tab_mark (GtkText* text, TabStopMark* tab_mark, GdkWChar ch)
3625 {
3626   if (tab_mark->to_next_tab == 1 || ch == '\t')
3627     {
3628       if (tab_mark->tab_stops->next)
3629         {
3630           tab_mark->tab_stops = tab_mark->tab_stops->next;
3631           tab_mark->to_next_tab = (gulong) tab_mark->tab_stops->data;
3632         }
3633       else
3634         {
3635           tab_mark->to_next_tab = text->default_tab_width;
3636         }
3637     }
3638   else
3639     {
3640       tab_mark->to_next_tab -= 1;
3641     }
3642 }
3643
3644 static void
3645 advance_tab_mark_n (GtkText* text, TabStopMark* tab_mark, gint n)
3646      /* No tabs! */
3647 {
3648   while (n--)
3649     advance_tab_mark (text, tab_mark, 0);
3650 }
3651
3652 static void
3653 find_cursor_at_line (GtkText* text, const LineParams* start_line, gint pixel_height)
3654 {
3655   GdkWChar ch;
3656   
3657   GtkPropertyMark mark        = start_line->start;
3658   TabStopMark  tab_mark    = start_line->tab_cont.tab_start;
3659   gint         pixel_width = LINE_START_PIXEL (*start_line);
3660   
3661   while (mark.index < text->cursor_mark.index)
3662     {
3663       pixel_width += find_char_width (text, &mark, &tab_mark);
3664       
3665       advance_tab_mark (text, &tab_mark, GTK_TEXT_INDEX(text, mark.index));
3666       advance_mark (&mark);
3667     }
3668   
3669   text->cursor_pos_x       = pixel_width;
3670   text->cursor_pos_y       = pixel_height;
3671   text->cursor_char_offset = start_line->font_descent;
3672   text->cursor_mark        = mark;
3673   
3674   ch = LAST_INDEX (text, mark) ? 
3675     LINE_DELIM : GTK_TEXT_INDEX (text, mark.index);
3676   
3677   if ((text->use_wchar) ? gdk_iswspace (ch) : isspace (ch))
3678     text->cursor_char = 0;
3679   else
3680     text->cursor_char = ch;
3681 }
3682
3683 static void
3684 find_cursor (GtkText* text, gboolean scroll)
3685 {
3686   if (GTK_WIDGET_REALIZED (text))
3687     {
3688       find_line_containing_point (text, text->cursor_mark.index, scroll);
3689       
3690       if (text->current_line)
3691         find_cursor_at_line (text,
3692                              &CACHE_DATA(text->current_line),
3693                              pixel_height_of(text, text->current_line));
3694     }
3695   
3696   GTK_OLD_EDITABLE (text)->current_pos = text->cursor_mark.index;
3697 }
3698
3699 static void
3700 find_mouse_cursor_at_line (GtkText *text, const LineParams* lp,
3701                            guint line_pixel_height,
3702                            gint button_x)
3703 {
3704   GtkPropertyMark mark     = lp->start;
3705   TabStopMark  tab_mark = lp->tab_cont.tab_start;
3706   
3707   gint char_width = find_char_width(text, &mark, &tab_mark);
3708   gint pixel_width = LINE_START_PIXEL (*lp) + (char_width+1)/2;
3709   
3710   text->cursor_pos_y = line_pixel_height;
3711   
3712   for (;;)
3713     {
3714       GdkWChar ch = LAST_INDEX (text, mark) ? 
3715         LINE_DELIM : GTK_TEXT_INDEX (text, mark.index);
3716       
3717       if (button_x < pixel_width || mark.index == lp->end.index)
3718         {
3719           text->cursor_pos_x       = pixel_width - (char_width+1)/2;
3720           text->cursor_mark        = mark;
3721           text->cursor_char_offset = lp->font_descent;
3722           
3723           if ((text->use_wchar) ? gdk_iswspace (ch) : isspace (ch))
3724             text->cursor_char = 0;
3725           else
3726             text->cursor_char = ch;
3727           
3728           break;
3729         }
3730       
3731       advance_tab_mark (text, &tab_mark, ch);
3732       advance_mark (&mark);
3733       
3734       pixel_width += char_width/2;
3735       
3736       char_width = find_char_width (text, &mark, &tab_mark);
3737       
3738       pixel_width += (char_width+1)/2;
3739     }
3740 }
3741
3742 static void
3743 find_mouse_cursor (GtkText* text, gint x, gint y)
3744 {
3745   gint pixel_height;
3746   GList* cache = text->line_start_cache;
3747   
3748   g_assert (cache);
3749   
3750   pixel_height = - text->first_cut_pixels;
3751   
3752   for (; cache; cache = cache->next)
3753     {
3754       pixel_height += LINE_HEIGHT(CACHE_DATA(cache));
3755       
3756       if (y < pixel_height || !cache->next)
3757         {
3758           find_mouse_cursor_at_line (text, &CACHE_DATA(cache), pixel_height, x);
3759           
3760           find_cursor (text, FALSE);
3761           
3762           return;
3763         }
3764     }
3765 }
3766
3767 /**********************************************************************/
3768 /*                          Cache Manager                             */
3769 /**********************************************************************/
3770
3771 static void
3772 free_cache (GtkText* text)
3773 {
3774   GList* cache = text->line_start_cache;
3775   
3776   if (cache)
3777     {
3778       while (cache->prev)
3779         cache = cache->prev;
3780       
3781       text->line_start_cache = cache;
3782     }
3783   
3784   for (; cache; cache = cache->next)
3785     g_mem_chunk_free (params_mem_chunk, cache->data);
3786   
3787   g_list_free (text->line_start_cache);
3788   
3789   text->line_start_cache = NULL;
3790 }
3791
3792 static GList*
3793 remove_cache_line (GtkText* text, GList* member)
3794 {
3795   GList *list;
3796   
3797   if (member == NULL)
3798     return NULL;
3799   
3800   if (member == text->line_start_cache)
3801     text->line_start_cache = text->line_start_cache->next;
3802   
3803   if (member->prev)
3804     member->prev->next = member->next;
3805   
3806   if (member->next)
3807     member->next->prev = member->prev;
3808   
3809   list = member->next;
3810   
3811   g_mem_chunk_free (params_mem_chunk, member->data);
3812   g_list_free_1 (member);
3813   
3814   return list;
3815 }
3816
3817 /**********************************************************************/
3818 /*                           Key Motion                               */
3819 /**********************************************************************/
3820
3821 static void
3822 move_cursor_buffer_ver (GtkText *text, int dir)
3823 {
3824   undraw_cursor (text, FALSE);
3825   
3826   if (dir > 0)
3827     {
3828       scroll_int (text, text->vadj->upper);
3829       text->cursor_mark = find_this_line_start_mark (text,
3830                                                      TEXT_LENGTH (text),
3831                                                      &text->cursor_mark);
3832     }
3833   else
3834     {
3835       scroll_int (text, - text->vadj->value);
3836       text->cursor_mark = find_this_line_start_mark (text,
3837                                                      0,
3838                                                      &text->cursor_mark);
3839     }
3840   
3841   find_cursor (text, TRUE);
3842   draw_cursor (text, FALSE);
3843 }
3844
3845 static void
3846 move_cursor_page_ver (GtkText *text, int dir)
3847 {
3848   scroll_int (text, dir * text->vadj->page_increment);
3849 }
3850
3851 static void
3852 move_cursor_ver (GtkText *text, int count)
3853 {
3854   gint i;
3855   GtkPropertyMark mark;
3856   gint offset;
3857   
3858   mark = find_this_line_start_mark (text, text->cursor_mark.index, &text->cursor_mark);
3859   offset = text->cursor_mark.index - mark.index;
3860   
3861   if (offset > text->cursor_virtual_x)
3862     text->cursor_virtual_x = offset;
3863   
3864   if (count < 0)
3865     {
3866       if (mark.index == 0)
3867         return;
3868       
3869       decrement_mark (&mark);
3870       mark = find_this_line_start_mark (text, mark.index, &mark);
3871     }
3872   else
3873     {
3874       mark = text->cursor_mark;
3875       
3876       while (!LAST_INDEX(text, mark) && GTK_TEXT_INDEX(text, mark.index) != LINE_DELIM)
3877         advance_mark (&mark);
3878       
3879       if (LAST_INDEX(text, mark))
3880         return;
3881       
3882       advance_mark (&mark);
3883     }
3884   
3885   for (i=0; i < text->cursor_virtual_x; i += 1, advance_mark(&mark))
3886     if (LAST_INDEX(text, mark) ||
3887         GTK_TEXT_INDEX(text, mark.index) == LINE_DELIM)
3888       break;
3889   
3890   undraw_cursor (text, FALSE);
3891   
3892   text->cursor_mark = mark;
3893   
3894   find_cursor (text, TRUE);
3895   
3896   draw_cursor (text, FALSE);
3897 }
3898
3899 static void
3900 move_cursor_hor (GtkText *text, int count)
3901 {
3902   /* count should be +-1. */
3903   if ( (count > 0 && text->cursor_mark.index + count > TEXT_LENGTH(text)) ||
3904        (count < 0 && text->cursor_mark.index < (- count)) ||
3905        (count == 0) )
3906     return;
3907   
3908   text->cursor_virtual_x = 0;
3909   
3910   undraw_cursor (text, FALSE);
3911   
3912   move_mark_n (&text->cursor_mark, count);
3913   
3914   find_cursor (text, TRUE);
3915   
3916   draw_cursor (text, FALSE);
3917 }
3918
3919 static void 
3920 gtk_text_move_cursor (GtkOldEditable *old_editable,
3921                       gint            x,
3922                       gint            y)
3923 {
3924   if (x > 0)
3925     {
3926       while (x-- != 0)
3927         move_cursor_hor (GTK_TEXT (old_editable), 1);
3928     }
3929   else if (x < 0)
3930     {
3931       while (x++ != 0)
3932         move_cursor_hor (GTK_TEXT (old_editable), -1);
3933     }
3934   
3935   if (y > 0)
3936     {
3937       while (y-- != 0)
3938         move_cursor_ver (GTK_TEXT (old_editable), 1);
3939     }
3940   else if (y < 0)
3941     {
3942       while (y++ != 0)
3943         move_cursor_ver (GTK_TEXT (old_editable), -1);
3944     }
3945 }
3946
3947 static void
3948 gtk_text_move_forward_character (GtkText *text)
3949 {
3950   move_cursor_hor (text, 1);
3951 }
3952
3953 static void
3954 gtk_text_move_backward_character (GtkText *text)
3955 {
3956   move_cursor_hor (text, -1);
3957 }
3958
3959 static void
3960 gtk_text_move_next_line (GtkText *text)
3961 {
3962   move_cursor_ver (text, 1);
3963 }
3964
3965 static void
3966 gtk_text_move_previous_line (GtkText *text)
3967 {
3968   move_cursor_ver (text, -1);
3969 }
3970
3971 static void 
3972 gtk_text_move_word (GtkOldEditable *old_editable,
3973                     gint            n)
3974 {
3975   if (n > 0)
3976     {
3977       while (n-- != 0)
3978         gtk_text_move_forward_word (GTK_TEXT (old_editable));
3979     }
3980   else if (n < 0)
3981     {
3982       while (n++ != 0)
3983         gtk_text_move_backward_word (GTK_TEXT (old_editable));
3984     }
3985 }
3986
3987 static void
3988 gtk_text_move_forward_word (GtkText *text)
3989 {
3990   text->cursor_virtual_x = 0;
3991   
3992   undraw_cursor (text, FALSE);
3993   
3994   if (text->use_wchar)
3995     {
3996       while (!LAST_INDEX (text, text->cursor_mark) && 
3997              !gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
3998         advance_mark (&text->cursor_mark);
3999       
4000       while (!LAST_INDEX (text, text->cursor_mark) && 
4001              gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
4002         advance_mark (&text->cursor_mark);
4003     }
4004   else
4005     {
4006       while (!LAST_INDEX (text, text->cursor_mark) && 
4007              !isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
4008         advance_mark (&text->cursor_mark);
4009       
4010       while (!LAST_INDEX (text, text->cursor_mark) && 
4011              isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
4012         advance_mark (&text->cursor_mark);
4013     }
4014   
4015   find_cursor (text, TRUE);
4016   draw_cursor (text, FALSE);
4017 }
4018
4019 static void
4020 gtk_text_move_backward_word (GtkText *text)
4021 {
4022   text->cursor_virtual_x = 0;
4023   
4024   undraw_cursor (text, FALSE);
4025   
4026   if (text->use_wchar)
4027     {
4028       while ((text->cursor_mark.index > 0) &&
4029              !gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
4030         decrement_mark (&text->cursor_mark);
4031       
4032       while ((text->cursor_mark.index > 0) &&
4033              gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
4034         decrement_mark (&text->cursor_mark);
4035     }
4036   else
4037     {
4038       while ((text->cursor_mark.index > 0) &&
4039              !isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
4040         decrement_mark (&text->cursor_mark);
4041       
4042       while ((text->cursor_mark.index > 0) &&
4043              isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
4044         decrement_mark (&text->cursor_mark);
4045     }
4046   
4047   find_cursor (text, TRUE);
4048   draw_cursor (text, FALSE);
4049 }
4050
4051 static void 
4052 gtk_text_move_page (GtkOldEditable *old_editable,
4053                     gint            x,
4054                     gint            y)
4055 {
4056   if (y != 0)
4057     scroll_int (GTK_TEXT (old_editable), 
4058                 y * GTK_TEXT(old_editable)->vadj->page_increment);  
4059 }
4060
4061 static void 
4062 gtk_text_move_to_row (GtkOldEditable *old_editable,
4063                       gint            row)
4064 {
4065 }
4066
4067 static void 
4068 gtk_text_move_to_column (GtkOldEditable *old_editable,
4069                          gint            column)
4070 {
4071   GtkText *text;
4072   
4073   text = GTK_TEXT (old_editable);
4074   
4075   text->cursor_virtual_x = 0;   /* FIXME */
4076   
4077   undraw_cursor (text, FALSE);
4078   
4079   /* Move to the beginning of the line */
4080   while ((text->cursor_mark.index > 0) &&
4081          (GTK_TEXT_INDEX (text, text->cursor_mark.index - 1) != LINE_DELIM))
4082     decrement_mark (&text->cursor_mark);
4083   
4084   while (!LAST_INDEX (text, text->cursor_mark) &&
4085          (GTK_TEXT_INDEX (text, text->cursor_mark.index) != LINE_DELIM))
4086     {
4087       if (column > 0)
4088         column--;
4089       else if (column == 0)
4090         break;
4091       
4092       advance_mark (&text->cursor_mark);
4093     }
4094   
4095   find_cursor (text, TRUE);
4096   draw_cursor (text, FALSE);
4097 }
4098
4099 static void
4100 gtk_text_move_beginning_of_line (GtkText *text)
4101 {
4102   gtk_text_move_to_column (GTK_OLD_EDITABLE (text), 0);
4103   
4104 }
4105
4106 static void
4107 gtk_text_move_end_of_line (GtkText *text)
4108 {
4109   gtk_text_move_to_column (GTK_OLD_EDITABLE (text), -1);
4110 }
4111
4112 static void 
4113 gtk_text_kill_char (GtkOldEditable *old_editable,
4114                     gint            direction)
4115 {
4116   GtkText *text;
4117   
4118   text = GTK_TEXT (old_editable);
4119   
4120   if (old_editable->selection_start_pos != old_editable->selection_end_pos)
4121     gtk_editable_delete_selection (GTK_EDITABLE (old_editable));
4122   else
4123     {
4124       if (direction >= 0)
4125         {
4126           if (text->point.index + 1 <= TEXT_LENGTH (text))
4127             gtk_editable_delete_text (GTK_EDITABLE (old_editable), text->point.index, text->point.index + 1);
4128         }
4129       else
4130         {
4131           if (text->point.index > 0)
4132             gtk_editable_delete_text (GTK_EDITABLE (old_editable), text->point.index - 1, text->point.index);
4133         }
4134     }
4135 }
4136
4137 static void
4138 gtk_text_delete_forward_character (GtkText *text)
4139 {
4140   gtk_text_kill_char (GTK_OLD_EDITABLE (text), 1);
4141 }
4142
4143 static void
4144 gtk_text_delete_backward_character (GtkText *text)
4145 {
4146   gtk_text_kill_char (GTK_OLD_EDITABLE (text), -1);
4147 }
4148
4149 static void 
4150 gtk_text_kill_word (GtkOldEditable *old_editable,
4151                     gint            direction)
4152 {
4153   if (old_editable->selection_start_pos != old_editable->selection_end_pos)
4154     gtk_editable_delete_selection (GTK_EDITABLE (old_editable));
4155   else
4156     {
4157       gint old_pos = old_editable->current_pos;
4158       if (direction >= 0)
4159         {
4160           gtk_text_move_word (old_editable, 1);
4161           gtk_editable_delete_text (GTK_EDITABLE (old_editable), old_pos, old_editable->current_pos);
4162         }
4163       else
4164         {
4165           gtk_text_move_word (old_editable, -1);
4166           gtk_editable_delete_text (GTK_EDITABLE (old_editable), old_editable->current_pos, old_pos);
4167         }
4168     }
4169 }
4170
4171 static void
4172 gtk_text_delete_forward_word (GtkText *text)
4173 {
4174   gtk_text_kill_word (GTK_OLD_EDITABLE (text), 1);
4175 }
4176
4177 static void
4178 gtk_text_delete_backward_word (GtkText *text)
4179 {
4180   gtk_text_kill_word (GTK_OLD_EDITABLE (text), -1);
4181 }
4182
4183 static void 
4184 gtk_text_kill_line (GtkOldEditable *old_editable,
4185                     gint            direction)
4186 {
4187   gint old_pos = old_editable->current_pos;
4188   if (direction >= 0)
4189     {
4190       gtk_text_move_to_column (old_editable, -1);
4191       gtk_editable_delete_text (GTK_EDITABLE (old_editable), old_pos, old_editable->current_pos);
4192     }
4193   else
4194     {
4195       gtk_text_move_to_column (old_editable, 0);
4196       gtk_editable_delete_text (GTK_EDITABLE (old_editable), old_editable->current_pos, old_pos);
4197     }
4198 }
4199
4200 static void
4201 gtk_text_delete_line (GtkText *text)
4202 {
4203   gtk_text_move_to_column (GTK_OLD_EDITABLE (text), 0);
4204   gtk_text_kill_line (GTK_OLD_EDITABLE (text), 1);
4205 }
4206
4207 static void
4208 gtk_text_delete_to_line_end (GtkText *text)
4209 {
4210   gtk_text_kill_line (GTK_OLD_EDITABLE (text), 1);
4211 }
4212
4213 static void
4214 gtk_text_select_word (GtkText *text, guint32 time)
4215 {
4216   gint start_pos;
4217   gint end_pos;
4218   
4219   GtkOldEditable *old_editable;
4220   old_editable = GTK_OLD_EDITABLE (text);
4221   
4222   gtk_text_move_backward_word (text);
4223   start_pos = text->cursor_mark.index;
4224   
4225   gtk_text_move_forward_word (text);
4226   end_pos = text->cursor_mark.index;
4227   
4228   old_editable->has_selection = TRUE;
4229   gtk_text_set_selection (old_editable, start_pos, end_pos);
4230   gtk_old_editable_claim_selection (old_editable, start_pos != end_pos, time);
4231 }
4232
4233 static void
4234 gtk_text_select_line (GtkText *text, guint32 time)
4235 {
4236   gint start_pos;
4237   gint end_pos;
4238   
4239   GtkOldEditable *old_editable;
4240   old_editable = GTK_OLD_EDITABLE (text);
4241   
4242   gtk_text_move_beginning_of_line (text);
4243   start_pos = text->cursor_mark.index;
4244   
4245   gtk_text_move_end_of_line (text);
4246   gtk_text_move_forward_character (text);
4247   end_pos = text->cursor_mark.index;
4248   
4249   old_editable->has_selection = TRUE;
4250   gtk_text_set_selection (old_editable, start_pos, end_pos);
4251   gtk_old_editable_claim_selection (old_editable, start_pos != end_pos, time);
4252 }
4253
4254 /**********************************************************************/
4255 /*                            Scrolling                               */
4256 /**********************************************************************/
4257
4258 static void
4259 adjust_adj (GtkText* text, GtkAdjustment* adj)
4260 {
4261   gint height;
4262   
4263   gdk_window_get_size (text->text_area, NULL, &height);
4264   
4265   adj->step_increment = MIN (adj->upper, SCROLL_PIXELS);
4266   adj->page_increment = MIN (adj->upper, height - KEY_SCROLL_PIXELS);
4267   adj->page_size      = MIN (adj->upper, height);
4268   adj->value          = MIN (adj->value, adj->upper - adj->page_size);
4269   adj->value          = MAX (adj->value, 0.0);
4270   
4271   gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed");
4272 }
4273
4274 static gint
4275 set_vertical_scroll_iterator (GtkText* text, LineParams* lp, void* data)
4276 {
4277   SetVerticalScrollData *svdata = (SetVerticalScrollData *) data;
4278   
4279   if ((text->first_line_start_index >= lp->start.index) &&
4280       (text->first_line_start_index <= lp->end.index))
4281     {
4282       svdata->mark = lp->start;
4283   
4284       if (text->first_line_start_index == lp->start.index)
4285         {
4286           text->first_onscreen_ver_pixel = svdata->pixel_height + text->first_cut_pixels;
4287         }
4288       else
4289         {
4290           text->first_onscreen_ver_pixel = svdata->pixel_height;
4291           text->first_cut_pixels = 0;
4292         }
4293       
4294       text->vadj->value = text->first_onscreen_ver_pixel;
4295     }
4296   
4297   svdata->pixel_height += LINE_HEIGHT (*lp);
4298   
4299   return FALSE;
4300 }
4301
4302 static gint
4303 set_vertical_scroll_find_iterator (GtkText* text, LineParams* lp, void* data)
4304 {
4305   SetVerticalScrollData *svdata = (SetVerticalScrollData *) data;
4306   gint return_val;
4307   
4308   if (svdata->pixel_height <= (gint) text->vadj->value &&
4309       svdata->pixel_height + LINE_HEIGHT(*lp) > (gint) text->vadj->value)
4310     {
4311       svdata->mark = lp->start;
4312       
4313       text->first_cut_pixels = (gint)text->vadj->value - svdata->pixel_height;
4314       text->first_onscreen_ver_pixel = svdata->pixel_height;
4315       text->first_line_start_index = lp->start.index;
4316       
4317       return_val = TRUE;
4318     }
4319   else
4320     {
4321       svdata->pixel_height += LINE_HEIGHT (*lp);
4322       
4323       return_val = FALSE;
4324     }
4325   
4326   return return_val;
4327 }
4328
4329 static GtkPropertyMark
4330 set_vertical_scroll (GtkText* text)
4331 {
4332   GtkPropertyMark mark = find_mark (text, 0);
4333   SetVerticalScrollData data;
4334   gint height;
4335   gint orig_value;
4336   
4337   data.pixel_height = 0;
4338   line_params_iterate (text, &mark, NULL, FALSE, &data, set_vertical_scroll_iterator);
4339   
4340   text->vadj->upper = data.pixel_height;
4341   orig_value = (gint) text->vadj->value;
4342   
4343   gdk_window_get_size (text->text_area, NULL, &height);
4344   
4345   text->vadj->step_increment = MIN (text->vadj->upper, SCROLL_PIXELS);
4346   text->vadj->page_increment = MIN (text->vadj->upper, height - KEY_SCROLL_PIXELS);
4347   text->vadj->page_size      = MIN (text->vadj->upper, height);
4348   text->vadj->value          = MIN (text->vadj->value, text->vadj->upper - text->vadj->page_size);
4349   text->vadj->value          = MAX (text->vadj->value, 0.0);
4350   
4351   text->last_ver_value = (gint)text->vadj->value;
4352   
4353   gtk_signal_emit_by_name (GTK_OBJECT (text->vadj), "changed");
4354   
4355   if (text->vadj->value != orig_value)
4356     {
4357       /* We got clipped, and don't really know which line to put first. */
4358       data.pixel_height = 0;
4359       data.last_didnt_wrap = TRUE;
4360       
4361       line_params_iterate (text, &mark, NULL,
4362                            FALSE, &data,
4363                            set_vertical_scroll_find_iterator);
4364     }
4365
4366   return data.mark;
4367 }
4368
4369 static void
4370 scroll_int (GtkText* text, gint diff)
4371 {
4372   gdouble upper;
4373   
4374   text->vadj->value += diff;
4375   
4376   upper = text->vadj->upper - text->vadj->page_size;
4377   text->vadj->value = MIN (text->vadj->value, upper);
4378   text->vadj->value = MAX (text->vadj->value, 0.0);
4379   
4380   gtk_signal_emit_by_name (GTK_OBJECT (text->vadj), "value_changed");
4381 }
4382
4383 static void 
4384 process_exposes (GtkText *text)
4385 {
4386   GdkEvent *event;
4387   
4388   /* Make sure graphics expose events are processed before scrolling
4389    * again */
4390   
4391   while ((event = gdk_event_get_graphics_expose (text->text_area)) != NULL)
4392     {
4393       gtk_widget_send_expose (GTK_WIDGET (text), event);
4394       if (event->expose.count == 0)
4395         {
4396           gdk_event_free (event);
4397           break;
4398         }
4399       gdk_event_free (event);
4400     }
4401 }
4402
4403 static gint last_visible_line_height (GtkText* text)
4404 {
4405   GList *cache = text->line_start_cache;
4406   gint height;
4407   
4408   gdk_window_get_size (text->text_area, NULL, &height);
4409   
4410   for (; cache->next; cache = cache->next)
4411     if (pixel_height_of(text, cache->next) > height)
4412       break;
4413   
4414   if (cache)
4415     return pixel_height_of(text, cache) - 1;
4416   else
4417     return 0;
4418 }
4419
4420 static gint first_visible_line_height (GtkText* text)
4421 {
4422   if (text->first_cut_pixels)
4423     return pixel_height_of(text, text->line_start_cache) + 1;
4424   else
4425     return 1;
4426 }
4427
4428 static void
4429 scroll_down (GtkText* text, gint diff0)
4430 {
4431   GdkRectangle rect;
4432   gint real_diff = 0;
4433   gint width, height;
4434   
4435   text->first_onscreen_ver_pixel += diff0;
4436   
4437   while (diff0-- > 0)
4438     {
4439       g_assert (text->line_start_cache);
4440       
4441       if (text->first_cut_pixels < LINE_HEIGHT(CACHE_DATA(text->line_start_cache)) - 1)
4442         {
4443           text->first_cut_pixels += 1;
4444         }
4445       else
4446         {
4447           text->first_cut_pixels = 0;
4448           
4449           text->line_start_cache = text->line_start_cache->next;
4450           
4451           text->first_line_start_index =
4452             CACHE_DATA(text->line_start_cache).start.index;
4453           
4454           if (!text->line_start_cache->next)
4455             fetch_lines_forward (text, 1);
4456         }
4457       
4458       real_diff += 1;
4459     }
4460   
4461   gdk_window_get_size (text->text_area, &width, &height);
4462   if (height > real_diff)
4463     gdk_draw_pixmap (text->text_area,
4464                      text->gc,
4465                      text->text_area,
4466                      0,
4467                      real_diff,
4468                      0,
4469                      0,
4470                      width,
4471                      height - real_diff);
4472   
4473   rect.x      = 0;
4474   rect.y      = MAX (0, height - real_diff);
4475   rect.width  = width;
4476   rect.height = MIN (height, real_diff);
4477   
4478   expose_text (text, &rect, FALSE);
4479   gtk_text_draw_focus ( (GtkWidget *) text);
4480   
4481   if (text->current_line)
4482     {
4483       gint cursor_min;
4484       
4485       text->cursor_pos_y -= real_diff;
4486       cursor_min = drawn_cursor_min(text);
4487       
4488       if (cursor_min < 0)
4489         find_mouse_cursor (text, text->cursor_pos_x,
4490                            first_visible_line_height (text));
4491     }
4492   
4493   if (height > real_diff)
4494     process_exposes (text);
4495 }
4496
4497 static void
4498 scroll_up (GtkText* text, gint diff0)
4499 {
4500   gint real_diff = 0;
4501   GdkRectangle rect;
4502   gint width, height;
4503   
4504   text->first_onscreen_ver_pixel += diff0;
4505   
4506   while (diff0++ < 0)
4507     {
4508       g_assert (text->line_start_cache);
4509       
4510       if (text->first_cut_pixels > 0)
4511         {
4512           text->first_cut_pixels -= 1;
4513         }
4514       else
4515         {
4516           if (!text->line_start_cache->prev)
4517             fetch_lines_backward (text);
4518           
4519           text->line_start_cache = text->line_start_cache->prev;
4520           
4521           text->first_line_start_index =
4522             CACHE_DATA(text->line_start_cache).start.index;
4523           
4524           text->first_cut_pixels = LINE_HEIGHT(CACHE_DATA(text->line_start_cache)) - 1;
4525         }
4526       
4527       real_diff += 1;
4528     }
4529   
4530   gdk_window_get_size (text->text_area, &width, &height);
4531   if (height > real_diff)
4532     gdk_draw_pixmap (text->text_area,
4533                      text->gc,
4534                      text->text_area,
4535                      0,
4536                      0,
4537                      0,
4538                      real_diff,
4539                      width,
4540                      height - real_diff);
4541   
4542   rect.x      = 0;
4543   rect.y      = 0;
4544   rect.width  = width;
4545   rect.height = MIN (height, real_diff);
4546   
4547   expose_text (text, &rect, FALSE);
4548   gtk_text_draw_focus ( (GtkWidget *) text);
4549   
4550   if (text->current_line)
4551     {
4552       gint cursor_max;
4553       gint height;
4554       
4555       text->cursor_pos_y += real_diff;
4556       cursor_max = drawn_cursor_max(text);
4557       gdk_window_get_size (text->text_area, NULL, &height);
4558       
4559       if (cursor_max >= height)
4560         find_mouse_cursor (text, text->cursor_pos_x,
4561                            last_visible_line_height (text));
4562     }
4563   
4564   if (height > real_diff)
4565     process_exposes (text);
4566 }
4567
4568 /**********************************************************************/
4569 /*                            Display Code                            */
4570 /**********************************************************************/
4571
4572 /* Assumes mark starts a line.  Calculates the height, width, and
4573  * displayable character count of a single DISPLAYABLE line.  That
4574  * means that in line-wrap mode, this does may not compute the
4575  * properties of an entire line. */
4576 static LineParams
4577 find_line_params (GtkText* text,
4578                   const GtkPropertyMark* mark,
4579                   const PrevTabCont *tab_cont,
4580                   PrevTabCont *next_cont)
4581 {
4582   LineParams lp;
4583   TabStopMark tab_mark = tab_cont->tab_start;
4584   guint max_display_pixels;
4585   GdkWChar ch;
4586   gint ch_width;
4587   GdkFont *font;
4588   
4589   gdk_window_get_size (text->text_area, (gint*) &max_display_pixels, NULL);
4590   max_display_pixels -= LINE_WRAP_ROOM;
4591   
4592   lp.wraps             = 0;
4593   lp.tab_cont          = *tab_cont;
4594   lp.start             = *mark;
4595   lp.end               = *mark;
4596   lp.pixel_width       = tab_cont->pixel_offset;
4597   lp.displayable_chars = 0;
4598   lp.font_ascent       = 0;
4599   lp.font_descent      = 0;
4600   
4601   init_tab_cont (text, next_cont);
4602   
4603   while (!LAST_INDEX(text, lp.end))
4604     {
4605       g_assert (lp.end.property);
4606       
4607       ch   = GTK_TEXT_INDEX (text, lp.end.index);
4608       font = MARK_CURRENT_FONT (text, &lp.end);
4609
4610       if (ch == LINE_DELIM)
4611         {
4612           /* Newline doesn't count in computation of line height, even
4613            * if its in a bigger font than the rest of the line.  Unless,
4614            * of course, there are no other characters. */
4615           
4616           if (!lp.font_ascent && !lp.font_descent)
4617             {
4618               lp.font_ascent = font->ascent;
4619               lp.font_descent = font->descent;
4620             }
4621           
4622           lp.tab_cont_next = *next_cont;
4623           
4624           return lp;
4625         }
4626       
4627       ch_width = find_char_width (text, &lp.end, &tab_mark);
4628       
4629       if ((ch_width + lp.pixel_width > max_display_pixels) &&
4630           (lp.end.index > lp.start.index))
4631         {
4632           lp.wraps = 1;
4633           
4634           if (text->line_wrap)
4635             {
4636               next_cont->tab_start    = tab_mark;
4637               next_cont->pixel_offset = 0;
4638               
4639               if (ch == '\t')
4640                 {
4641                   /* Here's the tough case, a tab is wrapping. */
4642                   gint pixels_avail = max_display_pixels - lp.pixel_width;
4643                   gint space_width  = MARK_CURRENT_TEXT_FONT(text, &lp.end)->char_widths[' '];
4644                   gint spaces_avail = pixels_avail / space_width;
4645                   
4646                   if (spaces_avail == 0)
4647                     {
4648                       decrement_mark (&lp.end);
4649                     }
4650                   else
4651                     {
4652                       advance_tab_mark (text, &next_cont->tab_start, '\t');
4653                       next_cont->pixel_offset = space_width * (tab_mark.to_next_tab -
4654                                                                spaces_avail);
4655                       lp.displayable_chars += 1;
4656                     }
4657                 }
4658               else
4659                 {
4660                   if (text->word_wrap)
4661                     {
4662                       GtkPropertyMark saved_mark = lp.end;
4663                       guint saved_characters = lp.displayable_chars;
4664                       
4665                       lp.displayable_chars += 1;
4666                       
4667                       if (text->use_wchar)
4668                         {
4669                           while (!gdk_iswspace (GTK_TEXT_INDEX (text, lp.end.index)) &&
4670                                  (lp.end.index > lp.start.index))
4671                             {
4672                               decrement_mark (&lp.end);
4673                               lp.displayable_chars -= 1;
4674                             }
4675                         }
4676                       else
4677                         {
4678                           while (!isspace(GTK_TEXT_INDEX (text, lp.end.index)) &&
4679                                  (lp.end.index > lp.start.index))
4680                             {
4681                               decrement_mark (&lp.end);
4682                               lp.displayable_chars -= 1;
4683                             }
4684                         }
4685                       
4686                       /* If whole line is one word, revert to char wrapping */
4687                       if (lp.end.index == lp.start.index)
4688                         {
4689                           lp.end = saved_mark;
4690                           lp.displayable_chars = saved_characters;
4691                           decrement_mark (&lp.end);
4692                         }
4693                     }
4694                   else
4695                     {
4696                       /* Don't include this character, it will wrap. */
4697                       decrement_mark (&lp.end);
4698                     }
4699                 }
4700               
4701               lp.tab_cont_next = *next_cont;
4702               
4703               return lp;
4704             }
4705         }
4706       else
4707         {
4708           lp.displayable_chars += 1;
4709         }
4710       
4711       lp.font_ascent = MAX (font->ascent, lp.font_ascent);
4712       lp.font_descent = MAX (font->descent, lp.font_descent);
4713       lp.pixel_width  += ch_width;
4714       
4715       advance_mark(&lp.end);
4716       advance_tab_mark (text, &tab_mark, ch);
4717     }
4718   
4719   if (LAST_INDEX(text, lp.start))
4720     {
4721       /* Special case, empty last line. */
4722       font = MARK_CURRENT_FONT (text, &lp.end);
4723
4724       lp.font_ascent = font->ascent;
4725       lp.font_descent = font->descent;
4726     }
4727   
4728   lp.tab_cont_next = *next_cont;
4729   
4730   return lp;
4731 }
4732
4733 static void
4734 expand_scratch_buffer (GtkText* text, guint len)
4735 {
4736   if (len >= text->scratch_buffer_len)
4737     {
4738       guint i = 1;
4739       
4740       while (i <= len && i < MIN_GAP_SIZE) i <<= 1;
4741       
4742       if (text->use_wchar)
4743         {
4744           if (text->scratch_buffer.wc)
4745             text->scratch_buffer.wc = g_new (GdkWChar, i);
4746           else
4747             text->scratch_buffer.wc = g_realloc (text->scratch_buffer.wc,
4748                                               i*sizeof (GdkWChar));
4749         }
4750       else
4751         {
4752           if (text->scratch_buffer.ch)
4753             text->scratch_buffer.ch = g_new (guchar, i);
4754           else
4755             text->scratch_buffer.ch = g_realloc (text->scratch_buffer.ch, i);
4756         }
4757       
4758       text->scratch_buffer_len = i;
4759     }
4760 }
4761
4762 /* Side effect: modifies text->gc
4763  */
4764 static void
4765 draw_bg_rect (GtkText* text, GtkPropertyMark *mark,
4766               gint x, gint y, gint width, gint height,
4767               gboolean already_cleared)
4768 {
4769   GtkOldEditable *old_editable = GTK_OLD_EDITABLE (text);
4770
4771   if ((mark->index >= MIN(old_editable->selection_start_pos, old_editable->selection_end_pos) &&
4772        mark->index < MAX(old_editable->selection_start_pos, old_editable->selection_end_pos)))
4773     {
4774       gtk_paint_flat_box(GTK_WIDGET(text)->style, text->text_area,
4775                          old_editable->has_selection ?
4776                             GTK_STATE_SELECTED : GTK_STATE_ACTIVE, 
4777                          GTK_SHADOW_NONE,
4778                          NULL, GTK_WIDGET(text), "text",
4779                          x, y, width, height);
4780     }
4781   else if (!gdk_color_equal(MARK_CURRENT_BACK (text, mark),
4782                             &GTK_WIDGET(text)->style->base[GTK_WIDGET_STATE (text)]))
4783     {
4784       gdk_gc_set_foreground (text->gc, MARK_CURRENT_BACK (text, mark));
4785
4786       gdk_draw_rectangle (text->text_area,
4787                           text->gc,
4788                           TRUE, x, y, width, height);
4789     }
4790   else if (GTK_WIDGET (text)->style->bg_pixmap[GTK_STATE_NORMAL])
4791     {
4792       GdkRectangle rect;
4793       
4794       rect.x = x;
4795       rect.y = y;
4796       rect.width = width;
4797       rect.height = height;
4798       
4799       clear_area (text, &rect);
4800     }
4801   else if (!already_cleared)
4802     gdk_window_clear_area (text->text_area, x, y, width, height);
4803 }
4804
4805 static void
4806 draw_line (GtkText* text,
4807            gint pixel_start_height,
4808            LineParams* lp)
4809 {
4810   GdkGCValues gc_values;
4811   gint i;
4812   gint len = 0;
4813   guint running_offset = lp->tab_cont.pixel_offset;
4814   union { GdkWChar *wc; guchar *ch; } buffer;
4815   GdkGC *fg_gc;
4816   
4817   GtkOldEditable *old_editable = GTK_OLD_EDITABLE (text);
4818   
4819   guint selection_start_pos = MIN (old_editable->selection_start_pos, old_editable->selection_end_pos);
4820   guint selection_end_pos = MAX (old_editable->selection_start_pos, old_editable->selection_end_pos);
4821   
4822   GtkPropertyMark mark = lp->start;
4823   TabStopMark tab_mark = lp->tab_cont.tab_start;
4824   gint pixel_height = pixel_start_height + lp->font_ascent;
4825   guint chars = lp->displayable_chars;
4826   
4827   /* First provide a contiguous segment of memory.  This makes reading
4828    * the code below *much* easier, and only incurs the cost of copying
4829    * when the line being displayed spans the gap. */
4830   if (mark.index <= text->gap_position &&
4831       mark.index + chars > text->gap_position)
4832     {
4833       expand_scratch_buffer (text, chars);
4834       
4835       if (text->use_wchar)
4836         {
4837           for (i = 0; i < chars; i += 1)
4838             text->scratch_buffer.wc[i] = GTK_TEXT_INDEX(text, mark.index + i);
4839           buffer.wc = text->scratch_buffer.wc;
4840         }
4841       else
4842         {
4843           for (i = 0; i < chars; i += 1)
4844             text->scratch_buffer.ch[i] = GTK_TEXT_INDEX(text, mark.index + i);
4845           buffer.ch = text->scratch_buffer.ch;
4846         }
4847     }
4848   else
4849     {
4850       if (text->use_wchar)
4851         {
4852           if (mark.index >= text->gap_position)
4853             buffer.wc = text->text.wc + mark.index + text->gap_size;
4854           else
4855             buffer.wc = text->text.wc + mark.index;
4856         }
4857       else
4858         {
4859           if (mark.index >= text->gap_position)
4860             buffer.ch = text->text.ch + mark.index + text->gap_size;
4861           else
4862             buffer.ch = text->text.ch + mark.index;
4863         }
4864     }
4865   
4866   
4867   if (running_offset > 0)
4868     {
4869       draw_bg_rect (text, &mark, 0, pixel_start_height, running_offset,
4870                     LINE_HEIGHT (*lp), TRUE);
4871     }
4872   
4873   while (chars > 0)
4874     {
4875       len = 0;
4876       if ((text->use_wchar && buffer.wc[0] != '\t') ||
4877           (!text->use_wchar && buffer.ch[0] != '\t'))
4878         {
4879           union { GdkWChar *wc; guchar *ch; } next_tab;
4880           gint pixel_width;
4881           GdkFont *font;
4882
4883           next_tab.wc = NULL;
4884           if (text->use_wchar)
4885             for (i=0; i<chars; i++)
4886               {
4887                 if (buffer.wc[i] == '\t')
4888                   {
4889                     next_tab.wc = buffer.wc + i;
4890                     break;
4891                   }
4892               }
4893           else
4894             next_tab.ch = memchr (buffer.ch, '\t', chars);
4895
4896           len = MIN (MARK_CURRENT_PROPERTY (&mark)->length - mark.offset, chars);
4897           
4898           if (text->use_wchar)
4899             {
4900               if (next_tab.wc)
4901                 len = MIN (len, next_tab.wc - buffer.wc);
4902             }
4903           else
4904             {
4905               if (next_tab.ch)
4906                 len = MIN (len, next_tab.ch - buffer.ch);
4907             }
4908
4909           if (mark.index < selection_start_pos)
4910             len = MIN (len, selection_start_pos - mark.index);
4911           else if (mark.index < selection_end_pos)
4912             len = MIN (len, selection_end_pos - mark.index);
4913
4914           font = MARK_CURRENT_FONT (text, &mark);
4915           if (font->type == GDK_FONT_FONT)
4916             {
4917               gdk_gc_set_font (text->gc, font);
4918               gdk_gc_get_values (text->gc, &gc_values);
4919               if (text->use_wchar)
4920                 pixel_width = gdk_text_width_wc (gc_values.font,
4921                                                  buffer.wc, len);
4922               else
4923               pixel_width = gdk_text_width (gc_values.font,
4924                                               buffer.ch, len);
4925             }
4926           else
4927             {
4928               if (text->use_wchar)
4929                 pixel_width = gdk_text_width_wc (font, buffer.wc, len);
4930               else
4931                 pixel_width = gdk_text_width (font, buffer.ch, len);
4932             }
4933           
4934           draw_bg_rect (text, &mark, running_offset, pixel_start_height,
4935                         pixel_width, LINE_HEIGHT (*lp), TRUE);
4936           
4937           if ((mark.index >= selection_start_pos) && 
4938               (mark.index < selection_end_pos))
4939             {
4940               if (old_editable->has_selection)
4941                 fg_gc = GTK_WIDGET(text)->style->text_gc[GTK_STATE_SELECTED];
4942               else
4943                 fg_gc = GTK_WIDGET(text)->style->text_gc[GTK_STATE_ACTIVE];
4944             }
4945           else
4946             {
4947               gdk_gc_set_foreground (text->gc, MARK_CURRENT_FORE (text, &mark));
4948               fg_gc = text->gc;
4949             }
4950
4951           if (text->use_wchar)
4952             gdk_draw_text_wc (text->text_area, MARK_CURRENT_FONT (text, &mark),
4953                               fg_gc,
4954                               running_offset,
4955                               pixel_height,
4956                               buffer.wc,
4957                               len);
4958           else
4959             gdk_draw_text (text->text_area, MARK_CURRENT_FONT (text, &mark),
4960                            fg_gc,
4961                            running_offset,
4962                            pixel_height,
4963                            buffer.ch,
4964                            len);
4965           
4966           running_offset += pixel_width;
4967           
4968           advance_tab_mark_n (text, &tab_mark, len);
4969         }
4970       else
4971         {
4972           gint pixels_remaining;
4973           gint space_width;
4974           gint spaces_avail;
4975               
4976           len = 1;
4977           
4978           gdk_window_get_size (text->text_area, &pixels_remaining, NULL);
4979           pixels_remaining -= (LINE_WRAP_ROOM + running_offset);
4980           
4981           space_width = MARK_CURRENT_TEXT_FONT(text, &mark)->char_widths[' '];
4982           
4983           spaces_avail = pixels_remaining / space_width;
4984           spaces_avail = MIN (spaces_avail, tab_mark.to_next_tab);
4985
4986           draw_bg_rect (text, &mark, running_offset, pixel_start_height,
4987                         spaces_avail * space_width, LINE_HEIGHT (*lp), TRUE);
4988
4989           running_offset += tab_mark.to_next_tab *
4990             MARK_CURRENT_TEXT_FONT(text, &mark)->char_widths[' '];
4991
4992           advance_tab_mark (text, &tab_mark, '\t');
4993         }
4994       
4995       advance_mark_n (&mark, len);
4996       if (text->use_wchar)
4997         buffer.wc += len;
4998       else
4999         buffer.ch += len;
5000       chars -= len;
5001     }
5002 }
5003
5004 static void
5005 draw_line_wrap (GtkText* text, guint height /* baseline height */)
5006 {
5007   gint width;
5008   GdkPixmap *bitmap;
5009   gint bitmap_width;
5010   gint bitmap_height;
5011   
5012   if (text->line_wrap)
5013     {
5014       bitmap = text->line_wrap_bitmap;
5015       bitmap_width = line_wrap_width;
5016       bitmap_height = line_wrap_height;
5017     }
5018   else
5019     {
5020       bitmap = text->line_arrow_bitmap;
5021       bitmap_width = line_arrow_width;
5022       bitmap_height = line_arrow_height;
5023     }
5024   
5025   gdk_window_get_size (text->text_area, &width, NULL);
5026   width -= LINE_WRAP_ROOM;
5027   
5028   gdk_gc_set_stipple (text->gc,
5029                       bitmap);
5030   
5031   gdk_gc_set_fill (text->gc, GDK_STIPPLED);
5032   
5033   gdk_gc_set_foreground (text->gc, &GTK_WIDGET (text)->style->text[GTK_STATE_NORMAL]);
5034   
5035   gdk_gc_set_ts_origin (text->gc,
5036                         width + 1,
5037                         height - bitmap_height - 1);
5038   
5039   gdk_draw_rectangle (text->text_area,
5040                       text->gc,
5041                       TRUE,
5042                       width + 1,
5043                       height - bitmap_height - 1 /* one pixel above the baseline. */,
5044                       bitmap_width,
5045                       bitmap_height);
5046   
5047   gdk_gc_set_ts_origin (text->gc, 0, 0);
5048   
5049   gdk_gc_set_fill (text->gc, GDK_SOLID);
5050 }
5051
5052 static void
5053 undraw_cursor (GtkText* text, gint absolute)
5054 {
5055   GtkOldEditable *old_editable = (GtkOldEditable *) text;
5056
5057   TDEBUG (("in undraw_cursor\n"));
5058   
5059   if (absolute)
5060     text->cursor_drawn_level = 0;
5061   
5062   if ((text->cursor_drawn_level ++ == 0) &&
5063       (old_editable->selection_start_pos == old_editable->selection_end_pos) &&
5064       GTK_WIDGET_DRAWABLE (text) && text->line_start_cache)
5065     {
5066       GdkFont* font;
5067       
5068       g_assert(text->cursor_mark.property);
5069
5070       font = MARK_CURRENT_FONT(text, &text->cursor_mark);
5071
5072       draw_bg_rect (text, &text->cursor_mark, 
5073                     text->cursor_pos_x,
5074                     text->cursor_pos_y - text->cursor_char_offset - font->ascent,
5075                     1, font->ascent + 1, FALSE);
5076       
5077       if (text->cursor_char)
5078         {
5079           if (font->type == GDK_FONT_FONT)
5080             gdk_gc_set_font (text->gc, font);
5081
5082           gdk_gc_set_foreground (text->gc, MARK_CURRENT_FORE (text, &text->cursor_mark));
5083
5084           gdk_draw_text_wc (text->text_area, font,
5085                          text->gc,
5086                          text->cursor_pos_x,
5087                          text->cursor_pos_y - text->cursor_char_offset,
5088                          &text->cursor_char,
5089                          1);
5090         }
5091     }
5092 }
5093
5094 static gint
5095 drawn_cursor_min (GtkText* text)
5096 {
5097   GdkFont* font;
5098   
5099   g_assert(text->cursor_mark.property);
5100   
5101   font = MARK_CURRENT_FONT(text, &text->cursor_mark);
5102   
5103   return text->cursor_pos_y - text->cursor_char_offset - font->ascent;
5104 }
5105
5106 static gint
5107 drawn_cursor_max (GtkText* text)
5108 {
5109   GdkFont* font;
5110   
5111   g_assert(text->cursor_mark.property);
5112   
5113   font = MARK_CURRENT_FONT(text, &text->cursor_mark);
5114   
5115   return text->cursor_pos_y - text->cursor_char_offset;
5116 }
5117
5118 static void
5119 draw_cursor (GtkText* text, gint absolute)
5120 {
5121   GtkOldEditable *old_editable = (GtkOldEditable *)text;
5122   
5123   TDEBUG (("in draw_cursor\n"));
5124   
5125   if (absolute)
5126     text->cursor_drawn_level = 1;
5127   
5128   if ((--text->cursor_drawn_level == 0) &&
5129       old_editable->editable &&
5130       (old_editable->selection_start_pos == old_editable->selection_end_pos) &&
5131       GTK_WIDGET_DRAWABLE (text) && text->line_start_cache)
5132     {
5133       GdkFont* font;
5134       
5135       g_assert (text->cursor_mark.property);
5136
5137       font = MARK_CURRENT_FONT (text, &text->cursor_mark);
5138
5139       gdk_gc_set_foreground (text->gc, &GTK_WIDGET (text)->style->text[GTK_STATE_NORMAL]);
5140       
5141       gdk_draw_line (text->text_area, text->gc, text->cursor_pos_x,
5142                      text->cursor_pos_y - text->cursor_char_offset,
5143                      text->cursor_pos_x,
5144                      text->cursor_pos_y - text->cursor_char_offset - font->ascent);
5145     }
5146 }
5147
5148 static GdkGC *
5149 create_bg_gc (GtkText *text)
5150 {
5151   GdkGCValues values;
5152   
5153   values.tile = GTK_WIDGET (text)->style->bg_pixmap[GTK_STATE_NORMAL];
5154   values.fill = GDK_TILED;
5155
5156   return gdk_gc_new_with_values (text->text_area, &values,
5157                                  GDK_GC_FILL | GDK_GC_TILE);
5158 }
5159
5160 static void
5161 clear_area (GtkText *text, GdkRectangle *area)
5162 {
5163   GtkWidget *widget = GTK_WIDGET (text);
5164   
5165   if (text->bg_gc)
5166     {
5167       gint width, height;
5168       
5169       gdk_window_get_size (widget->style->bg_pixmap[GTK_STATE_NORMAL], &width, &height);
5170       
5171       gdk_gc_set_ts_origin (text->bg_gc,
5172                             (- text->first_onscreen_hor_pixel) % width,
5173                             (- text->first_onscreen_ver_pixel) % height);
5174
5175       gdk_draw_rectangle (text->text_area, text->bg_gc, TRUE,
5176                           area->x, area->y, area->width, area->height);
5177     }
5178   else
5179     gdk_window_clear_area (text->text_area, area->x, area->y, area->width, area->height);
5180 }
5181
5182 static void
5183 expose_text (GtkText* text, GdkRectangle *area, gboolean cursor)
5184 {
5185   GList *cache = text->line_start_cache;
5186   gint pixels = - text->first_cut_pixels;
5187   gint min_y = MAX (0, area->y);
5188   gint max_y = MAX (0, area->y + area->height);
5189   gint height;
5190   
5191   gdk_window_get_size (text->text_area, NULL, &height);
5192   max_y = MIN (max_y, height);
5193   
5194   TDEBUG (("in expose x=%d y=%d w=%d h=%d\n", area->x, area->y, area->width, area->height));
5195   
5196   clear_area (text, area);
5197   
5198   for (; pixels < height; cache = cache->next)
5199     {
5200       if (pixels < max_y && (pixels + (gint)LINE_HEIGHT(CACHE_DATA(cache))) >= min_y)
5201         {
5202           draw_line (text, pixels, &CACHE_DATA(cache));
5203           
5204           if (CACHE_DATA(cache).wraps)
5205             draw_line_wrap (text, pixels + CACHE_DATA(cache).font_ascent);
5206         }
5207       
5208       if (cursor && GTK_WIDGET_HAS_FOCUS (text))
5209         {
5210           if (CACHE_DATA(cache).start.index <= text->cursor_mark.index &&
5211               CACHE_DATA(cache).end.index >= text->cursor_mark.index)
5212             {
5213               /* We undraw and draw the cursor here to get the drawn
5214                * level right ... FIXME - maybe the second parameter
5215                * of draw_cursor should work differently
5216                */
5217               undraw_cursor (text, FALSE);
5218               draw_cursor (text, FALSE);
5219             }
5220         }
5221       
5222       pixels += LINE_HEIGHT(CACHE_DATA(cache));
5223       
5224       if (!cache->next)
5225         {
5226           fetch_lines_forward (text, 1);
5227           
5228           if (!cache->next)
5229             break;
5230         }
5231     }
5232 }
5233
5234 static void 
5235 gtk_text_update_text (GtkOldEditable    *old_editable,
5236                       gint               start_pos,
5237                       gint               end_pos)
5238 {
5239   GtkText *text = GTK_TEXT (old_editable);
5240   
5241   GList *cache = text->line_start_cache;
5242   gint pixels = - text->first_cut_pixels;
5243   GdkRectangle area;
5244   gint width;
5245   gint height;
5246   
5247   if (end_pos < 0)
5248     end_pos = TEXT_LENGTH (text);
5249   
5250   if (end_pos < start_pos)
5251     return;
5252   
5253   gdk_window_get_size (text->text_area, &width, &height);
5254   area.x = 0;
5255   area.y = -1;
5256   area.width = width;
5257   area.height = 0;
5258   
5259   TDEBUG (("in expose span start=%d stop=%d\n", start_pos, end_pos));
5260   
5261   for (; pixels < height; cache = cache->next)
5262     {
5263       if (CACHE_DATA(cache).start.index < end_pos)
5264         {
5265           if (CACHE_DATA(cache).end.index >= start_pos)
5266             {
5267               if (area.y < 0)
5268                 area.y = MAX(0,pixels);
5269               area.height = pixels + LINE_HEIGHT(CACHE_DATA(cache)) - area.y;
5270             }
5271         }
5272       else
5273         break;
5274       
5275       pixels += LINE_HEIGHT(CACHE_DATA(cache));
5276       
5277       if (!cache->next)
5278         {
5279           fetch_lines_forward (text, 1);
5280           
5281           if (!cache->next)
5282             break;
5283         }
5284     }
5285   
5286   if (area.y >= 0)
5287     expose_text (text, &area, TRUE);
5288 }
5289
5290 static void
5291 recompute_geometry (GtkText* text)
5292 {
5293   GtkPropertyMark mark, start_mark;
5294   GList *new_lines;
5295   gint height;
5296   gint width;
5297   
5298   free_cache (text);
5299   
5300   mark = start_mark = set_vertical_scroll (text);
5301
5302   /* We need a real start of a line when calling fetch_lines().
5303    * not the start of a wrapped line.
5304    */
5305   while (mark.index > 0 &&
5306          GTK_TEXT_INDEX (text, mark.index - 1) != LINE_DELIM)
5307     decrement_mark (&mark);
5308
5309   gdk_window_get_size (text->text_area, &width, &height);
5310
5311   /* Fetch an entire line, to make sure that we get all the text
5312    * we backed over above, in addition to enough text to fill up
5313    * the space vertically
5314    */
5315
5316   new_lines = fetch_lines (text,
5317                            &mark,
5318                            NULL,
5319                            FetchLinesCount,
5320                            1);
5321
5322   mark = CACHE_DATA (g_list_last (new_lines)).end;
5323   if (!LAST_INDEX (text, mark))
5324     {
5325       advance_mark (&mark);
5326
5327       new_lines = g_list_concat (new_lines, 
5328                                  fetch_lines (text,
5329                                               &mark,
5330                                               NULL,
5331                                               FetchLinesPixels,
5332                                               height + text->first_cut_pixels));
5333     }
5334
5335   /* Now work forward to the actual first onscreen line */
5336
5337   while (CACHE_DATA (new_lines).start.index < start_mark.index)
5338     new_lines = new_lines->next;
5339   
5340   text->line_start_cache = new_lines;
5341   
5342   find_cursor (text, TRUE);
5343 }
5344
5345 /**********************************************************************/
5346 /*                            Selection                               */
5347 /**********************************************************************/
5348
5349 static void 
5350 gtk_text_set_selection  (GtkOldEditable  *old_editable,
5351                          gint             start,
5352                          gint             end)
5353 {
5354   GtkText *text = GTK_TEXT (old_editable);
5355   
5356   guint start1, end1, start2, end2;
5357   
5358   if (end < 0)
5359     end = TEXT_LENGTH (text);
5360   
5361   start1 = MIN(start,end);
5362   end1 = MAX(start,end);
5363   start2 = MIN(old_editable->selection_start_pos, old_editable->selection_end_pos);
5364   end2 = MAX(old_editable->selection_start_pos, old_editable->selection_end_pos);
5365   
5366   if (start2 < start1)
5367     {
5368       guint tmp;
5369       
5370       tmp = start1; start1 = start2; start2 = tmp;
5371       tmp = end1;   end1   = end2;   end2   = tmp;
5372     }
5373   
5374   undraw_cursor (text, FALSE);
5375   old_editable->selection_start_pos = start;
5376   old_editable->selection_end_pos = end;
5377   draw_cursor (text, FALSE);
5378   
5379   /* Expose only what changed */
5380   
5381   if (start1 < start2)
5382     gtk_text_update_text (old_editable, start1, MIN(end1, start2));
5383   
5384   if (end2 > end1)
5385     gtk_text_update_text (old_editable, MAX(end1, start2), end2);
5386   else if (end2 < end1)
5387     gtk_text_update_text (old_editable, end2, end1);
5388 }
5389
5390
5391 /**********************************************************************/
5392 /*                              Debug                                 */
5393 /**********************************************************************/
5394
5395 #ifdef DEBUG_GTK_TEXT
5396 static void
5397 gtk_text_show_cache_line (GtkText *text, GList *cache,
5398                           const char* what, const char* func, gint line)
5399 {
5400   LineParams *lp = &CACHE_DATA(cache);
5401   gint i;
5402   
5403   if (cache == text->line_start_cache)
5404     g_message ("Line Start Cache: ");
5405   
5406   if (cache == text->current_line)
5407     g_message("Current Line: ");
5408   
5409   g_message ("%s:%d: cache line %s s=%d,e=%d,lh=%d (",
5410              func,
5411              line,
5412              what,
5413              lp->start.index,
5414              lp->end.index,
5415              LINE_HEIGHT(*lp));
5416   
5417   for (i = lp->start.index; i < (lp->end.index + lp->wraps); i += 1)
5418     g_message ("%c", GTK_TEXT_INDEX (text, i));
5419   
5420   g_message (")\n");
5421 }
5422
5423 static void
5424 gtk_text_show_cache (GtkText *text, const char* func, gint line)
5425 {
5426   GList *l = text->line_start_cache;
5427   
5428   if (!l) {
5429     return;
5430   }
5431   
5432   /* back up to the absolute beginning of the line cache */
5433   while (l->prev)
5434     l = l->prev;
5435   
5436   g_message ("*** line cache ***\n");
5437   for (; l; l = l->next)
5438     gtk_text_show_cache_line (text, l, "all", func, line);
5439 }
5440
5441 static void
5442 gtk_text_assert_mark (GtkText         *text,
5443                       GtkPropertyMark *mark,
5444                       GtkPropertyMark *before,
5445                       GtkPropertyMark *after,
5446                       const gchar     *msg,
5447                       const gchar     *where,
5448                       gint             line)
5449 {
5450   GtkPropertyMark correct_mark = find_mark (text, mark->index);
5451   
5452   if (mark->offset != correct_mark.offset ||
5453       mark->property != correct_mark.property)
5454     g_warning ("incorrect %s text property marker in %s:%d, index %d -- bad!", where, msg, line, mark->index);
5455 }
5456
5457 static void
5458 gtk_text_assert (GtkText         *text,
5459                  const gchar     *msg,
5460                  gint             line)
5461 {
5462   GList* cache = text->line_start_cache;
5463   GtkPropertyMark* before_mark = NULL;
5464   GtkPropertyMark* after_mark = NULL;
5465   
5466   gtk_text_show_props (text, msg, line);
5467   
5468   for (; cache->prev; cache = cache->prev)
5469     /* nothing */;
5470   
5471   g_message ("*** line markers ***\n");
5472   
5473   for (; cache; cache = cache->next)
5474     {
5475       after_mark = &CACHE_DATA(cache).end;
5476       gtk_text_assert_mark (text, &CACHE_DATA(cache).start, before_mark, after_mark, msg, "start", line);
5477       before_mark = &CACHE_DATA(cache).start;
5478       
5479       if (cache->next)
5480         after_mark = &CACHE_DATA(cache->next).start;
5481       else
5482         after_mark = NULL;
5483       
5484       gtk_text_assert_mark (text, &CACHE_DATA(cache).end, before_mark, after_mark, msg, "end", line);
5485       before_mark = &CACHE_DATA(cache).end;
5486     }
5487 }
5488
5489 static void
5490 gtk_text_show_adj (GtkText *text,
5491                    GtkAdjustment *adj,
5492                    const char* what,
5493                    const char* func,
5494                    gint line)
5495 {
5496   g_message ("*** adjustment ***\n");
5497   
5498   g_message ("%s:%d: %s adjustment l=%.1f u=%.1f v=%.1f si=%.1f pi=%.1f ps=%.1f\n",
5499              func,
5500              line,
5501              what,
5502              adj->lower,
5503              adj->upper,
5504              adj->value,
5505              adj->step_increment,
5506              adj->page_increment,
5507              adj->page_size);
5508 }
5509
5510 static void
5511 gtk_text_show_props (GtkText *text,
5512                      const char* msg,
5513                      int line)
5514 {
5515   GList* props = text->text_properties;
5516   int proplen = 0;
5517   
5518   g_message ("%s:%d: ", msg, line);
5519   
5520   for (; props; props = props->next)
5521     {
5522       TextProperty *p = (TextProperty*)props->data;
5523       
5524       proplen += p->length;
5525
5526       g_message ("[%d,%p,", p->length, p);
5527       if (p->flags & PROPERTY_FONT)
5528         g_message ("%p,", p->font);
5529       else
5530         g_message ("-,");
5531       if (p->flags & PROPERTY_FOREGROUND)
5532         g_message ("%ld, ", p->fore_color.pixel);
5533       else
5534         g_message ("-,");
5535       if (p->flags & PROPERTY_BACKGROUND)
5536         g_message ("%ld] ", p->back_color.pixel);
5537       else
5538         g_message ("-] ");
5539     }
5540   
5541   g_message ("\n");
5542   
5543   if (proplen - 1 != TEXT_LENGTH(text))
5544     g_warning ("incorrect property list length in %s:%d -- bad!", msg, line);
5545 }
5546 #endif