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