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