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