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