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