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