]> Pileus Git - ~andy/gtk/blob - gtk/gtktext.c
urg, removed implementation of gtk_marshal_VOID__INT_INT_INT_INT. if
[~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       (GtkEditable       *editable,
223                                         const gchar       *new_text,
224                                         gint               new_text_length,
225                                         gint               *position);
226 static void gtk_text_delete_text       (GtkEditable        *editable,
227                                         gint               start_pos,
228                                         gint               end_pos);
229 static void gtk_text_update_text       (GtkEditable       *editable,
230                                         gint               start_pos,
231                                         gint               end_pos);
232 static gchar *gtk_text_get_chars       (GtkEditable       *editable,
233                                         gint               start,
234                                         gint               end);
235 static void gtk_text_set_selection     (GtkEditable       *editable,
236                                         gint               start,
237                                         gint               end);
238 static void gtk_text_real_set_editable (GtkEditable       *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         (GtkEditable *editable,
350                                           gint         x,
351                                           gint         y);
352 static void gtk_text_move_word           (GtkEditable *editable,
353                                           gint         n);
354 static void gtk_text_move_page           (GtkEditable *editable,
355                                           gint         x,
356                                           gint         y);
357 static void gtk_text_move_to_row         (GtkEditable *editable,
358                                           gint         row);
359 static void gtk_text_move_to_column      (GtkEditable *editable,
360                                           gint         row);
361 static void gtk_text_kill_char           (GtkEditable *editable,
362                                           gint         direction);
363 static void gtk_text_kill_word           (GtkEditable *editable,
364                                           gint         direction);
365 static void gtk_text_kill_line           (GtkEditable *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  (GtkEditable       *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_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   GtkEditableClass *editable_class;
541   
542   object_class = (GtkObjectClass*) class;
543   widget_class = (GtkWidgetClass*) class;
544   editable_class = (GtkEditableClass*) class;
545   parent_class = gtk_type_class (GTK_TYPE_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   editable_class->set_editable = gtk_text_real_set_editable;
570   editable_class->insert_text = gtk_text_insert_text;
571   editable_class->delete_text = gtk_text_delete_text;
572   
573   editable_class->move_cursor = gtk_text_move_cursor;
574   editable_class->move_word = gtk_text_move_word;
575   editable_class->move_page = gtk_text_move_page;
576   editable_class->move_to_row = gtk_text_move_to_row;
577   editable_class->move_to_column = gtk_text_move_to_column;
578   
579   editable_class->kill_char = gtk_text_kill_char;
580   editable_class->kill_word = gtk_text_kill_word;
581   editable_class->kill_line = gtk_text_kill_line;
582   
583   editable_class->update_text = gtk_text_update_text;
584   editable_class->get_chars   = gtk_text_get_chars;
585   editable_class->set_selection = gtk_text_set_selection;
586   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_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 (GtkEditable *editable,
793                             gboolean     is_editable)
794 {
795   GtkText *text;
796   
797   g_return_if_fail (editable != NULL);
798   g_return_if_fail (GTK_IS_TEXT (editable));
799   
800   text = GTK_TEXT (editable);
801   
802   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   GtkEditable *editable = GTK_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 < editable->selection_start_pos)
1036         editable->selection_start_pos += numwcs;
1037       if (text->point.index < editable->selection_end_pos)
1038         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   GtkEditable *editable = GTK_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 < editable->selection_start_pos)
1115     editable->selection_start_pos -= 
1116       MIN(nchars, editable->selection_start_pos - text->point.index);
1117   if (text->point.index < editable->selection_end_pos)
1118     editable->selection_end_pos -= 
1119       MIN(nchars, 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 (GtkEditable *editable,
1145                        gint position)
1146 {
1147   GtkText *text = (GtkText *) 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 (editable, 0, 0);
1154 }
1155
1156 static gchar *    
1157 gtk_text_get_chars (GtkEditable   *editable,
1158                     gint           start_pos,
1159                     gint           end_pos)
1160 {
1161   GtkText *text;
1162
1163   gchar *retval;
1164   
1165   g_return_val_if_fail (editable != NULL, NULL);
1166   g_return_val_if_fail (GTK_IS_TEXT (editable), NULL);
1167   text = GTK_TEXT (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   GtkEditable *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   editable = GTK_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 () && (editable->ic_attr = gdk_ic_attr_new ()) != NULL)
1345     {
1346       gint width, height;
1347       GdkColormap *colormap;
1348       GdkEventMask mask;
1349       GdkICAttr *attr = 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       editable->ic = gdk_ic_new (attr, attrmask);
1393       
1394       if (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 (editable->ic);
1400           gdk_window_set_events (text->text_area, mask);
1401           
1402           if (GTK_WIDGET_HAS_FOCUS (widget))
1403             gdk_im_begin (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 (editable->selection_start_pos != editable->selection_end_pos)
1413     gtk_editable_claim_selection (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_EDITABLE (widget)->ic)
1471     {
1472       gdk_ic_destroy (GTK_EDITABLE (widget)->ic);
1473       GTK_EDITABLE (widget)->ic = NULL;
1474     }
1475   if (GTK_EDITABLE (widget)->ic_attr)
1476     {
1477       gdk_ic_attr_destroy (GTK_EDITABLE (widget)->ic_attr);
1478       GTK_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   GtkEditable *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   editable = GTK_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 (editable->ic && (gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION))
1657         {
1658           gint width, height;
1659           
1660           gdk_window_get_size (text->text_area, &width, &height);
1661           editable->ic_attr->preedit_area.width = width;
1662           editable->ic_attr->preedit_area.height = height;
1663
1664           gdk_ic_set_attr (editable->ic,
1665                            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   GtkEditable *editable;
1746   static GdkAtom ctext_atom = GDK_NONE;
1747   
1748   g_return_val_if_fail (widget != NULL, FALSE);
1749   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
1750   g_return_val_if_fail (event != NULL, FALSE);
1751   
1752   if (ctext_atom == GDK_NONE)
1753     ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
1754   
1755   text = GTK_TEXT (widget);
1756   editable = GTK_EDITABLE (widget);
1757   
1758   if (text->button && (event->button != text->button))
1759     return FALSE;
1760   
1761   text->button = event->button;
1762   
1763   if (!GTK_WIDGET_HAS_FOCUS (widget))
1764     gtk_widget_grab_focus (widget);
1765   
1766   if (event->button == 1)
1767     {
1768       switch (event->type)
1769         {
1770         case GDK_BUTTON_PRESS:
1771           gtk_grab_add (widget);
1772           
1773           undraw_cursor (text, FALSE);
1774           find_mouse_cursor (text, (gint)event->x, (gint)event->y);
1775           draw_cursor (text, FALSE);
1776           
1777           /* Set it now, so we display things right. We'll unset it
1778            * later if things don't work out */
1779           editable->has_selection = TRUE;
1780           gtk_text_set_selection (GTK_EDITABLE(text),
1781                                   text->cursor_mark.index,
1782                                   text->cursor_mark.index);
1783           
1784           break;
1785           
1786         case GDK_2BUTTON_PRESS:
1787           gtk_text_select_word (text, event->time);
1788           break;
1789           
1790         case GDK_3BUTTON_PRESS:
1791           gtk_text_select_line (text, event->time);
1792           break;
1793           
1794         default:
1795           break;
1796         }
1797     }
1798   else if (event->type == GDK_BUTTON_PRESS)
1799     {
1800       if ((event->button == 2) && editable->editable)
1801         {
1802           if (editable->selection_start_pos == editable->selection_end_pos ||
1803               editable->has_selection)
1804             {
1805               undraw_cursor (text, FALSE);
1806               find_mouse_cursor (text, (gint)event->x, (gint)event->y);
1807               draw_cursor (text, FALSE);
1808               
1809             }
1810           
1811           gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
1812                                  ctext_atom, event->time);
1813         }
1814       else
1815         {
1816           gtk_grab_add (widget);
1817           
1818           undraw_cursor (text, FALSE);
1819           find_mouse_cursor (text, event->x, event->y);
1820           draw_cursor (text, FALSE);
1821           
1822           gtk_text_set_selection (GTK_EDITABLE(text),
1823                                   text->cursor_mark.index,
1824                                   text->cursor_mark.index);
1825           
1826           editable->has_selection = FALSE;
1827           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
1828             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
1829         }
1830     }
1831   
1832   return FALSE;
1833 }
1834
1835 static gint
1836 gtk_text_button_release (GtkWidget      *widget,
1837                          GdkEventButton *event)
1838 {
1839   GtkText *text;
1840   GtkEditable *editable;
1841   g_return_val_if_fail (widget != NULL, FALSE);
1842   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
1843   g_return_val_if_fail (event != NULL, FALSE);
1844   
1845   text = GTK_TEXT (widget);
1846   
1847   gtk_grab_remove (widget);
1848   
1849   if (text->button != event->button)
1850     return FALSE;
1851   
1852   text->button = 0;
1853   
1854   if (text->timer)
1855     {
1856       gtk_timeout_remove (text->timer);
1857       text->timer = 0;
1858     }
1859   
1860   if (event->button == 1)
1861     {
1862       text = GTK_TEXT (widget);
1863       editable = GTK_EDITABLE (widget);
1864       
1865       gtk_grab_remove (widget);
1866       
1867       editable->has_selection = FALSE;
1868       if (editable->selection_start_pos != editable->selection_end_pos)
1869         {
1870           if (gtk_selection_owner_set (widget,
1871                                        GDK_SELECTION_PRIMARY,
1872                                        event->time))
1873             editable->has_selection = TRUE;
1874           else
1875             gtk_text_update_text (editable, editable->selection_start_pos,
1876                                   editable->selection_end_pos);
1877         }
1878       else
1879         {
1880           if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
1881             gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
1882         }
1883     }
1884   else if (event->button == 3)
1885     {
1886       gtk_grab_remove (widget);
1887     }
1888   
1889   undraw_cursor (text, FALSE);
1890   find_cursor (text, TRUE);
1891   draw_cursor (text, FALSE);
1892   
1893   return FALSE;
1894 }
1895
1896 static gint
1897 gtk_text_motion_notify (GtkWidget      *widget,
1898                         GdkEventMotion *event)
1899 {
1900   GtkText *text;
1901   gint x, y;
1902   gint height;
1903   GdkModifierType mask;
1904   
1905   g_return_val_if_fail (widget != NULL, FALSE);
1906   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
1907   g_return_val_if_fail (event != NULL, FALSE);
1908   
1909   text = GTK_TEXT (widget);
1910   
1911   x = event->x;
1912   y = event->y;
1913   mask = event->state;
1914   if (event->is_hint || (text->text_area != event->window))
1915     {
1916       gdk_window_get_pointer (text->text_area, &x, &y, &mask);
1917     }
1918   
1919   if ((text->button == 0) ||
1920       !(mask & (GDK_BUTTON1_MASK | GDK_BUTTON3_MASK)))
1921     return FALSE;
1922   
1923   gdk_window_get_size (text->text_area, NULL, &height);
1924   
1925   if ((y < 0) || (y > height))
1926     {
1927       if (text->timer == 0)
1928         {
1929           text->timer = gtk_timeout_add (SCROLL_TIME, 
1930                                          gtk_text_scroll_timeout,
1931                                          text);
1932           
1933           if (y < 0)
1934             scroll_int (text, y/2);
1935           else
1936             scroll_int (text, (y - height)/2);
1937         }
1938       else
1939         return FALSE;
1940     }
1941   
1942   undraw_cursor (GTK_TEXT (widget), FALSE);
1943   find_mouse_cursor (GTK_TEXT (widget), x, y);
1944   draw_cursor (GTK_TEXT (widget), FALSE);
1945   
1946   gtk_text_set_selection (GTK_EDITABLE(text), 
1947                           GTK_EDITABLE(text)->selection_start_pos,
1948                           text->cursor_mark.index);
1949   
1950   return FALSE;
1951 }
1952
1953 static void 
1954 gtk_text_insert_text    (GtkEditable       *editable,
1955                          const gchar       *new_text,
1956                          gint               new_text_length,
1957                          gint              *position)
1958 {
1959   GtkText *text = GTK_TEXT (editable);
1960   GdkFont *font;
1961   GdkColor *fore, *back;
1962
1963   TextProperty *property;
1964
1965   gtk_text_set_point (text, *position);
1966
1967   property = MARK_CURRENT_PROPERTY (&text->point);
1968   font = property->flags & PROPERTY_FONT ? property->font->gdk_font : NULL; 
1969   fore = property->flags & PROPERTY_FOREGROUND ? &property->fore_color : NULL; 
1970   back = property->flags & PROPERTY_BACKGROUND ? &property->back_color : NULL; 
1971   
1972   gtk_text_insert (text, font, fore, back, new_text, new_text_length);
1973
1974   *position = text->point.index;
1975 }
1976
1977 static void 
1978 gtk_text_delete_text    (GtkEditable       *editable,
1979                          gint               start_pos,
1980                          gint               end_pos)
1981 {
1982   GtkText *text;
1983   
1984   g_return_if_fail (start_pos >= 0);
1985   
1986   text = GTK_TEXT (editable);
1987   
1988   gtk_text_set_point (text, start_pos);
1989   if (end_pos < 0)
1990     end_pos = TEXT_LENGTH (text);
1991   
1992   if (end_pos > start_pos)
1993     gtk_text_forward_delete (text, end_pos - start_pos);
1994 }
1995
1996 static gint
1997 gtk_text_key_press (GtkWidget   *widget,
1998                     GdkEventKey *event)
1999 {
2000   GtkText *text;
2001   GtkEditable *editable;
2002   gchar key;
2003   gint return_val;
2004   gint position;
2005   
2006   g_return_val_if_fail (widget != NULL, FALSE);
2007   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
2008   g_return_val_if_fail (event != NULL, FALSE);
2009   
2010   return_val = FALSE;
2011   
2012   text = GTK_TEXT (widget);
2013   editable = GTK_EDITABLE (widget);
2014   
2015   key = event->keyval;
2016   return_val = TRUE;
2017   
2018   if ((GTK_EDITABLE(text)->editable == FALSE))
2019     {
2020       switch (event->keyval)
2021         {
2022         case GDK_Home:      
2023           if (event->state & GDK_CONTROL_MASK)
2024             scroll_int (text, -text->vadj->value);
2025           else
2026             return_val = FALSE;
2027           break;
2028         case GDK_End:
2029           if (event->state & GDK_CONTROL_MASK)
2030             scroll_int (text, +text->vadj->upper); 
2031           else
2032             return_val = FALSE;
2033           break;
2034         case GDK_Page_Up:   scroll_int (text, -text->vadj->page_increment); break;
2035         case GDK_Page_Down: scroll_int (text, +text->vadj->page_increment); break;
2036         case GDK_Up:        scroll_int (text, -KEY_SCROLL_PIXELS); break;
2037         case GDK_Down:      scroll_int (text, +KEY_SCROLL_PIXELS); break;
2038         case GDK_Return:
2039           if (event->state & GDK_CONTROL_MASK)
2040             gtk_signal_emit_by_name (GTK_OBJECT (text), "activate");
2041           else
2042             return_val = FALSE;
2043           break;
2044         default:
2045           return_val = FALSE;
2046           break;
2047         }
2048     }
2049   else
2050     {
2051       gint extend_selection;
2052       gint extend_start;
2053       guint initial_pos = editable->current_pos;
2054       
2055       text->point = find_mark (text, text->cursor_mark.index);
2056       
2057       extend_selection = event->state & GDK_SHIFT_MASK;
2058       extend_start = FALSE;
2059       
2060       if (extend_selection)
2061         {
2062           editable->has_selection = TRUE;
2063           
2064           if (editable->selection_start_pos == editable->selection_end_pos)
2065             {
2066               editable->selection_start_pos = text->point.index;
2067               editable->selection_end_pos = text->point.index;
2068             }
2069           
2070           extend_start = (text->point.index == editable->selection_start_pos);
2071         }
2072       
2073       switch (event->keyval)
2074         {
2075         case GDK_Home:
2076           if (event->state & GDK_CONTROL_MASK)
2077             move_cursor_buffer_ver (text, -1);
2078           else
2079             gtk_text_move_beginning_of_line (text);
2080           break;
2081         case GDK_End:
2082           if (event->state & GDK_CONTROL_MASK)
2083             move_cursor_buffer_ver (text, +1);
2084           else
2085             gtk_text_move_end_of_line (text);
2086           break;
2087         case GDK_Page_Up:   move_cursor_page_ver (text, -1); break;
2088         case GDK_Page_Down: move_cursor_page_ver (text, +1); break;
2089           /* CUA has Ctrl-Up/Ctrl-Down as paragraph up down */
2090         case GDK_Up:        move_cursor_ver (text, -1); break;
2091         case GDK_Down:      move_cursor_ver (text, +1); break;
2092         case GDK_Left:
2093           if (event->state & GDK_CONTROL_MASK)
2094             gtk_text_move_backward_word (text);
2095           else
2096             move_cursor_hor (text, -1); 
2097           break;
2098         case GDK_Right:     
2099           if (event->state & GDK_CONTROL_MASK)
2100             gtk_text_move_forward_word (text);
2101           else
2102             move_cursor_hor (text, +1); 
2103           break;
2104           
2105         case GDK_BackSpace:
2106           if (event->state & GDK_CONTROL_MASK)
2107             gtk_text_delete_backward_word (text);
2108           else
2109             gtk_text_delete_backward_character (text);
2110           break;
2111         case GDK_Clear:
2112           gtk_text_delete_line (text);
2113           break;
2114         case GDK_Insert:
2115           if (event->state & GDK_SHIFT_MASK)
2116             {
2117               extend_selection = FALSE;
2118               gtk_editable_paste_clipboard (editable);
2119             }
2120           else if (event->state & GDK_CONTROL_MASK)
2121             {
2122               gtk_editable_copy_clipboard (editable);
2123             }
2124           else
2125             {
2126               /* gtk_toggle_insert(text) -- IMPLEMENT */
2127             }
2128           break;
2129         case GDK_Delete:
2130           if (event->state & GDK_CONTROL_MASK)
2131             gtk_text_delete_forward_word (text);
2132           else if (event->state & GDK_SHIFT_MASK)
2133             {
2134               extend_selection = FALSE;
2135               gtk_editable_cut_clipboard (editable);
2136             }
2137           else
2138             gtk_text_delete_forward_character (text);
2139           break;
2140         case GDK_Tab:
2141           position = text->point.index;
2142           gtk_editable_insert_text (editable, "\t", 1, &position);
2143           break;
2144         case GDK_Return:
2145           if (event->state & GDK_CONTROL_MASK)
2146             gtk_signal_emit_by_name (GTK_OBJECT (text), "activate");
2147           else
2148             {
2149               position = text->point.index;
2150               gtk_editable_insert_text (editable, "\n", 1, &position);
2151             }
2152           break;
2153         case GDK_Escape:
2154           /* Don't insert literally */
2155           return_val = FALSE;
2156           break;
2157           
2158         default:
2159           return_val = FALSE;
2160           
2161           if (event->state & GDK_CONTROL_MASK)
2162             {
2163               if ((key >= 'A') && (key <= 'Z'))
2164                 key -= 'A' - 'a';
2165               
2166               if ((key >= 'a') && (key <= 'z') && control_keys[(int) (key - 'a')])
2167                 {
2168                   (* control_keys[(int) (key - 'a')]) (editable, event->time);
2169                   return_val = TRUE;
2170                 }
2171               
2172               break;
2173             }
2174           else if (event->state & GDK_MOD1_MASK)
2175             {
2176               if ((key >= 'A') && (key <= 'Z'))
2177                 key -= 'A' - 'a';
2178               
2179               if ((key >= 'a') && (key <= 'z') && alt_keys[(int) (key - 'a')])
2180                 {
2181                   (* alt_keys[(int) (key - 'a')]) (editable, event->time);
2182                   return_val = TRUE;
2183                 }
2184               
2185               break;
2186             }
2187           else if (event->length > 0)
2188             {
2189               extend_selection = FALSE;
2190               
2191               gtk_editable_delete_selection (editable);
2192               position = text->point.index;
2193               gtk_editable_insert_text (editable, event->string, event->length, &position);
2194               
2195               return_val = TRUE;
2196             }
2197           else
2198             return_val = FALSE;
2199         }
2200       
2201       if (return_val && (editable->current_pos != initial_pos))
2202         {
2203           if (extend_selection)
2204             {
2205               if (editable->current_pos < editable->selection_start_pos)
2206                 gtk_text_set_selection (editable, editable->current_pos,
2207                                         editable->selection_end_pos);
2208               else if (editable->current_pos > editable->selection_end_pos)
2209                 gtk_text_set_selection (editable, editable->selection_start_pos,
2210                                         editable->current_pos);
2211               else
2212                 {
2213                   if (extend_start)
2214                     gtk_text_set_selection (editable, editable->current_pos,
2215                                             editable->selection_end_pos);
2216                   else
2217                     gtk_text_set_selection (editable, editable->selection_start_pos,
2218                                             editable->current_pos);
2219                 }
2220             }
2221           else
2222             gtk_text_set_selection (editable, 0, 0);
2223           
2224           gtk_editable_claim_selection (editable,
2225                                         editable->selection_start_pos != editable->selection_end_pos,
2226                                         event->time);
2227         }
2228     }
2229   
2230   return return_val;
2231 }
2232
2233 static gint
2234 gtk_text_focus_in (GtkWidget     *widget,
2235                    GdkEventFocus *event)
2236 {
2237   g_return_val_if_fail (widget != NULL, FALSE);
2238   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
2239   g_return_val_if_fail (event != NULL, FALSE);
2240   
2241   TDEBUG (("in gtk_text_focus_in\n"));
2242   
2243   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2244   gtk_widget_draw_focus (widget);
2245   
2246 #ifdef USE_XIM
2247   if (GTK_EDITABLE(widget)->ic)
2248     gdk_im_begin (GTK_EDITABLE(widget)->ic, GTK_TEXT(widget)->text_area);
2249 #endif
2250   
2251   draw_cursor (GTK_TEXT(widget), TRUE);
2252   
2253   return FALSE;
2254 }
2255
2256 static gint
2257 gtk_text_focus_out (GtkWidget     *widget,
2258                     GdkEventFocus *event)
2259 {
2260   g_return_val_if_fail (widget != NULL, FALSE);
2261   g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE);
2262   g_return_val_if_fail (event != NULL, FALSE);
2263   
2264   TDEBUG (("in gtk_text_focus_out\n"));
2265   
2266   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2267   gtk_widget_draw_focus (widget);
2268   
2269   undraw_cursor (GTK_TEXT(widget), TRUE);
2270   
2271 #ifdef USE_XIM
2272   gdk_im_end ();
2273 #endif
2274   
2275   return FALSE;
2276 }
2277
2278 static void
2279 gtk_text_adjustment (GtkAdjustment *adjustment,
2280                      GtkText       *text)
2281 {
2282   g_return_if_fail (adjustment != NULL);
2283   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
2284   g_return_if_fail (text != NULL);
2285   g_return_if_fail (GTK_IS_TEXT (text));
2286   
2287   /* Just ignore it if we haven't been size-allocated and realized yet */
2288   if (text->line_start_cache == NULL) 
2289     return;
2290   
2291   if (adjustment == text->hadj)
2292     {
2293       g_warning ("horizontal scrolling not implemented");
2294     }
2295   else
2296     {
2297       gint diff = ((gint)adjustment->value) - text->last_ver_value;
2298       
2299       if (diff != 0)
2300         {
2301           undraw_cursor (text, FALSE);
2302           
2303           if (diff > 0)
2304             scroll_down (text, diff);
2305           else /* if (diff < 0) */
2306             scroll_up (text, diff);
2307           
2308           draw_cursor (text, FALSE);
2309           
2310           text->last_ver_value = adjustment->value;
2311         }
2312     }
2313 }
2314
2315 static void
2316 gtk_text_disconnect (GtkAdjustment *adjustment,
2317                      GtkText       *text)
2318 {
2319   g_return_if_fail (adjustment != NULL);
2320   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
2321   g_return_if_fail (text != NULL);
2322   g_return_if_fail (GTK_IS_TEXT (text));
2323
2324   if (adjustment == text->hadj)
2325     gtk_text_set_adjustments (text, NULL, text->vadj);
2326   if (adjustment == text->vadj)
2327     gtk_text_set_adjustments (text, text->hadj, NULL);
2328 }
2329
2330
2331 static GtkPropertyMark
2332 find_this_line_start_mark (GtkText* text, guint point_position, const GtkPropertyMark* near)
2333 {
2334   GtkPropertyMark mark;
2335   
2336   mark = find_mark_near (text, point_position, near);
2337   
2338   while (mark.index > 0 &&
2339          GTK_TEXT_INDEX (text, mark.index - 1) != LINE_DELIM)
2340     decrement_mark (&mark);
2341   
2342   return mark;
2343 }
2344
2345 static void
2346 init_tab_cont (GtkText* text, PrevTabCont* tab_cont)
2347 {
2348   tab_cont->pixel_offset          = 0;
2349   tab_cont->tab_start.tab_stops   = text->tab_stops;
2350   tab_cont->tab_start.to_next_tab = (gulong) text->tab_stops->data;
2351   
2352   if (!tab_cont->tab_start.to_next_tab)
2353     tab_cont->tab_start.to_next_tab = text->default_tab_width;
2354 }
2355
2356 static void
2357 line_params_iterate (GtkText* text,
2358                      const GtkPropertyMark* mark0,
2359                      const PrevTabCont* tab_mark0,
2360                      gint8 alloc,
2361                      void* data,
2362                      LineIteratorFunction iter)
2363      /* mark0 MUST be a real line start.  if ALLOC, allocate line params
2364       * from a mem chunk.  DATA is passed to ITER_CALL, which is called
2365       * for each line following MARK, iteration continues unless ITER_CALL
2366       * returns TRUE. */
2367 {
2368   GtkPropertyMark mark = *mark0;
2369   PrevTabCont  tab_conts[2];
2370   LineParams   *lp, lpbuf;
2371   gint         tab_cont_index = 0;
2372   
2373   if (tab_mark0)
2374     tab_conts[0] = *tab_mark0;
2375   else
2376     init_tab_cont (text, tab_conts);
2377   
2378   for (;;)
2379     {
2380       if (alloc)
2381         lp = g_chunk_new (LineParams, params_mem_chunk);
2382       else
2383         lp = &lpbuf;
2384       
2385       *lp = find_line_params (text, &mark, tab_conts + tab_cont_index,
2386                               tab_conts + (tab_cont_index + 1) % 2);
2387       
2388       if ((*iter) (text, lp, data))
2389         return;
2390       
2391       if (LAST_INDEX (text, lp->end))
2392         break;
2393       
2394       mark = lp->end;
2395       advance_mark (&mark);
2396       tab_cont_index = (tab_cont_index + 1) % 2;
2397     }
2398 }
2399
2400 static gint
2401 fetch_lines_iterator (GtkText* text, LineParams* lp, void* data)
2402 {
2403   FetchLinesData *fldata = (FetchLinesData*) data;
2404   
2405   fldata->new_lines = g_list_prepend (fldata->new_lines, lp);
2406   
2407   switch (fldata->fl_type)
2408     {
2409     case FetchLinesCount:
2410       if (!text->line_wrap || !lp->wraps)
2411         fldata->data += 1;
2412       
2413       if (fldata->data >= fldata->data_max)
2414         return TRUE;
2415       
2416       break;
2417     case FetchLinesPixels:
2418       
2419       fldata->data += LINE_HEIGHT(*lp);
2420       
2421       if (fldata->data >= fldata->data_max)
2422         return TRUE;
2423       
2424       break;
2425     }
2426   
2427   return FALSE;
2428 }
2429
2430 static GList*
2431 fetch_lines (GtkText* text,
2432              const GtkPropertyMark* mark0,
2433              const PrevTabCont* tab_cont0,
2434              FLType fl_type,
2435              gint data)
2436 {
2437   FetchLinesData fl_data;
2438   
2439   fl_data.new_lines = NULL;
2440   fl_data.data      = 0;
2441   fl_data.data_max  = data;
2442   fl_data.fl_type   = fl_type;
2443   
2444   line_params_iterate (text, mark0, tab_cont0, TRUE, &fl_data, fetch_lines_iterator);
2445   
2446   return g_list_reverse (fl_data.new_lines);
2447 }
2448
2449 static void
2450 fetch_lines_backward (GtkText* text)
2451 {
2452   GList* new_lines = NULL, *new_line_start;
2453   GtkPropertyMark mark;
2454   
2455   if (CACHE_DATA(text->line_start_cache).start.index == 0)
2456     return;
2457   
2458   mark = find_this_line_start_mark (text,
2459                                     CACHE_DATA(text->line_start_cache).start.index - 1,
2460                                     &CACHE_DATA(text->line_start_cache).start);
2461   
2462   new_line_start = new_lines = fetch_lines (text, &mark, NULL, FetchLinesCount, 1);
2463   
2464   while (new_line_start->next)
2465     new_line_start = new_line_start->next;
2466   
2467   new_line_start->next = text->line_start_cache;
2468   text->line_start_cache->prev = new_line_start;
2469 }
2470
2471 static void
2472 fetch_lines_forward (GtkText* text, gint line_count)
2473 {
2474   GtkPropertyMark mark;
2475   GList* line = text->line_start_cache;
2476   
2477   while(line->next)
2478     line = line->next;
2479   
2480   mark = CACHE_DATA(line).end;
2481   
2482   if (LAST_INDEX (text, mark))
2483     return;
2484   
2485   advance_mark(&mark);
2486   
2487   line->next = fetch_lines (text, &mark, &CACHE_DATA(line).tab_cont_next, FetchLinesCount, line_count);
2488   
2489   if (line->next)
2490     line->next->prev = line;
2491 }
2492
2493 /* Compute the number of lines, and vertical pixels for n characters
2494  * starting from the point 
2495  */
2496 static void
2497 compute_lines_pixels (GtkText* text, guint char_count,
2498                       guint *lines, guint *pixels)
2499 {
2500   GList *line = text->current_line;
2501   gint chars_left = char_count;
2502   
2503   *lines = 0;
2504   *pixels = 0;
2505   
2506   /* If chars_left == 0, that means we're joining two lines in a
2507    * deletion, so add in the values for the next line as well 
2508    */
2509   for (; line && chars_left >= 0; line = line->next)
2510     {
2511       *pixels += LINE_HEIGHT(CACHE_DATA(line));
2512       
2513       if (line == text->current_line)
2514         chars_left -= CACHE_DATA(line).end.index - text->point.index + 1;
2515       else
2516         chars_left -= CACHE_DATA(line).end.index - CACHE_DATA(line).start.index + 1;
2517       
2518       if (!text->line_wrap || !CACHE_DATA(line).wraps)
2519         *lines += 1;
2520       else
2521         if (chars_left < 0)
2522           chars_left = 0;       /* force another loop */
2523       
2524       if (!line->next)
2525         fetch_lines_forward (text, 1);
2526     }
2527 }
2528
2529 static gint
2530 total_line_height (GtkText* text, GList* line, gint line_count)
2531 {
2532   gint height = 0;
2533   
2534   for (; line && line_count > 0; line = line->next)
2535     {
2536       height += LINE_HEIGHT(CACHE_DATA(line));
2537       
2538       if (!text->line_wrap || !CACHE_DATA(line).wraps)
2539         line_count -= 1;
2540       
2541       if (!line->next)
2542         fetch_lines_forward (text, line_count);
2543     }
2544   
2545   return height;
2546 }
2547
2548 static void
2549 swap_lines (GtkText* text, GList* old, GList* new, guint old_line_count)
2550 {
2551   if (old == text->line_start_cache)
2552     {
2553       GList* last;
2554       
2555       for (; old_line_count > 0; old_line_count -= 1)
2556         {
2557           while (text->line_start_cache &&
2558                  text->line_wrap &&
2559                  CACHE_DATA(text->line_start_cache).wraps)
2560             remove_cache_line(text, text->line_start_cache);
2561           
2562           remove_cache_line(text, text->line_start_cache);
2563         }
2564       
2565       last = g_list_last (new);
2566       
2567       last->next = text->line_start_cache;
2568       
2569       if (text->line_start_cache)
2570         text->line_start_cache->prev = last;
2571       
2572       text->line_start_cache = new;
2573     }
2574   else
2575     {
2576       GList *last;
2577       
2578       g_assert (old->prev);
2579       
2580       last = old->prev;
2581       
2582       for (; old_line_count > 0; old_line_count -= 1)
2583         {
2584           while (old && text->line_wrap && CACHE_DATA(old).wraps)
2585             old = remove_cache_line (text, old);
2586           
2587           old = remove_cache_line (text, old);
2588         }
2589       
2590       last->next = new;
2591       new->prev = last;
2592       
2593       last = g_list_last (new);
2594       
2595       last->next = old;
2596       
2597       if (old)
2598         old->prev = last;
2599     }
2600 }
2601
2602 static void
2603 correct_cache_delete (GtkText* text, gint nchars, gint lines)
2604 {
2605   GList* cache = text->current_line;
2606   gint i;
2607   
2608   for (i = 0; cache && i < lines; i += 1, cache = cache->next)
2609     /* nothing */;
2610   
2611   for (; cache; cache = cache->next)
2612     {
2613       GtkPropertyMark *start = &CACHE_DATA(cache).start;
2614       GtkPropertyMark *end = &CACHE_DATA(cache).end;
2615       
2616       start->index -= nchars;
2617       end->index -= nchars;
2618       
2619       if (LAST_INDEX (text, text->point) &&
2620           start->index == text->point.index)
2621         *start = text->point;
2622       else if (start->property == text->point.property)
2623         start->offset = start->index - (text->point.index - text->point.offset);
2624       
2625       if (LAST_INDEX (text, text->point) &&
2626           end->index == text->point.index)
2627         *end = text->point;
2628       if (end->property == text->point.property)
2629         end->offset = end->index - (text->point.index - text->point.offset);
2630       
2631       /*TEXT_ASSERT_MARK(text, start, "start");*/
2632       /*TEXT_ASSERT_MARK(text, end, "end");*/
2633     }
2634 }
2635
2636 static void
2637 delete_expose (GtkText* text, guint nchars, guint old_lines, guint old_pixels)
2638 {
2639   GtkWidget *widget = GTK_WIDGET (text);
2640   
2641   gint pixel_height;
2642   guint new_pixels = 0;
2643   GdkRectangle rect;
2644   GList* new_line = NULL;
2645   gint width, height;
2646   
2647   text->cursor_virtual_x = 0;
2648   
2649   correct_cache_delete (text, nchars, old_lines);
2650   
2651   pixel_height = pixel_height_of(text, text->current_line) -
2652     LINE_HEIGHT(CACHE_DATA(text->current_line));
2653   
2654   if (CACHE_DATA(text->current_line).start.index == text->point.index)
2655     CACHE_DATA(text->current_line).start = text->point;
2656   
2657   new_line = fetch_lines (text,
2658                           &CACHE_DATA(text->current_line).start,
2659                           &CACHE_DATA(text->current_line).tab_cont,
2660                           FetchLinesCount,
2661                           1);
2662   
2663   swap_lines (text, text->current_line, new_line, old_lines);
2664   
2665   text->current_line = new_line;
2666   
2667   new_pixels = total_line_height (text, new_line, 1);
2668   
2669   gdk_window_get_size (text->text_area, &width, &height);
2670   
2671   if (old_pixels != new_pixels)
2672     {
2673       if (!widget->style->bg_pixmap[GTK_STATE_NORMAL])
2674         {
2675           gdk_draw_pixmap (text->text_area,
2676                            text->gc,
2677                            text->text_area,
2678                            0,
2679                            pixel_height + old_pixels,
2680                            0,
2681                            pixel_height + new_pixels,
2682                            width,
2683                            height);
2684         }
2685       text->vadj->upper += new_pixels;
2686       text->vadj->upper -= old_pixels;
2687       adjust_adj (text, text->vadj);
2688     }
2689   
2690   rect.x = 0;
2691   rect.y = pixel_height;
2692   rect.width = width;
2693   rect.height = new_pixels;
2694   
2695   expose_text (text, &rect, FALSE);
2696   gtk_text_draw_focus ( (GtkWidget *) text);
2697   
2698   text->cursor_mark = text->point;
2699   
2700   find_cursor (text, TRUE);
2701   
2702   if (old_pixels != new_pixels)
2703     {
2704       if (widget->style->bg_pixmap[GTK_STATE_NORMAL])
2705         {
2706           rect.x = 0;
2707           rect.y = pixel_height + new_pixels;
2708           rect.width = width;
2709           rect.height = height - rect.y;
2710           
2711           expose_text (text, &rect, FALSE);
2712         }
2713       else
2714         process_exposes (text);
2715     }
2716   
2717   TEXT_ASSERT (text);
2718   TEXT_SHOW(text);
2719 }
2720
2721 /* note, the point has already been moved forward */
2722 static void
2723 correct_cache_insert (GtkText* text, gint nchars)
2724 {
2725   GList *cache;
2726   GtkPropertyMark *start;
2727   GtkPropertyMark *end;
2728   gboolean was_split = FALSE;
2729   
2730   /* We need to distinguish whether the property was split in the
2731    * insert or not, so we check if the point (which points after
2732    * the insertion here), points to the same character as the
2733    * point before. Ugh.
2734    */
2735   if (nchars > 0)
2736     {
2737       GtkPropertyMark tmp_mark = text->point;
2738       move_mark_n (&tmp_mark, -1);
2739       
2740       if (tmp_mark.property != text->point.property)
2741         was_split = TRUE;
2742     }
2743   
2744   /* If we inserted a property exactly at the beginning of the
2745    * line, we have to correct here, or fetch_lines will
2746    * fetch junk.
2747    */
2748   start = &CACHE_DATA(text->current_line).start;
2749
2750   /* Check if if we split exactly at the beginning of the line:
2751    * (was_split won't be set if we are inserting at the end of the text, 
2752    *  so we don't check)
2753    */
2754   if (start->offset ==  MARK_CURRENT_PROPERTY (start)->length)
2755     SET_PROPERTY_MARK (start, start->property->next, 0);
2756   /* Check if we inserted a property at the beginning of the text: */
2757   else if (was_split &&
2758            (start->property == text->point.property) &&
2759            (start->index == text->point.index - nchars))
2760     SET_PROPERTY_MARK (start, start->property->prev, 0);
2761
2762   /* Now correct the offsets, and check for start or end marks that
2763    * are after the point, yet point to a property before the point's
2764    * property. This indicates that they are meant to point to the
2765    * second half of a property we split in insert_text_property(), so
2766    * we fix them up that way.  
2767    */
2768   cache = text->current_line->next;
2769   
2770   for (; cache; cache = cache->next)
2771     {
2772       start = &CACHE_DATA(cache).start;
2773       end = &CACHE_DATA(cache).end;
2774       
2775       if (LAST_INDEX (text, text->point) &&
2776           start->index == text->point.index)
2777         *start = text->point;
2778       else if (start->index >= text->point.index - nchars)
2779         {
2780           if (!was_split && start->property == text->point.property)
2781             move_mark_n(start, nchars);
2782           else
2783             {
2784               if (start->property->next &&
2785                   (start->property->next->next == text->point.property))
2786                 {
2787                   g_assert (start->offset >=  MARK_CURRENT_PROPERTY (start)->length);
2788                   start->offset -= MARK_CURRENT_PROPERTY (start)->length;
2789                   start->property = text->point.property;
2790                 }
2791               start->index += nchars;
2792             }
2793         }
2794       
2795       if (LAST_INDEX (text, text->point) &&
2796           end->index == text->point.index)
2797         *end = text->point;
2798       if (end->index >= text->point.index - nchars)
2799         {
2800           if (!was_split && end->property == text->point.property)
2801             move_mark_n(end, nchars);
2802           else 
2803             {
2804               if (end->property->next &&
2805                   (end->property->next->next == text->point.property))
2806                 {
2807                   g_assert (end->offset >=  MARK_CURRENT_PROPERTY (end)->length);
2808                   end->offset -= MARK_CURRENT_PROPERTY (end)->length;
2809                   end->property = text->point.property;
2810                 }
2811               end->index += nchars;
2812             }
2813         }
2814       
2815       /*TEXT_ASSERT_MARK(text, start, "start");*/
2816       /*TEXT_ASSERT_MARK(text, end, "end");*/
2817     }
2818 }
2819
2820
2821 static void
2822 insert_expose (GtkText* text, guint old_pixels, gint nchars,
2823                guint new_line_count)
2824 {
2825   GtkWidget *widget = GTK_WIDGET (text);
2826   
2827   gint pixel_height;
2828   guint new_pixels = 0;
2829   GdkRectangle rect;
2830   GList* new_lines = NULL;
2831   gint width, height;
2832   
2833   text->cursor_virtual_x = 0;
2834   
2835   undraw_cursor (text, FALSE);
2836   
2837   correct_cache_insert (text, nchars);
2838   
2839   TEXT_SHOW_ADJ (text, text->vadj, "vadj");
2840   
2841   pixel_height = pixel_height_of(text, text->current_line) -
2842     LINE_HEIGHT(CACHE_DATA(text->current_line));
2843   
2844   new_lines = fetch_lines (text,
2845                            &CACHE_DATA(text->current_line).start,
2846                            &CACHE_DATA(text->current_line).tab_cont,
2847                            FetchLinesCount,
2848                            new_line_count);
2849   
2850   swap_lines (text, text->current_line, new_lines, 1);
2851   
2852   text->current_line = new_lines;
2853   
2854   new_pixels = total_line_height (text, new_lines, new_line_count);
2855   
2856   gdk_window_get_size (text->text_area, &width, &height);
2857   
2858   if (old_pixels != new_pixels)
2859     {
2860       if (!widget->style->bg_pixmap[GTK_STATE_NORMAL])
2861         {
2862           gdk_draw_pixmap (text->text_area,
2863                            text->gc,
2864                            text->text_area,
2865                            0,
2866                            pixel_height + old_pixels,
2867                            0,
2868                            pixel_height + new_pixels,
2869                            width,
2870                            height + (old_pixels - new_pixels) - pixel_height);
2871           
2872         }
2873       text->vadj->upper += new_pixels;
2874       text->vadj->upper -= old_pixels;
2875       adjust_adj (text, text->vadj);
2876     }
2877   
2878   rect.x = 0;
2879   rect.y = pixel_height;
2880   rect.width = width;
2881   rect.height = new_pixels;
2882   
2883   expose_text (text, &rect, FALSE);
2884   gtk_text_draw_focus ( (GtkWidget *) text);
2885   
2886   text->cursor_mark = text->point;
2887   
2888   find_cursor (text, TRUE);
2889   
2890   draw_cursor (text, FALSE);
2891   
2892   if (old_pixels != new_pixels)
2893     {
2894       if (widget->style->bg_pixmap[GTK_STATE_NORMAL])
2895         {
2896           rect.x = 0;
2897           rect.y = pixel_height + new_pixels;
2898           rect.width = width;
2899           rect.height = height - rect.y;
2900           
2901           expose_text (text, &rect, FALSE);
2902         }
2903       else
2904         process_exposes (text);
2905     }
2906   
2907   TEXT_SHOW_ADJ (text, text->vadj, "vadj");
2908   TEXT_ASSERT (text);
2909   TEXT_SHOW(text);
2910 }
2911
2912 /* Text property functions */
2913
2914 static guint
2915 font_hash (gconstpointer font)
2916 {
2917   return gdk_font_id ((const GdkFont*) font);
2918 }
2919
2920 static GHashTable *font_cache_table = NULL;
2921
2922 static GtkTextFont*
2923 get_text_font (GdkFont* gfont)
2924 {
2925   GtkTextFont* tf;
2926   gint i;
2927   
2928   if (!font_cache_table)
2929     font_cache_table = g_hash_table_new (font_hash, (GCompareFunc) gdk_font_equal);
2930   
2931   tf = g_hash_table_lookup (font_cache_table, gfont);
2932   
2933   if (tf)
2934     {
2935       tf->ref_count++;
2936       return tf;
2937     }
2938
2939   tf = g_new (GtkTextFont, 1);
2940   tf->ref_count = 1;
2941
2942   tf->gdk_font = gfont;
2943   gdk_font_ref (gfont);
2944   
2945   for(i = 0; i < 256; i += 1)
2946     tf->char_widths[i] = gdk_char_width (gfont, (char)i);
2947   
2948   g_hash_table_insert (font_cache_table, gfont, tf);
2949   
2950   return tf;
2951 }
2952
2953 static void
2954 text_font_unref (GtkTextFont *text_font)
2955 {
2956   text_font->ref_count--;
2957   if (text_font->ref_count == 0)
2958     {
2959       g_hash_table_remove (font_cache_table, text_font->gdk_font);
2960       gdk_font_unref (text_font->gdk_font);
2961       g_free (text_font);
2962     }
2963 }
2964
2965 static gint
2966 text_properties_equal (TextProperty* prop, GdkFont* font, GdkColor *fore, GdkColor *back)
2967 {
2968   if (prop->flags & PROPERTY_FONT)
2969     {
2970       gboolean retval;
2971       GtkTextFont *text_font;
2972
2973       if (!font)
2974         return FALSE;
2975
2976       text_font = get_text_font (font);
2977
2978       retval = (prop->font == text_font);
2979       text_font_unref (text_font);
2980       
2981       if (!retval)
2982         return FALSE;
2983     }
2984   else
2985     if (font != NULL)
2986       return FALSE;
2987
2988   if (prop->flags & PROPERTY_FOREGROUND)
2989     {
2990       if (!fore || !gdk_color_equal (&prop->fore_color, fore))
2991         return FALSE;
2992     }
2993   else
2994     if (fore != NULL)
2995       return FALSE;
2996
2997   if (prop->flags & PROPERTY_BACKGROUND)
2998     {
2999       if (!back || !gdk_color_equal (&prop->back_color, back))
3000         return FALSE;
3001     }
3002   else
3003     if (back != NULL)
3004       return FALSE;
3005   
3006   return TRUE;
3007 }
3008
3009 static void
3010 realize_property (GtkText *text, TextProperty *prop)
3011 {
3012   GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (text));
3013
3014   if (prop->flags & PROPERTY_FOREGROUND)
3015     gdk_colormap_alloc_color (colormap, &prop->fore_color, FALSE, FALSE);
3016   
3017   if (prop->flags & PROPERTY_BACKGROUND)
3018     gdk_colormap_alloc_color (colormap, &prop->back_color, FALSE, FALSE);
3019 }
3020
3021 static void
3022 realize_properties (GtkText *text)
3023 {
3024   GList *tmp_list = text->text_properties;
3025
3026   while (tmp_list)
3027     {
3028       realize_property (text, tmp_list->data);
3029       
3030       tmp_list = tmp_list->next;
3031     }
3032 }
3033
3034 static void
3035 unrealize_property (GtkText *text, TextProperty *prop)
3036 {
3037   GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (text));
3038
3039   if (prop->flags & PROPERTY_FOREGROUND)
3040     gdk_colormap_free_colors (colormap, &prop->fore_color, 1);
3041   
3042   if (prop->flags & PROPERTY_BACKGROUND)
3043     gdk_colormap_free_colors (colormap, &prop->back_color, 1);
3044 }
3045
3046 static void
3047 unrealize_properties (GtkText *text)
3048 {
3049   GList *tmp_list = text->text_properties;
3050
3051   while (tmp_list)
3052     {
3053       unrealize_property (text, tmp_list->data);
3054
3055       tmp_list = tmp_list->next;
3056     }
3057 }
3058
3059 static TextProperty*
3060 new_text_property (GtkText *text, GdkFont *font, GdkColor* fore, 
3061                    GdkColor* back, guint length)
3062 {
3063   TextProperty *prop;
3064   
3065   if (text_property_chunk == NULL)
3066     {
3067       text_property_chunk = g_mem_chunk_new ("text property mem chunk",
3068                                              sizeof(TextProperty),
3069                                              1024*sizeof(TextProperty),
3070                                              G_ALLOC_AND_FREE);
3071     }
3072   
3073   prop = g_chunk_new(TextProperty, text_property_chunk);
3074
3075   prop->flags = 0;
3076   if (font)
3077     {
3078       prop->flags |= PROPERTY_FONT;
3079       prop->font = get_text_font (font);
3080     }
3081   else
3082     prop->font = NULL;
3083   
3084   if (fore)
3085     {
3086       prop->flags |= PROPERTY_FOREGROUND;
3087       prop->fore_color = *fore;
3088     }
3089       
3090   if (back)
3091     {
3092       prop->flags |= PROPERTY_BACKGROUND;
3093       prop->back_color = *back;
3094     }
3095
3096   prop->length = length;
3097
3098   if (GTK_WIDGET_REALIZED (text))
3099     realize_property (text, prop);
3100
3101   return prop;
3102 }
3103
3104 static void
3105 destroy_text_property (TextProperty *prop)
3106 {
3107   if (prop->font)
3108     text_font_unref (prop->font);
3109   
3110   g_mem_chunk_free (text_property_chunk, prop);
3111 }
3112
3113 /* Flop the memory between the point and the gap around like a
3114  * dead fish. */
3115 static void
3116 move_gap (GtkText* text, guint index)
3117 {
3118   if (text->gap_position < index)
3119     {
3120       gint diff = index - text->gap_position;
3121       
3122       if (text->use_wchar)
3123         g_memmove (text->text.wc + text->gap_position,
3124                    text->text.wc + text->gap_position + text->gap_size,
3125                    diff*sizeof (GdkWChar));
3126       else
3127         g_memmove (text->text.ch + text->gap_position,
3128                    text->text.ch + text->gap_position + text->gap_size,
3129                    diff);
3130       
3131       text->gap_position = index;
3132     }
3133   else if (text->gap_position > index)
3134     {
3135       gint diff = text->gap_position - index;
3136       
3137       if (text->use_wchar)
3138         g_memmove (text->text.wc + index + text->gap_size,
3139                    text->text.wc + index,
3140                    diff*sizeof (GdkWChar));
3141       else
3142         g_memmove (text->text.ch + index + text->gap_size,
3143                    text->text.ch + index,
3144                    diff);
3145       
3146       text->gap_position = index;
3147     }
3148 }
3149
3150 /* Increase the gap size. */
3151 static void
3152 make_forward_space (GtkText* text, guint len)
3153 {
3154   if (text->gap_size < len)
3155     {
3156       guint sum = MAX(2*len, MIN_GAP_SIZE) + text->text_end;
3157       
3158       if (sum >= text->text_len)
3159         {
3160           guint i = 1;
3161           
3162           while (i <= sum) i <<= 1;
3163           
3164           if (text->use_wchar)
3165             text->text.wc = (GdkWChar *)g_realloc(text->text.wc,
3166                                                   i*sizeof(GdkWChar));
3167           else
3168             text->text.ch = (guchar *)g_realloc(text->text.ch, i);
3169           text->text_len = i;
3170         }
3171       
3172       if (text->use_wchar)
3173         g_memmove (text->text.wc + text->gap_position + text->gap_size + 2*len,
3174                    text->text.wc + text->gap_position + text->gap_size,
3175                    (text->text_end - (text->gap_position + text->gap_size))
3176                    *sizeof(GdkWChar));
3177       else
3178         g_memmove (text->text.ch + text->gap_position + text->gap_size + 2*len,
3179                    text->text.ch + text->gap_position + text->gap_size,
3180                    text->text_end - (text->gap_position + text->gap_size));
3181       
3182       text->text_end += len*2;
3183       text->gap_size += len*2;
3184     }
3185 }
3186
3187 /* Inserts into the text property list a list element that guarantees
3188  * that for len characters following the point, text has the correct
3189  * property.  does not move point.  adjusts text_properties_point and
3190  * text_properties_point_offset relative to the current value of
3191  * point. */
3192 static void
3193 insert_text_property (GtkText* text, GdkFont* font,
3194                       GdkColor *fore, GdkColor* back, guint len)
3195 {
3196   GtkPropertyMark *mark = &text->point;
3197   TextProperty* forward_prop = MARK_CURRENT_PROPERTY(mark);
3198   TextProperty* backward_prop = MARK_PREV_PROPERTY(mark);
3199   
3200   if (MARK_OFFSET(mark) == 0)
3201     {
3202       /* Point is on the boundary of two properties.
3203        * If it is the same as either, grow, else insert
3204        * a new one. */
3205       
3206       if (text_properties_equal(forward_prop, font, fore, back))
3207         {
3208           /* Grow the property in front of us. */
3209           
3210           MARK_PROPERTY_LENGTH(mark) += len;
3211         }
3212       else if (backward_prop &&
3213                text_properties_equal(backward_prop, font, fore, back))
3214         {
3215           /* Grow property behind us, point property and offset
3216            * change. */
3217           
3218           SET_PROPERTY_MARK (&text->point,
3219                              MARK_PREV_LIST_PTR (mark),
3220                              backward_prop->length);
3221           
3222           backward_prop->length += len;
3223         }
3224       else if ((MARK_NEXT_LIST_PTR(mark) == NULL) &&
3225                (forward_prop->length == 1))
3226         {
3227           /* Next property just has last position, take it over */
3228
3229           if (GTK_WIDGET_REALIZED (text))
3230             unrealize_property (text, forward_prop);
3231
3232           forward_prop->flags = 0;
3233           if (font)
3234             {
3235               forward_prop->flags |= PROPERTY_FONT;
3236               forward_prop->font = get_text_font (font);
3237             }
3238           else
3239             forward_prop->font = NULL;
3240             
3241           if (fore)
3242             {
3243               forward_prop->flags |= PROPERTY_FOREGROUND;
3244               forward_prop->fore_color = *fore;
3245             }
3246           if (back)
3247             {
3248               forward_prop->flags |= PROPERTY_BACKGROUND;
3249               forward_prop->back_color = *back;
3250             }
3251           forward_prop->length += len;
3252
3253           if (GTK_WIDGET_REALIZED (text))
3254             realize_property (text, forward_prop);
3255         }
3256       else
3257         {
3258           /* Splice a new property into the list. */
3259           
3260           GList* new_prop = g_list_alloc();
3261           
3262           new_prop->next = MARK_LIST_PTR(mark);
3263           new_prop->prev = MARK_PREV_LIST_PTR(mark);
3264           new_prop->next->prev = new_prop;
3265           
3266           if (new_prop->prev)
3267             new_prop->prev->next = new_prop;
3268
3269           new_prop->data = new_text_property (text, font, fore, back, len);
3270
3271           SET_PROPERTY_MARK (mark, new_prop, 0);
3272         }
3273     }
3274   else
3275     {
3276       /* The following will screw up the line_start cache,
3277        * we'll fix it up in correct_cache_insert
3278        */
3279       
3280       /* In the middle of forward_prop, if properties are equal,
3281        * just add to its length, else split it into two and splice
3282        * in a new one. */
3283       if (text_properties_equal (forward_prop, font, fore, back))
3284         {
3285           forward_prop->length += len;
3286         }
3287       else if ((MARK_NEXT_LIST_PTR(mark) == NULL) &&
3288                (MARK_OFFSET(mark) + 1 == forward_prop->length))
3289         {
3290           /* Inserting before only the last position in the text */
3291           
3292           GList* new_prop;
3293           forward_prop->length -= 1;
3294           
3295           new_prop = g_list_alloc();
3296           new_prop->data = new_text_property (text, font, fore, back, len+1);
3297           new_prop->prev = MARK_LIST_PTR(mark);
3298           new_prop->next = NULL;
3299           MARK_NEXT_LIST_PTR(mark) = new_prop;
3300           
3301           SET_PROPERTY_MARK (mark, new_prop, 0);
3302         }
3303       else
3304         {
3305           GList* new_prop = g_list_alloc();
3306           GList* new_prop_forward = g_list_alloc();
3307           gint old_length = forward_prop->length;
3308           GList* next = MARK_NEXT_LIST_PTR(mark);
3309           
3310           /* Set the new lengths according to where they are split.  Construct
3311            * two new properties. */
3312           forward_prop->length = MARK_OFFSET(mark);
3313
3314           new_prop_forward->data = 
3315             new_text_property(text,
3316                               forward_prop->flags & PROPERTY_FONT ? 
3317                                      forward_prop->font->gdk_font : NULL,
3318                               forward_prop->flags & PROPERTY_FOREGROUND ? 
3319                                      &forward_prop->fore_color : NULL,
3320                               forward_prop->flags & PROPERTY_BACKGROUND ? 
3321                                      &forward_prop->back_color : NULL,
3322                               old_length - forward_prop->length);
3323
3324           new_prop->data = new_text_property(text, font, fore, back, len);
3325
3326           /* Now splice things in. */
3327           MARK_NEXT_LIST_PTR(mark) = new_prop;
3328           new_prop->prev = MARK_LIST_PTR(mark);
3329           
3330           new_prop->next = new_prop_forward;
3331           new_prop_forward->prev = new_prop;
3332           
3333           new_prop_forward->next = next;
3334           
3335           if (next)
3336             next->prev = new_prop_forward;
3337           
3338           SET_PROPERTY_MARK (mark, new_prop, 0);
3339         }
3340     }
3341   
3342   while (text->text_properties_end->next)
3343     text->text_properties_end = text->text_properties_end->next;
3344   
3345   while (text->text_properties->prev)
3346     text->text_properties = text->text_properties->prev;
3347 }
3348
3349 static void
3350 delete_text_property (GtkText* text, guint nchars)
3351 {
3352   /* Delete nchars forward from point. */
3353   
3354   /* Deleting text properties is problematical, because we
3355    * might be storing around marks pointing to a property.
3356    *
3357    * The marks in question and how we handle them are:
3358    *
3359    *  point: We know the new value, since it will be at the
3360    *         end of the deleted text, and we move it there
3361    *         first.
3362    *  cursor: We just remove the mark and set it equal to the
3363    *         point after the operation.
3364    *  line-start cache: We replace most affected lines.
3365    *         The current line gets used to fetch the new
3366    *         lines so, if necessary, (delete at the beginning
3367    *         of a line) we fix it up by setting it equal to the
3368    *         point.
3369    */
3370   
3371   TextProperty *prop;
3372   GList        *tmp;
3373   gint          is_first;
3374   
3375   for(; nchars; nchars -= 1)
3376     {
3377       prop = MARK_CURRENT_PROPERTY(&text->point);
3378       
3379       prop->length -= 1;
3380       
3381       if (prop->length == 0)
3382         {
3383           tmp = MARK_LIST_PTR (&text->point);
3384           
3385           is_first = tmp == text->text_properties;
3386           
3387           MARK_LIST_PTR (&text->point) = g_list_remove_link (tmp, tmp);
3388           text->point.offset = 0;
3389
3390           if (GTK_WIDGET_REALIZED (text))
3391             unrealize_property (text, prop);
3392
3393           destroy_text_property (prop);
3394           g_list_free_1 (tmp);
3395           
3396           prop = MARK_CURRENT_PROPERTY (&text->point);
3397           
3398           if (is_first)
3399             text->text_properties = MARK_LIST_PTR (&text->point);
3400           
3401           g_assert (prop->length != 0);
3402         }
3403       else if (prop->length == text->point.offset)
3404         {
3405           MARK_LIST_PTR (&text->point) = MARK_NEXT_LIST_PTR (&text->point);
3406           text->point.offset = 0;
3407         }
3408     }
3409   
3410   /* Check to see if we have just the single final position remaining
3411    * along in a property; if so, combine it with the previous property
3412    */
3413   if (LAST_INDEX (text, text->point) && 
3414       (MARK_OFFSET (&text->point) == 0) &&
3415       (MARK_PREV_LIST_PTR(&text->point) != NULL))
3416     {
3417       tmp = MARK_LIST_PTR (&text->point);
3418       prop = MARK_CURRENT_PROPERTY(&text->point);
3419       
3420       MARK_LIST_PTR (&text->point) = MARK_PREV_LIST_PTR (&text->point);
3421       MARK_CURRENT_PROPERTY(&text->point)->length += 1;
3422       MARK_NEXT_LIST_PTR(&text->point) = NULL;
3423       
3424       text->point.offset = MARK_CURRENT_PROPERTY(&text->point)->length - 1;
3425       
3426       if (GTK_WIDGET_REALIZED (text))
3427         unrealize_property (text, prop);
3428
3429       destroy_text_property (prop);
3430       g_list_free_1 (tmp);
3431     }
3432 }
3433
3434 static void
3435 init_properties (GtkText *text)
3436 {
3437   if (!text->text_properties)
3438     {
3439       text->text_properties = g_list_alloc();
3440       text->text_properties->next = NULL;
3441       text->text_properties->prev = NULL;
3442       text->text_properties->data = new_text_property (text, NULL, NULL, NULL, 1);
3443       text->text_properties_end = text->text_properties;
3444       
3445       SET_PROPERTY_MARK (&text->point, text->text_properties, 0);
3446       
3447       text->point.index = 0;
3448     }
3449 }
3450
3451
3452 /**********************************************************************/
3453 /*                         Property Movement                          */
3454 /**********************************************************************/
3455
3456 static void
3457 move_mark_n (GtkPropertyMark* mark, gint n)
3458 {
3459   if (n > 0)
3460     advance_mark_n(mark, n);
3461   else if (n < 0)
3462     decrement_mark_n(mark, -n);
3463 }
3464
3465 static void
3466 advance_mark (GtkPropertyMark* mark)
3467 {
3468   TextProperty* prop = MARK_CURRENT_PROPERTY (mark);
3469   
3470   mark->index += 1;
3471   
3472   if (prop->length > mark->offset + 1)
3473     mark->offset += 1;
3474   else
3475     {
3476       mark->property = MARK_NEXT_LIST_PTR (mark);
3477       mark->offset   = 0;
3478     }
3479 }
3480
3481 static void
3482 advance_mark_n (GtkPropertyMark* mark, gint n)
3483 {
3484   gint i;
3485   TextProperty* prop;
3486
3487   g_assert (n > 0);
3488
3489   i = 0;                        /* otherwise it migth not be init. */
3490   prop = MARK_CURRENT_PROPERTY(mark);
3491
3492   if ((prop->length - mark->offset - 1) < n) { /* if we need to change prop. */
3493     /* to make it easier */
3494     n += (mark->offset);
3495     mark->index -= mark->offset;
3496     mark->offset = 0;
3497     /* first we take seven-mile-leaps to get to the right text
3498      * property. */
3499     while ((n-i) > prop->length - 1) {
3500       i += prop->length;
3501       mark->index += prop->length;
3502       mark->property = MARK_NEXT_LIST_PTR (mark);
3503       prop = MARK_CURRENT_PROPERTY (mark);
3504     }
3505   }
3506
3507   /* and then the rest */
3508   mark->index += n - i;
3509   mark->offset += n - i;
3510 }
3511
3512 static void
3513 decrement_mark (GtkPropertyMark* mark)
3514 {
3515   mark->index -= 1;
3516   
3517   if (mark->offset > 0)
3518     mark->offset -= 1;
3519   else
3520     {
3521       mark->property = MARK_PREV_LIST_PTR (mark);
3522       mark->offset   = MARK_CURRENT_PROPERTY (mark)->length - 1;
3523     }
3524 }
3525
3526 static void
3527 decrement_mark_n (GtkPropertyMark* mark, gint n)
3528 {
3529   g_assert (n > 0);
3530
3531   while (mark->offset < n) {
3532     /* jump to end of prev */
3533     n -= mark->offset + 1;
3534     mark->index -= mark->offset + 1;
3535     mark->property = MARK_PREV_LIST_PTR (mark);
3536     mark->offset = MARK_CURRENT_PROPERTY (mark)->length - 1;
3537   }
3538
3539   /* and the rest */
3540   mark->index -= n;
3541   mark->offset -= n;
3542 }
3543  
3544 static GtkPropertyMark
3545 find_mark (GtkText* text, guint mark_position)
3546 {
3547   return find_mark_near (text, mark_position, &text->point);
3548 }
3549
3550 /*
3551  * You can also start from the end, what a drag.
3552  */
3553 static GtkPropertyMark
3554 find_mark_near (GtkText* text, guint mark_position, const GtkPropertyMark* near)
3555 {
3556   gint diffa;
3557   gint diffb;
3558   
3559   GtkPropertyMark mark;
3560   
3561   if (!near)
3562     diffa = mark_position + 1;
3563   else
3564     diffa = mark_position - near->index;
3565   
3566   diffb = mark_position;
3567   
3568   if (diffa < 0)
3569     diffa = -diffa;
3570   
3571   if (diffa <= diffb)
3572     {
3573       mark = *near;
3574     }
3575   else
3576     {
3577       mark.index = 0;
3578       mark.property = text->text_properties;
3579       mark.offset = 0;
3580     }
3581
3582   move_mark_n (&mark, mark_position - mark.index);
3583    
3584   return mark;
3585 }
3586
3587 /* This routine must be called with scroll == FALSE, only when
3588  * point is at least partially on screen
3589  */
3590
3591 static void
3592 find_line_containing_point (GtkText* text, guint point,
3593                             gboolean scroll)
3594 {
3595   GList* cache;
3596   gint height;
3597   
3598   text->current_line = NULL;
3599
3600   TEXT_SHOW (text);
3601
3602   /* Scroll backwards until the point is on screen
3603    */
3604   while (CACHE_DATA(text->line_start_cache).start.index > point)
3605     scroll_int (text, - LINE_HEIGHT(CACHE_DATA(text->line_start_cache)));
3606
3607   /* Now additionally try to make sure that the point is fully on screen
3608    */
3609   if (scroll)
3610     {
3611       while (text->first_cut_pixels != 0 && 
3612              text->line_start_cache->next &&
3613              CACHE_DATA(text->line_start_cache->next).start.index > point)
3614         scroll_int (text, - LINE_HEIGHT(CACHE_DATA(text->line_start_cache->next)));
3615     }
3616
3617   gdk_window_get_size (text->text_area, NULL, &height);
3618   
3619   for (cache = text->line_start_cache; cache; cache = cache->next)
3620     {
3621       guint lph;
3622       
3623       if (CACHE_DATA(cache).end.index >= point ||
3624           LAST_INDEX(text, CACHE_DATA(cache).end))
3625         {
3626           text->current_line = cache; /* LOOK HERE, this proc has an
3627                                        * important side effect. */
3628           return;
3629         }
3630       
3631       TEXT_SHOW_LINE (text, cache, "cache");
3632       
3633       if (cache->next == NULL)
3634         fetch_lines_forward (text, 1);
3635       
3636       if (scroll)
3637         {
3638           lph = pixel_height_of (text, cache->next);
3639           
3640           /* Scroll the bottom of the line is on screen, or until
3641            * the line is the first onscreen line.
3642            */
3643           while (cache->next != text->line_start_cache && lph > height)
3644             {
3645               TEXT_SHOW_LINE (text, cache, "cache");
3646               TEXT_SHOW_LINE (text, cache->next, "cache->next");
3647               scroll_int (text, LINE_HEIGHT(CACHE_DATA(cache->next)));
3648               lph = pixel_height_of (text, cache->next);
3649             }
3650         }
3651     }
3652   
3653   g_assert_not_reached (); /* Must set text->current_line here */
3654 }
3655
3656 static guint
3657 pixel_height_of (GtkText* text, GList* cache_line)
3658 {
3659   gint pixels = - text->first_cut_pixels;
3660   GList *cache = text->line_start_cache;
3661   
3662   while (TRUE) {
3663     pixels += LINE_HEIGHT (CACHE_DATA(cache));
3664     
3665     if (cache->data == cache_line->data)
3666       break;
3667     
3668     cache = cache->next;
3669   }
3670   
3671   return pixels;
3672 }
3673
3674 /**********************************************************************/
3675 /*                      Search and Placement                          */
3676 /**********************************************************************/
3677
3678 static gint
3679 find_char_width (GtkText* text, const GtkPropertyMark *mark, const TabStopMark *tab_mark)
3680 {
3681   GdkWChar ch;
3682   gint16* char_widths;
3683   
3684   if (LAST_INDEX (text, *mark))
3685     return 0;
3686   
3687   ch = GTK_TEXT_INDEX (text, mark->index);
3688   char_widths = MARK_CURRENT_TEXT_FONT (text, mark)->char_widths;
3689
3690   if (ch == '\t')
3691     {
3692       return tab_mark->to_next_tab * char_widths[' '];
3693     }
3694   else if (ch < 256)
3695     {
3696       return char_widths[ch];
3697     }
3698   else
3699     {
3700       return gdk_char_width_wc(MARK_CURRENT_TEXT_FONT(text, mark)->gdk_font, ch);
3701     }
3702 }
3703
3704 static void
3705 advance_tab_mark (GtkText* text, TabStopMark* tab_mark, GdkWChar ch)
3706 {
3707   if (tab_mark->to_next_tab == 1 || ch == '\t')
3708     {
3709       if (tab_mark->tab_stops->next)
3710         {
3711           tab_mark->tab_stops = tab_mark->tab_stops->next;
3712           tab_mark->to_next_tab = (gulong) tab_mark->tab_stops->data;
3713         }
3714       else
3715         {
3716           tab_mark->to_next_tab = text->default_tab_width;
3717         }
3718     }
3719   else
3720     {
3721       tab_mark->to_next_tab -= 1;
3722     }
3723 }
3724
3725 static void
3726 advance_tab_mark_n (GtkText* text, TabStopMark* tab_mark, gint n)
3727      /* No tabs! */
3728 {
3729   while (n--)
3730     advance_tab_mark (text, tab_mark, 0);
3731 }
3732
3733 static void
3734 find_cursor_at_line (GtkText* text, const LineParams* start_line, gint pixel_height)
3735 {
3736   GdkWChar ch;
3737   GtkEditable *editable = (GtkEditable *)text;
3738   
3739   GtkPropertyMark mark        = start_line->start;
3740   TabStopMark  tab_mark    = start_line->tab_cont.tab_start;
3741   gint         pixel_width = LINE_START_PIXEL (*start_line);
3742   
3743   while (mark.index < text->cursor_mark.index)
3744     {
3745       pixel_width += find_char_width (text, &mark, &tab_mark);
3746       
3747       advance_tab_mark (text, &tab_mark, GTK_TEXT_INDEX(text, mark.index));
3748       advance_mark (&mark);
3749     }
3750   
3751   text->cursor_pos_x       = pixel_width;
3752   text->cursor_pos_y       = pixel_height;
3753   text->cursor_char_offset = start_line->font_descent;
3754   text->cursor_mark        = mark;
3755   
3756   ch = LAST_INDEX (text, mark) ? 
3757     LINE_DELIM : GTK_TEXT_INDEX (text, mark.index);
3758   
3759   if ((text->use_wchar) ? gdk_iswspace (ch) : isspace (ch))
3760     text->cursor_char = 0;
3761   else
3762     text->cursor_char = ch;
3763     
3764 #ifdef USE_XIM
3765   if (GTK_WIDGET_HAS_FOCUS(text) && gdk_im_ready() && editable->ic && 
3766       (gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION))
3767     {
3768       GdkICAttributesType mask = GDK_IC_SPOT_LOCATION |
3769                                  GDK_IC_PREEDIT_FOREGROUND |
3770                                  GDK_IC_PREEDIT_BACKGROUND;
3771
3772       editable->ic_attr->spot_location.x = text->cursor_pos_x;
3773       editable->ic_attr->spot_location.y
3774         = text->cursor_pos_y - text->cursor_char_offset;
3775       editable->ic_attr->preedit_foreground = *MARK_CURRENT_FORE (text, &mark);
3776       editable->ic_attr->preedit_background = *MARK_CURRENT_BACK (text, &mark);
3777
3778       if (MARK_CURRENT_FONT (text, &mark)->type == GDK_FONT_FONTSET)
3779         {
3780           mask |= GDK_IC_PREEDIT_FONTSET;
3781           editable->ic_attr->preedit_fontset = MARK_CURRENT_FONT (text, &mark);
3782         }
3783       
3784       gdk_ic_set_attr (editable->ic, editable->ic_attr, mask);
3785     }
3786 #endif 
3787 }
3788
3789 static void
3790 find_cursor (GtkText* text, gboolean scroll)
3791 {
3792   if (GTK_WIDGET_REALIZED (text))
3793     {
3794       find_line_containing_point (text, text->cursor_mark.index, scroll);
3795       
3796       if (text->current_line)
3797         find_cursor_at_line (text,
3798                              &CACHE_DATA(text->current_line),
3799                              pixel_height_of(text, text->current_line));
3800     }
3801   
3802   GTK_EDITABLE (text)->current_pos = text->cursor_mark.index;
3803 }
3804
3805 static void
3806 find_mouse_cursor_at_line (GtkText *text, const LineParams* lp,
3807                            guint line_pixel_height,
3808                            gint button_x)
3809 {
3810   GtkPropertyMark mark     = lp->start;
3811   TabStopMark  tab_mark = lp->tab_cont.tab_start;
3812   
3813   gint char_width = find_char_width(text, &mark, &tab_mark);
3814   gint pixel_width = LINE_START_PIXEL (*lp) + (char_width+1)/2;
3815   
3816   text->cursor_pos_y = line_pixel_height;
3817   
3818   for (;;)
3819     {
3820       GdkWChar ch = LAST_INDEX (text, mark) ? 
3821         LINE_DELIM : GTK_TEXT_INDEX (text, mark.index);
3822       
3823       if (button_x < pixel_width || mark.index == lp->end.index)
3824         {
3825           text->cursor_pos_x       = pixel_width - (char_width+1)/2;
3826           text->cursor_mark        = mark;
3827           text->cursor_char_offset = lp->font_descent;
3828           
3829           if ((text->use_wchar) ? gdk_iswspace (ch) : isspace (ch))
3830             text->cursor_char = 0;
3831           else
3832             text->cursor_char = ch;
3833           
3834           break;
3835         }
3836       
3837       advance_tab_mark (text, &tab_mark, ch);
3838       advance_mark (&mark);
3839       
3840       pixel_width += char_width/2;
3841       
3842       char_width = find_char_width (text, &mark, &tab_mark);
3843       
3844       pixel_width += (char_width+1)/2;
3845     }
3846 }
3847
3848 static void
3849 find_mouse_cursor (GtkText* text, gint x, gint y)
3850 {
3851   gint pixel_height;
3852   GList* cache = text->line_start_cache;
3853   
3854   g_assert (cache);
3855   
3856   pixel_height = - text->first_cut_pixels;
3857   
3858   for (; cache; cache = cache->next)
3859     {
3860       pixel_height += LINE_HEIGHT(CACHE_DATA(cache));
3861       
3862       if (y < pixel_height || !cache->next)
3863         {
3864           find_mouse_cursor_at_line (text, &CACHE_DATA(cache), pixel_height, x);
3865           
3866           find_cursor (text, FALSE);
3867           
3868           return;
3869         }
3870     }
3871 }
3872
3873 /**********************************************************************/
3874 /*                          Cache Manager                             */
3875 /**********************************************************************/
3876
3877 static void
3878 free_cache (GtkText* text)
3879 {
3880   GList* cache = text->line_start_cache;
3881   
3882   if (cache)
3883     {
3884       while (cache->prev)
3885         cache = cache->prev;
3886       
3887       text->line_start_cache = cache;
3888     }
3889   
3890   for (; cache; cache = cache->next)
3891     g_mem_chunk_free (params_mem_chunk, cache->data);
3892   
3893   g_list_free (text->line_start_cache);
3894   
3895   text->line_start_cache = NULL;
3896 }
3897
3898 static GList*
3899 remove_cache_line (GtkText* text, GList* member)
3900 {
3901   GList *list;
3902   
3903   if (member == NULL)
3904     return NULL;
3905   
3906   if (member == text->line_start_cache)
3907     text->line_start_cache = text->line_start_cache->next;
3908   
3909   if (member->prev)
3910     member->prev->next = member->next;
3911   
3912   if (member->next)
3913     member->next->prev = member->prev;
3914   
3915   list = member->next;
3916   
3917   g_mem_chunk_free (params_mem_chunk, member->data);
3918   g_list_free_1 (member);
3919   
3920   return list;
3921 }
3922
3923 /**********************************************************************/
3924 /*                           Key Motion                               */
3925 /**********************************************************************/
3926
3927 static void
3928 move_cursor_buffer_ver (GtkText *text, int dir)
3929 {
3930   undraw_cursor (text, FALSE);
3931   
3932   if (dir > 0)
3933     {
3934       scroll_int (text, text->vadj->upper);
3935       text->cursor_mark = find_this_line_start_mark (text,
3936                                                      TEXT_LENGTH (text),
3937                                                      &text->cursor_mark);
3938     }
3939   else
3940     {
3941       scroll_int (text, - text->vadj->value);
3942       text->cursor_mark = find_this_line_start_mark (text,
3943                                                      0,
3944                                                      &text->cursor_mark);
3945     }
3946   
3947   find_cursor (text, TRUE);
3948   draw_cursor (text, FALSE);
3949 }
3950
3951 static void
3952 move_cursor_page_ver (GtkText *text, int dir)
3953 {
3954   scroll_int (text, dir * text->vadj->page_increment);
3955 }
3956
3957 static void
3958 move_cursor_ver (GtkText *text, int count)
3959 {
3960   gint i;
3961   GtkPropertyMark mark;
3962   gint offset;
3963   
3964   mark = find_this_line_start_mark (text, text->cursor_mark.index, &text->cursor_mark);
3965   offset = text->cursor_mark.index - mark.index;
3966   
3967   if (offset > text->cursor_virtual_x)
3968     text->cursor_virtual_x = offset;
3969   
3970   if (count < 0)
3971     {
3972       if (mark.index == 0)
3973         return;
3974       
3975       decrement_mark (&mark);
3976       mark = find_this_line_start_mark (text, mark.index, &mark);
3977     }
3978   else
3979     {
3980       mark = text->cursor_mark;
3981       
3982       while (!LAST_INDEX(text, mark) && GTK_TEXT_INDEX(text, mark.index) != LINE_DELIM)
3983         advance_mark (&mark);
3984       
3985       if (LAST_INDEX(text, mark))
3986         return;
3987       
3988       advance_mark (&mark);
3989     }
3990   
3991   for (i=0; i < text->cursor_virtual_x; i += 1, advance_mark(&mark))
3992     if (LAST_INDEX(text, mark) ||
3993         GTK_TEXT_INDEX(text, mark.index) == LINE_DELIM)
3994       break;
3995   
3996   undraw_cursor (text, FALSE);
3997   
3998   text->cursor_mark = mark;
3999   
4000   find_cursor (text, TRUE);
4001   
4002   draw_cursor (text, FALSE);
4003 }
4004
4005 static void
4006 move_cursor_hor (GtkText *text, int count)
4007 {
4008   /* count should be +-1. */
4009   if ( (count > 0 && text->cursor_mark.index + count > TEXT_LENGTH(text)) ||
4010        (count < 0 && text->cursor_mark.index < (- count)) ||
4011        (count == 0) )
4012     return;
4013   
4014   text->cursor_virtual_x = 0;
4015   
4016   undraw_cursor (text, FALSE);
4017   
4018   move_mark_n (&text->cursor_mark, count);
4019   
4020   find_cursor (text, TRUE);
4021   
4022   draw_cursor (text, FALSE);
4023 }
4024
4025 static void 
4026 gtk_text_move_cursor (GtkEditable *editable,
4027                       gint         x,
4028                       gint         y)
4029 {
4030   if (x > 0)
4031     {
4032       while (x-- != 0)
4033         move_cursor_hor (GTK_TEXT (editable), 1);
4034     }
4035   else if (x < 0)
4036     {
4037       while (x++ != 0)
4038         move_cursor_hor (GTK_TEXT (editable), -1);
4039     }
4040   
4041   if (y > 0)
4042     {
4043       while (y-- != 0)
4044         move_cursor_ver (GTK_TEXT (editable), 1);
4045     }
4046   else if (y < 0)
4047     {
4048       while (y++ != 0)
4049         move_cursor_ver (GTK_TEXT (editable), -1);
4050     }
4051 }
4052
4053 static void
4054 gtk_text_move_forward_character (GtkText *text)
4055 {
4056   move_cursor_hor (text, 1);
4057 }
4058
4059 static void
4060 gtk_text_move_backward_character (GtkText *text)
4061 {
4062   move_cursor_hor (text, -1);
4063 }
4064
4065 static void
4066 gtk_text_move_next_line (GtkText *text)
4067 {
4068   move_cursor_ver (text, 1);
4069 }
4070
4071 static void
4072 gtk_text_move_previous_line (GtkText *text)
4073 {
4074   move_cursor_ver (text, -1);
4075 }
4076
4077 static void 
4078 gtk_text_move_word (GtkEditable *editable,
4079                     gint         n)
4080 {
4081   if (n > 0)
4082     {
4083       while (n-- != 0)
4084         gtk_text_move_forward_word (GTK_TEXT (editable));
4085     }
4086   else if (n < 0)
4087     {
4088       while (n++ != 0)
4089         gtk_text_move_backward_word (GTK_TEXT (editable));
4090     }
4091 }
4092
4093 static void
4094 gtk_text_move_forward_word (GtkText *text)
4095 {
4096   text->cursor_virtual_x = 0;
4097   
4098   undraw_cursor (text, FALSE);
4099   
4100   if (text->use_wchar)
4101     {
4102       while (!LAST_INDEX (text, text->cursor_mark) && 
4103              !gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
4104         advance_mark (&text->cursor_mark);
4105       
4106       while (!LAST_INDEX (text, text->cursor_mark) && 
4107              gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
4108         advance_mark (&text->cursor_mark);
4109     }
4110   else
4111     {
4112       while (!LAST_INDEX (text, text->cursor_mark) && 
4113              !isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
4114         advance_mark (&text->cursor_mark);
4115       
4116       while (!LAST_INDEX (text, text->cursor_mark) && 
4117              isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
4118         advance_mark (&text->cursor_mark);
4119     }
4120   
4121   find_cursor (text, TRUE);
4122   draw_cursor (text, FALSE);
4123 }
4124
4125 static void
4126 gtk_text_move_backward_word (GtkText *text)
4127 {
4128   text->cursor_virtual_x = 0;
4129   
4130   undraw_cursor (text, FALSE);
4131   
4132   if (text->use_wchar)
4133     {
4134       while ((text->cursor_mark.index > 0) &&
4135              !gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
4136         decrement_mark (&text->cursor_mark);
4137       
4138       while ((text->cursor_mark.index > 0) &&
4139              gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
4140         decrement_mark (&text->cursor_mark);
4141     }
4142   else
4143     {
4144       while ((text->cursor_mark.index > 0) &&
4145              !isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
4146         decrement_mark (&text->cursor_mark);
4147       
4148       while ((text->cursor_mark.index > 0) &&
4149              isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
4150         decrement_mark (&text->cursor_mark);
4151     }
4152   
4153   find_cursor (text, TRUE);
4154   draw_cursor (text, FALSE);
4155 }
4156
4157 static void 
4158 gtk_text_move_page (GtkEditable *editable,
4159                     gint         x,
4160                     gint         y)
4161 {
4162   if (y != 0)
4163     scroll_int (GTK_TEXT (editable), 
4164                 y * GTK_TEXT(editable)->vadj->page_increment);  
4165 }
4166
4167 static void 
4168 gtk_text_move_to_row (GtkEditable *editable,
4169                       gint         row)
4170 {
4171 }
4172
4173 static void 
4174 gtk_text_move_to_column (GtkEditable *editable,
4175                          gint         column)
4176 {
4177   GtkText *text;
4178   
4179   text = GTK_TEXT (editable);
4180   
4181   text->cursor_virtual_x = 0;   /* FIXME */
4182   
4183   undraw_cursor (text, FALSE);
4184   
4185   /* Move to the beginning of the line */
4186   while ((text->cursor_mark.index > 0) &&
4187          (GTK_TEXT_INDEX (text, text->cursor_mark.index - 1) != LINE_DELIM))
4188     decrement_mark (&text->cursor_mark);
4189   
4190   while (!LAST_INDEX (text, text->cursor_mark) &&
4191          (GTK_TEXT_INDEX (text, text->cursor_mark.index) != LINE_DELIM))
4192     {
4193       if (column > 0)
4194         column--;
4195       else if (column == 0)
4196         break;
4197       
4198       advance_mark (&text->cursor_mark);
4199     }
4200   
4201   find_cursor (text, TRUE);
4202   draw_cursor (text, FALSE);
4203 }
4204
4205 static void
4206 gtk_text_move_beginning_of_line (GtkText *text)
4207 {
4208   gtk_text_move_to_column (GTK_EDITABLE (text), 0);
4209   
4210 }
4211
4212 static void
4213 gtk_text_move_end_of_line (GtkText *text)
4214 {
4215   gtk_text_move_to_column (GTK_EDITABLE (text), -1);
4216 }
4217
4218 static void 
4219 gtk_text_kill_char (GtkEditable *editable,
4220                     gint         direction)
4221 {
4222   GtkText *text;
4223   
4224   text = GTK_TEXT (editable);
4225   
4226   if (editable->selection_start_pos != editable->selection_end_pos)
4227     gtk_editable_delete_selection (editable);
4228   else
4229     {
4230       if (direction >= 0)
4231         {
4232           if (text->point.index + 1 <= TEXT_LENGTH (text))
4233             gtk_editable_delete_text (editable, text->point.index, text->point.index + 1);
4234         }
4235       else
4236         {
4237           if (text->point.index > 0)
4238             gtk_editable_delete_text (editable, text->point.index - 1, text->point.index);
4239         }
4240     }
4241 }
4242
4243 static void
4244 gtk_text_delete_forward_character (GtkText *text)
4245 {
4246   gtk_text_kill_char (GTK_EDITABLE (text), 1);
4247 }
4248
4249 static void
4250 gtk_text_delete_backward_character (GtkText *text)
4251 {
4252   gtk_text_kill_char (GTK_EDITABLE (text), -1);
4253 }
4254
4255 static void 
4256 gtk_text_kill_word (GtkEditable *editable,
4257                     gint         direction)
4258 {
4259   if (editable->selection_start_pos != editable->selection_end_pos)
4260     gtk_editable_delete_selection (editable);
4261   else
4262     {
4263       gint old_pos = editable->current_pos;
4264       if (direction >= 0)
4265         {
4266           gtk_text_move_word (editable, 1);
4267           gtk_editable_delete_text (editable, old_pos, editable->current_pos);
4268         }
4269       else
4270         {
4271           gtk_text_move_word (editable, -1);
4272           gtk_editable_delete_text (editable, editable->current_pos, old_pos);
4273         }
4274     }
4275 }
4276
4277 static void
4278 gtk_text_delete_forward_word (GtkText *text)
4279 {
4280   gtk_text_kill_word (GTK_EDITABLE (text), 1);
4281 }
4282
4283 static void
4284 gtk_text_delete_backward_word (GtkText *text)
4285 {
4286   gtk_text_kill_word (GTK_EDITABLE (text), -1);
4287 }
4288
4289 static void 
4290 gtk_text_kill_line (GtkEditable *editable,
4291                     gint         direction)
4292 {
4293   gint old_pos = editable->current_pos;
4294   if (direction >= 0)
4295     {
4296       gtk_text_move_to_column (editable, -1);
4297       gtk_editable_delete_text (editable, old_pos, editable->current_pos);
4298     }
4299   else
4300     {
4301       gtk_text_move_to_column (editable, 0);
4302       gtk_editable_delete_text (editable, editable->current_pos, old_pos);
4303     }
4304 }
4305
4306 static void
4307 gtk_text_delete_line (GtkText *text)
4308 {
4309   gtk_text_move_to_column (GTK_EDITABLE (text), 0);
4310   gtk_text_kill_line (GTK_EDITABLE (text), 1);
4311 }
4312
4313 static void
4314 gtk_text_delete_to_line_end (GtkText *text)
4315 {
4316   gtk_text_kill_line (GTK_EDITABLE (text), 1);
4317 }
4318
4319 static void
4320 gtk_text_select_word (GtkText *text, guint32 time)
4321 {
4322   gint start_pos;
4323   gint end_pos;
4324   
4325   GtkEditable *editable;
4326   editable = GTK_EDITABLE (text);
4327   
4328   gtk_text_move_backward_word (text);
4329   start_pos = text->cursor_mark.index;
4330   
4331   gtk_text_move_forward_word (text);
4332   end_pos = text->cursor_mark.index;
4333   
4334   editable->has_selection = TRUE;
4335   gtk_text_set_selection (editable, start_pos, end_pos);
4336   gtk_editable_claim_selection (editable, start_pos != end_pos, time);
4337 }
4338
4339 static void
4340 gtk_text_select_line (GtkText *text, guint32 time)
4341 {
4342   gint start_pos;
4343   gint end_pos;
4344   
4345   GtkEditable *editable;
4346   editable = GTK_EDITABLE (text);
4347   
4348   gtk_text_move_beginning_of_line (text);
4349   start_pos = text->cursor_mark.index;
4350   
4351   gtk_text_move_end_of_line (text);
4352   gtk_text_move_forward_character (text);
4353   end_pos = text->cursor_mark.index;
4354   
4355   editable->has_selection = TRUE;
4356   gtk_text_set_selection (editable, start_pos, end_pos);
4357   gtk_editable_claim_selection (editable, start_pos != end_pos, time);
4358 }
4359
4360 /**********************************************************************/
4361 /*                            Scrolling                               */
4362 /**********************************************************************/
4363
4364 static void
4365 adjust_adj (GtkText* text, GtkAdjustment* adj)
4366 {
4367   gint height;
4368   
4369   gdk_window_get_size (text->text_area, NULL, &height);
4370   
4371   adj->step_increment = MIN (adj->upper, (float) SCROLL_PIXELS);
4372   adj->page_increment = MIN (adj->upper, height - (float) KEY_SCROLL_PIXELS);
4373   adj->page_size      = MIN (adj->upper, height);
4374   adj->value          = MIN (adj->value, adj->upper - adj->page_size);
4375   adj->value          = MAX (adj->value, 0.0);
4376   
4377   gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed");
4378 }
4379
4380 static gint
4381 set_vertical_scroll_iterator (GtkText* text, LineParams* lp, void* data)
4382 {
4383   SetVerticalScrollData *svdata = (SetVerticalScrollData *) data;
4384   
4385   if ((text->first_line_start_index >= lp->start.index) &&
4386       (text->first_line_start_index <= lp->end.index))
4387     {
4388       svdata->mark = lp->start;
4389   
4390       if (text->first_line_start_index == lp->start.index)
4391         {
4392           text->first_onscreen_ver_pixel = svdata->pixel_height + text->first_cut_pixels;
4393         }
4394       else
4395         {
4396           text->first_onscreen_ver_pixel = svdata->pixel_height;
4397           text->first_cut_pixels = 0;
4398         }
4399       
4400       text->vadj->value = (float) text->first_onscreen_ver_pixel;
4401     }
4402   
4403   svdata->pixel_height += LINE_HEIGHT (*lp);
4404   
4405   return FALSE;
4406 }
4407
4408 static gint
4409 set_vertical_scroll_find_iterator (GtkText* text, LineParams* lp, void* data)
4410 {
4411   SetVerticalScrollData *svdata = (SetVerticalScrollData *) data;
4412   gint return_val;
4413   
4414   if (svdata->pixel_height <= (gint) text->vadj->value &&
4415       svdata->pixel_height + LINE_HEIGHT(*lp) > (gint) text->vadj->value)
4416     {
4417       svdata->mark = lp->start;
4418       
4419       text->first_cut_pixels = (gint)text->vadj->value - svdata->pixel_height;
4420       text->first_onscreen_ver_pixel = svdata->pixel_height;
4421       text->first_line_start_index = lp->start.index;
4422       
4423       return_val = TRUE;
4424     }
4425   else
4426     {
4427       svdata->pixel_height += LINE_HEIGHT (*lp);
4428       
4429       return_val = FALSE;
4430     }
4431   
4432   return return_val;
4433 }
4434
4435 static GtkPropertyMark
4436 set_vertical_scroll (GtkText* text)
4437 {
4438   GtkPropertyMark mark = find_mark (text, 0);
4439   SetVerticalScrollData data;
4440   gint height;
4441   gint orig_value;
4442   
4443   data.pixel_height = 0;
4444   line_params_iterate (text, &mark, NULL, FALSE, &data, set_vertical_scroll_iterator);
4445   
4446   text->vadj->upper = (float) data.pixel_height;
4447   orig_value = (gint) text->vadj->value;
4448   
4449   gdk_window_get_size (text->text_area, NULL, &height);
4450   
4451   text->vadj->step_increment = MIN (text->vadj->upper, (float) SCROLL_PIXELS);
4452   text->vadj->page_increment = MIN (text->vadj->upper, height - (float) KEY_SCROLL_PIXELS);
4453   text->vadj->page_size      = MIN (text->vadj->upper, height);
4454   text->vadj->value          = MIN (text->vadj->value, text->vadj->upper - text->vadj->page_size);
4455   text->vadj->value          = MAX (text->vadj->value, 0.0);
4456   
4457   text->last_ver_value = (gint)text->vadj->value;
4458   
4459   gtk_signal_emit_by_name (GTK_OBJECT (text->vadj), "changed");
4460   
4461   if (text->vadj->value != orig_value)
4462     {
4463       /* We got clipped, and don't really know which line to put first. */
4464       data.pixel_height = 0;
4465       data.last_didnt_wrap = TRUE;
4466       
4467       line_params_iterate (text, &mark, NULL,
4468                            FALSE, &data,
4469                            set_vertical_scroll_find_iterator);
4470     }
4471
4472   return data.mark;
4473 }
4474
4475 static void
4476 scroll_int (GtkText* text, gint diff)
4477 {
4478   gfloat upper;
4479   
4480   text->vadj->value += diff;
4481   
4482   upper = text->vadj->upper - text->vadj->page_size;
4483   text->vadj->value = MIN (text->vadj->value, upper);
4484   text->vadj->value = MAX (text->vadj->value, 0.0);
4485   
4486   gtk_signal_emit_by_name (GTK_OBJECT (text->vadj), "value_changed");
4487 }
4488
4489 static void 
4490 process_exposes (GtkText *text)
4491 {
4492   GdkEvent *event;
4493   
4494   /* Make sure graphics expose events are processed before scrolling
4495    * again */
4496   
4497   while ((event = gdk_event_get_graphics_expose (text->text_area)) != NULL)
4498     {
4499       gtk_widget_event (GTK_WIDGET (text), event);
4500       if (event->expose.count == 0)
4501         {
4502           gdk_event_free (event);
4503           break;
4504         }
4505       gdk_event_free (event);
4506     }
4507 }
4508
4509 static gint last_visible_line_height (GtkText* text)
4510 {
4511   GList *cache = text->line_start_cache;
4512   gint height;
4513   
4514   gdk_window_get_size (text->text_area, NULL, &height);
4515   
4516   for (; cache->next; cache = cache->next)
4517     if (pixel_height_of(text, cache->next) > height)
4518       break;
4519   
4520   if (cache)
4521     return pixel_height_of(text, cache) - 1;
4522   else
4523     return 0;
4524 }
4525
4526 static gint first_visible_line_height (GtkText* text)
4527 {
4528   if (text->first_cut_pixels)
4529     return pixel_height_of(text, text->line_start_cache) + 1;
4530   else
4531     return 1;
4532 }
4533
4534 static void
4535 scroll_down (GtkText* text, gint diff0)
4536 {
4537   GdkRectangle rect;
4538   gint real_diff = 0;
4539   gint width, height;
4540   
4541   text->first_onscreen_ver_pixel += diff0;
4542   
4543   while (diff0-- > 0)
4544     {
4545       g_assert (text->line_start_cache);
4546       
4547       if (text->first_cut_pixels < LINE_HEIGHT(CACHE_DATA(text->line_start_cache)) - 1)
4548         {
4549           text->first_cut_pixels += 1;
4550         }
4551       else
4552         {
4553           text->first_cut_pixels = 0;
4554           
4555           text->line_start_cache = text->line_start_cache->next;
4556           
4557           text->first_line_start_index =
4558             CACHE_DATA(text->line_start_cache).start.index;
4559           
4560           if (!text->line_start_cache->next)
4561             fetch_lines_forward (text, 1);
4562         }
4563       
4564       real_diff += 1;
4565     }
4566   
4567   gdk_window_get_size (text->text_area, &width, &height);
4568   if (height > real_diff)
4569     gdk_draw_pixmap (text->text_area,
4570                      text->gc,
4571                      text->text_area,
4572                      0,
4573                      real_diff,
4574                      0,
4575                      0,
4576                      width,
4577                      height - real_diff);
4578   
4579   rect.x      = 0;
4580   rect.y      = MAX (0, height - real_diff);
4581   rect.width  = width;
4582   rect.height = MIN (height, real_diff);
4583   
4584   expose_text (text, &rect, FALSE);
4585   gtk_text_draw_focus ( (GtkWidget *) text);
4586   
4587   if (text->current_line)
4588     {
4589       gint cursor_min;
4590       
4591       text->cursor_pos_y -= real_diff;
4592       cursor_min = drawn_cursor_min(text);
4593       
4594       if (cursor_min < 0)
4595         find_mouse_cursor (text, text->cursor_pos_x,
4596                            first_visible_line_height (text));
4597     }
4598   
4599   if (height > real_diff)
4600     process_exposes (text);
4601 }
4602
4603 static void
4604 scroll_up (GtkText* text, gint diff0)
4605 {
4606   gint real_diff = 0;
4607   GdkRectangle rect;
4608   gint width, height;
4609   
4610   text->first_onscreen_ver_pixel += diff0;
4611   
4612   while (diff0++ < 0)
4613     {
4614       g_assert (text->line_start_cache);
4615       
4616       if (text->first_cut_pixels > 0)
4617         {
4618           text->first_cut_pixels -= 1;
4619         }
4620       else
4621         {
4622           if (!text->line_start_cache->prev)
4623             fetch_lines_backward (text);
4624           
4625           text->line_start_cache = text->line_start_cache->prev;
4626           
4627           text->first_line_start_index =
4628             CACHE_DATA(text->line_start_cache).start.index;
4629           
4630           text->first_cut_pixels = LINE_HEIGHT(CACHE_DATA(text->line_start_cache)) - 1;
4631         }
4632       
4633       real_diff += 1;
4634     }
4635   
4636   gdk_window_get_size (text->text_area, &width, &height);
4637   if (height > real_diff)
4638     gdk_draw_pixmap (text->text_area,
4639                      text->gc,
4640                      text->text_area,
4641                      0,
4642                      0,
4643                      0,
4644                      real_diff,
4645                      width,
4646                      height - real_diff);
4647   
4648   rect.x      = 0;
4649   rect.y      = 0;
4650   rect.width  = width;
4651   rect.height = MIN (height, real_diff);
4652   
4653   expose_text (text, &rect, FALSE);
4654   gtk_text_draw_focus ( (GtkWidget *) text);
4655   
4656   if (text->current_line)
4657     {
4658       gint cursor_max;
4659       gint height;
4660       
4661       text->cursor_pos_y += real_diff;
4662       cursor_max = drawn_cursor_max(text);
4663       gdk_window_get_size (text->text_area, NULL, &height);
4664       
4665       if (cursor_max >= height)
4666         find_mouse_cursor (text, text->cursor_pos_x,
4667                            last_visible_line_height (text));
4668     }
4669   
4670   if (height > real_diff)
4671     process_exposes (text);
4672 }
4673
4674 /**********************************************************************/
4675 /*                            Display Code                            */
4676 /**********************************************************************/
4677
4678 /* Assumes mark starts a line.  Calculates the height, width, and
4679  * displayable character count of a single DISPLAYABLE line.  That
4680  * means that in line-wrap mode, this does may not compute the
4681  * properties of an entire line. */
4682 static LineParams
4683 find_line_params (GtkText* text,
4684                   const GtkPropertyMark* mark,
4685                   const PrevTabCont *tab_cont,
4686                   PrevTabCont *next_cont)
4687 {
4688   LineParams lp;
4689   TabStopMark tab_mark = tab_cont->tab_start;
4690   guint max_display_pixels;
4691   GdkWChar ch;
4692   gint ch_width;
4693   GdkFont *font;
4694   
4695   gdk_window_get_size (text->text_area, (gint*) &max_display_pixels, NULL);
4696   max_display_pixels -= LINE_WRAP_ROOM;
4697   
4698   lp.wraps             = 0;
4699   lp.tab_cont          = *tab_cont;
4700   lp.start             = *mark;
4701   lp.end               = *mark;
4702   lp.pixel_width       = tab_cont->pixel_offset;
4703   lp.displayable_chars = 0;
4704   lp.font_ascent       = 0;
4705   lp.font_descent      = 0;
4706   
4707   init_tab_cont (text, next_cont);
4708   
4709   while (!LAST_INDEX(text, lp.end))
4710     {
4711       g_assert (lp.end.property);
4712       
4713       ch   = GTK_TEXT_INDEX (text, lp.end.index);
4714       font = MARK_CURRENT_FONT (text, &lp.end);
4715
4716       if (ch == LINE_DELIM)
4717         {
4718           /* Newline doesn't count in computation of line height, even
4719            * if its in a bigger font than the rest of the line.  Unless,
4720            * of course, there are no other characters. */
4721           
4722           if (!lp.font_ascent && !lp.font_descent)
4723             {
4724               lp.font_ascent = font->ascent;
4725               lp.font_descent = font->descent;
4726             }
4727           
4728           lp.tab_cont_next = *next_cont;
4729           
4730           return lp;
4731         }
4732       
4733       ch_width = find_char_width (text, &lp.end, &tab_mark);
4734       
4735       if ((ch_width + lp.pixel_width > max_display_pixels) &&
4736           (lp.end.index > lp.start.index))
4737         {
4738           lp.wraps = 1;
4739           
4740           if (text->line_wrap)
4741             {
4742               next_cont->tab_start    = tab_mark;
4743               next_cont->pixel_offset = 0;
4744               
4745               if (ch == '\t')
4746                 {
4747                   /* Here's the tough case, a tab is wrapping. */
4748                   gint pixels_avail = max_display_pixels - lp.pixel_width;
4749                   gint space_width  = MARK_CURRENT_TEXT_FONT(text, &lp.end)->char_widths[' '];
4750                   gint spaces_avail = pixels_avail / space_width;
4751                   
4752                   if (spaces_avail == 0)
4753                     {
4754                       decrement_mark (&lp.end);
4755                     }
4756                   else
4757                     {
4758                       advance_tab_mark (text, &next_cont->tab_start, '\t');
4759                       next_cont->pixel_offset = space_width * (tab_mark.to_next_tab -
4760                                                                spaces_avail);
4761                       lp.displayable_chars += 1;
4762                     }
4763                 }
4764               else
4765                 {
4766                   if (text->word_wrap)
4767                     {
4768                       GtkPropertyMark saved_mark = lp.end;
4769                       guint saved_characters = lp.displayable_chars;
4770                       
4771                       lp.displayable_chars += 1;
4772                       
4773                       if (text->use_wchar)
4774                         {
4775                           while (!gdk_iswspace (GTK_TEXT_INDEX (text, lp.end.index)) &&
4776                                  (lp.end.index > lp.start.index))
4777                             {
4778                               decrement_mark (&lp.end);
4779                               lp.displayable_chars -= 1;
4780                             }
4781                         }
4782                       else
4783                         {
4784                           while (!isspace(GTK_TEXT_INDEX (text, lp.end.index)) &&
4785                                  (lp.end.index > lp.start.index))
4786                             {
4787                               decrement_mark (&lp.end);
4788                               lp.displayable_chars -= 1;
4789                             }
4790                         }
4791                       
4792                       /* If whole line is one word, revert to char wrapping */
4793                       if (lp.end.index == lp.start.index)
4794                         {
4795                           lp.end = saved_mark;
4796                           lp.displayable_chars = saved_characters;
4797                           decrement_mark (&lp.end);
4798                         }
4799                     }
4800                   else
4801                     {
4802                       /* Don't include this character, it will wrap. */
4803                       decrement_mark (&lp.end);
4804                     }
4805                 }
4806               
4807               lp.tab_cont_next = *next_cont;
4808               
4809               return lp;
4810             }
4811         }
4812       else
4813         {
4814           lp.displayable_chars += 1;
4815         }
4816       
4817       lp.font_ascent = MAX (font->ascent, lp.font_ascent);
4818       lp.font_descent = MAX (font->descent, lp.font_descent);
4819       lp.pixel_width  += ch_width;
4820       
4821       advance_mark(&lp.end);
4822       advance_tab_mark (text, &tab_mark, ch);
4823     }
4824   
4825   if (LAST_INDEX(text, lp.start))
4826     {
4827       /* Special case, empty last line. */
4828       font = MARK_CURRENT_FONT (text, &lp.end);
4829
4830       lp.font_ascent = font->ascent;
4831       lp.font_descent = font->descent;
4832     }
4833   
4834   lp.tab_cont_next = *next_cont;
4835   
4836   return lp;
4837 }
4838
4839 static void
4840 expand_scratch_buffer (GtkText* text, guint len)
4841 {
4842   if (len >= text->scratch_buffer_len)
4843     {
4844       guint i = 1;
4845       
4846       while (i <= len && i < MIN_GAP_SIZE) i <<= 1;
4847       
4848       if (text->use_wchar)
4849         {
4850           if (text->scratch_buffer.wc)
4851             text->scratch_buffer.wc = g_new (GdkWChar, i);
4852           else
4853             text->scratch_buffer.wc = g_realloc (text->scratch_buffer.wc,
4854                                               i*sizeof (GdkWChar));
4855         }
4856       else
4857         {
4858           if (text->scratch_buffer.ch)
4859             text->scratch_buffer.ch = g_new (guchar, i);
4860           else
4861             text->scratch_buffer.ch = g_realloc (text->scratch_buffer.ch, i);
4862         }
4863       
4864       text->scratch_buffer_len = i;
4865     }
4866 }
4867
4868 /* Side effect: modifies text->gc
4869  */
4870
4871 static void
4872 draw_bg_rect (GtkText* text, GtkPropertyMark *mark,
4873               gint x, gint y, gint width, gint height,
4874               gboolean already_cleared)
4875 {
4876   GtkEditable *editable = GTK_EDITABLE(text);
4877
4878   if ((mark->index >= MIN(editable->selection_start_pos, editable->selection_end_pos) &&
4879        mark->index < MAX(editable->selection_start_pos, editable->selection_end_pos)))
4880     {
4881       gtk_paint_flat_box(GTK_WIDGET(text)->style, text->text_area,
4882                          editable->has_selection ?
4883                             GTK_STATE_SELECTED : GTK_STATE_ACTIVE, 
4884                          GTK_SHADOW_NONE,
4885                          NULL, GTK_WIDGET(text), "text",
4886                          x, y, width, height);
4887     }
4888   else if (!gdk_color_equal(MARK_CURRENT_BACK (text, mark),
4889                             &GTK_WIDGET(text)->style->base[GTK_WIDGET_STATE (text)]))
4890     {
4891       gdk_gc_set_foreground (text->gc, MARK_CURRENT_BACK (text, mark));
4892
4893       gdk_draw_rectangle (text->text_area,
4894                           text->gc,
4895                           TRUE, x, y, width, height);
4896     }
4897   else if (GTK_WIDGET (text)->style->bg_pixmap[GTK_STATE_NORMAL])
4898     {
4899       GdkRectangle rect;
4900       
4901       rect.x = x;
4902       rect.y = y;
4903       rect.width = width;
4904       rect.height = height;
4905       
4906       clear_area (text, &rect);
4907     }
4908   else if (!already_cleared)
4909     gdk_window_clear_area (text->text_area, x, y, width, height);
4910 }
4911
4912 static void
4913 draw_line (GtkText* text,
4914            gint pixel_start_height,
4915            LineParams* lp)
4916 {
4917   GdkGCValues gc_values;
4918   gint i;
4919   gint len = 0;
4920   guint running_offset = lp->tab_cont.pixel_offset;
4921   union { GdkWChar *wc; guchar *ch; } buffer;
4922   GdkGC *fg_gc;
4923   
4924   GtkEditable *editable = GTK_EDITABLE(text);
4925   
4926   guint selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos);
4927   guint selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos);
4928   
4929   GtkPropertyMark mark = lp->start;
4930   TabStopMark tab_mark = lp->tab_cont.tab_start;
4931   gint pixel_height = pixel_start_height + lp->font_ascent;
4932   guint chars = lp->displayable_chars;
4933   
4934   /* First provide a contiguous segment of memory.  This makes reading
4935    * the code below *much* easier, and only incurs the cost of copying
4936    * when the line being displayed spans the gap. */
4937   if (mark.index <= text->gap_position &&
4938       mark.index + chars > text->gap_position)
4939     {
4940       expand_scratch_buffer (text, chars);
4941       
4942       if (text->use_wchar)
4943         {
4944           for (i = 0; i < chars; i += 1)
4945             text->scratch_buffer.wc[i] = GTK_TEXT_INDEX(text, mark.index + i);
4946           buffer.wc = text->scratch_buffer.wc;
4947         }
4948       else
4949         {
4950           for (i = 0; i < chars; i += 1)
4951             text->scratch_buffer.ch[i] = GTK_TEXT_INDEX(text, mark.index + i);
4952           buffer.ch = text->scratch_buffer.ch;
4953         }
4954     }
4955   else
4956     {
4957       if (text->use_wchar)
4958         {
4959           if (mark.index >= text->gap_position)
4960             buffer.wc = text->text.wc + mark.index + text->gap_size;
4961           else
4962             buffer.wc = text->text.wc + mark.index;
4963         }
4964       else
4965         {
4966           if (mark.index >= text->gap_position)
4967             buffer.ch = text->text.ch + mark.index + text->gap_size;
4968           else
4969             buffer.ch = text->text.ch + mark.index;
4970         }
4971     }
4972   
4973   
4974   if (running_offset > 0)
4975     {
4976       draw_bg_rect (text, &mark, 0, pixel_start_height, running_offset,
4977                     LINE_HEIGHT (*lp), TRUE);
4978     }
4979   
4980   while (chars > 0)
4981     {
4982       len = 0;
4983       if ((text->use_wchar && buffer.wc[0] != '\t') ||
4984           (!text->use_wchar && buffer.ch[0] != '\t'))
4985         {
4986           union { GdkWChar *wc; guchar *ch; } next_tab;
4987           gint pixel_width;
4988           GdkFont *font;
4989
4990           next_tab.wc = NULL;
4991           if (text->use_wchar)
4992             for (i=0; i<chars; i++)
4993               {
4994                 if (buffer.wc[i] == '\t')
4995                   {
4996                     next_tab.wc = buffer.wc + i;
4997                     break;
4998                   }
4999               }
5000           else
5001             next_tab.ch = memchr (buffer.ch, '\t', chars);
5002
5003           len = MIN (MARK_CURRENT_PROPERTY (&mark)->length - mark.offset, chars);
5004           
5005           if (text->use_wchar)
5006             {
5007               if (next_tab.wc)
5008                 len = MIN (len, next_tab.wc - buffer.wc);
5009             }
5010           else
5011             {
5012               if (next_tab.ch)
5013                 len = MIN (len, next_tab.ch - buffer.ch);
5014             }
5015
5016           if (mark.index < selection_start_pos)
5017             len = MIN (len, selection_start_pos - mark.index);
5018           else if (mark.index < selection_end_pos)
5019             len = MIN (len, selection_end_pos - mark.index);
5020
5021           font = MARK_CURRENT_FONT (text, &mark);
5022           if (font->type == GDK_FONT_FONT)
5023             {
5024               gdk_gc_set_font (text->gc, font);
5025               gdk_gc_get_values (text->gc, &gc_values);
5026               if (text->use_wchar)
5027                 pixel_width = gdk_text_width_wc (gc_values.font,
5028                                                  buffer.wc, len);
5029               else
5030               pixel_width = gdk_text_width (gc_values.font,
5031                                               buffer.ch, len);
5032             }
5033           else
5034             {
5035               if (text->use_wchar)
5036                 pixel_width = gdk_text_width_wc (font, buffer.wc, len);
5037               else
5038                 pixel_width = gdk_text_width (font, buffer.ch, len);
5039             }
5040           
5041           draw_bg_rect (text, &mark, running_offset, pixel_start_height,
5042                         pixel_width, LINE_HEIGHT (*lp), TRUE);
5043           
5044           if ((mark.index >= selection_start_pos) && 
5045               (mark.index < selection_end_pos))
5046             {
5047               if (editable->has_selection)
5048                 fg_gc = GTK_WIDGET(text)->style->fg_gc[GTK_STATE_SELECTED];
5049               else
5050                 fg_gc = GTK_WIDGET(text)->style->fg_gc[GTK_STATE_ACTIVE];
5051             }
5052           else
5053             {
5054               gdk_gc_set_foreground (text->gc, MARK_CURRENT_FORE (text, &mark));
5055               fg_gc = text->gc;
5056             }
5057
5058           if (text->use_wchar)
5059             gdk_draw_text_wc (text->text_area, MARK_CURRENT_FONT (text, &mark),
5060                               fg_gc,
5061                               running_offset,
5062                               pixel_height,
5063                               buffer.wc,
5064                               len);
5065           else
5066             gdk_draw_text (text->text_area, MARK_CURRENT_FONT (text, &mark),
5067                            fg_gc,
5068                            running_offset,
5069                            pixel_height,
5070                            buffer.ch,
5071                            len);
5072           
5073           running_offset += pixel_width;
5074           
5075           advance_tab_mark_n (text, &tab_mark, len);
5076         }
5077       else
5078         {
5079           gint pixels_remaining;
5080           gint space_width;
5081           gint spaces_avail;
5082               
5083           len = 1;
5084           
5085           gdk_window_get_size (text->text_area, &pixels_remaining, NULL);
5086           pixels_remaining -= (LINE_WRAP_ROOM + running_offset);
5087           
5088           space_width = MARK_CURRENT_TEXT_FONT(text, &mark)->char_widths[' '];
5089           
5090           spaces_avail = pixels_remaining / space_width;
5091           spaces_avail = MIN (spaces_avail, tab_mark.to_next_tab);
5092
5093           draw_bg_rect (text, &mark, running_offset, pixel_start_height,
5094                         spaces_avail * space_width, LINE_HEIGHT (*lp), TRUE);
5095
5096           running_offset += tab_mark.to_next_tab *
5097             MARK_CURRENT_TEXT_FONT(text, &mark)->char_widths[' '];
5098
5099           advance_tab_mark (text, &tab_mark, '\t');
5100         }
5101       
5102       advance_mark_n (&mark, len);
5103       if (text->use_wchar)
5104         buffer.wc += len;
5105       else
5106         buffer.ch += len;
5107       chars -= len;
5108     }
5109 }
5110
5111 static void
5112 draw_line_wrap (GtkText* text, guint height /* baseline height */)
5113 {
5114   gint width;
5115   GdkPixmap *bitmap;
5116   gint bitmap_width;
5117   gint bitmap_height;
5118   
5119   if (text->line_wrap)
5120     {
5121       bitmap = text->line_wrap_bitmap;
5122       bitmap_width = line_wrap_width;
5123       bitmap_height = line_wrap_height;
5124     }
5125   else
5126     {
5127       bitmap = text->line_arrow_bitmap;
5128       bitmap_width = line_arrow_width;
5129       bitmap_height = line_arrow_height;
5130     }
5131   
5132   gdk_window_get_size (text->text_area, &width, NULL);
5133   width -= LINE_WRAP_ROOM;
5134   
5135   gdk_gc_set_stipple (text->gc,
5136                       bitmap);
5137   
5138   gdk_gc_set_fill (text->gc, GDK_STIPPLED);
5139   
5140   gdk_gc_set_foreground (text->gc, &GTK_WIDGET (text)->style->text[GTK_STATE_NORMAL]);
5141   
5142   gdk_gc_set_ts_origin (text->gc,
5143                         width + 1,
5144                         height - bitmap_height - 1);
5145   
5146   gdk_draw_rectangle (text->text_area,
5147                       text->gc,
5148                       TRUE,
5149                       width + 1,
5150                       height - bitmap_height - 1 /* one pixel above the baseline. */,
5151                       bitmap_width,
5152                       bitmap_height);
5153   
5154   gdk_gc_set_ts_origin (text->gc, 0, 0);
5155   
5156   gdk_gc_set_fill (text->gc, GDK_SOLID);
5157 }
5158
5159 static void
5160 undraw_cursor (GtkText* text, gint absolute)
5161 {
5162   GtkEditable *editable = (GtkEditable *)text;
5163
5164   TDEBUG (("in undraw_cursor\n"));
5165   
5166   if (absolute)
5167     text->cursor_drawn_level = 0;
5168   
5169   if ((text->cursor_drawn_level ++ == 0) &&
5170       (editable->selection_start_pos == editable->selection_end_pos) &&
5171       GTK_WIDGET_DRAWABLE (text) && text->line_start_cache)
5172     {
5173       GdkFont* font;
5174       
5175       g_assert(text->cursor_mark.property);
5176
5177       font = MARK_CURRENT_FONT(text, &text->cursor_mark);
5178
5179       draw_bg_rect (text, &text->cursor_mark, 
5180                     text->cursor_pos_x,
5181                     text->cursor_pos_y - text->cursor_char_offset - font->ascent,
5182                     1, font->ascent + 1, FALSE);
5183       
5184       if (text->cursor_char)
5185         {
5186           if (font->type == GDK_FONT_FONT)
5187             gdk_gc_set_font (text->gc, font);
5188
5189           gdk_gc_set_foreground (text->gc, MARK_CURRENT_FORE (text, &text->cursor_mark));
5190
5191           gdk_draw_text_wc (text->text_area, font,
5192                          text->gc,
5193                          text->cursor_pos_x,
5194                          text->cursor_pos_y - text->cursor_char_offset,
5195                          &text->cursor_char,
5196                          1);
5197         }
5198     }
5199 }
5200
5201 static gint
5202 drawn_cursor_min (GtkText* text)
5203 {
5204   GdkFont* font;
5205   
5206   g_assert(text->cursor_mark.property);
5207   
5208   font = MARK_CURRENT_FONT(text, &text->cursor_mark);
5209   
5210   return text->cursor_pos_y - text->cursor_char_offset - font->ascent;
5211 }
5212
5213 static gint
5214 drawn_cursor_max (GtkText* text)
5215 {
5216   GdkFont* font;
5217   
5218   g_assert(text->cursor_mark.property);
5219   
5220   font = MARK_CURRENT_FONT(text, &text->cursor_mark);
5221   
5222   return text->cursor_pos_y - text->cursor_char_offset;
5223 }
5224
5225 static void
5226 draw_cursor (GtkText* text, gint absolute)
5227 {
5228   GtkEditable *editable = (GtkEditable *)text;
5229   
5230   TDEBUG (("in draw_cursor\n"));
5231   
5232   if (absolute)
5233     text->cursor_drawn_level = 1;
5234   
5235   if ((--text->cursor_drawn_level == 0) &&
5236       editable->editable &&
5237       (editable->selection_start_pos == editable->selection_end_pos) &&
5238       GTK_WIDGET_DRAWABLE (text) && text->line_start_cache)
5239     {
5240       GdkFont* font;
5241       
5242       g_assert (text->cursor_mark.property);
5243
5244       font = MARK_CURRENT_FONT (text, &text->cursor_mark);
5245
5246       gdk_gc_set_foreground (text->gc, &GTK_WIDGET (text)->style->text[GTK_STATE_NORMAL]);
5247       
5248       gdk_draw_line (text->text_area, text->gc, text->cursor_pos_x,
5249                      text->cursor_pos_y - text->cursor_char_offset,
5250                      text->cursor_pos_x,
5251                      text->cursor_pos_y - text->cursor_char_offset - font->ascent);
5252     }
5253 }
5254
5255 static GdkGC *
5256 create_bg_gc (GtkText *text)
5257 {
5258   GdkGCValues values;
5259   
5260   values.tile = GTK_WIDGET (text)->style->bg_pixmap[GTK_STATE_NORMAL];
5261   values.fill = GDK_TILED;
5262
5263   return gdk_gc_new_with_values (text->text_area, &values,
5264                                  GDK_GC_FILL | GDK_GC_TILE);
5265 }
5266
5267 static void
5268 clear_area (GtkText *text, GdkRectangle *area)
5269 {
5270   GtkWidget *widget = GTK_WIDGET (text);
5271   
5272   if (text->bg_gc)
5273     {
5274       gint width, height;
5275       
5276       gdk_window_get_size (widget->style->bg_pixmap[GTK_STATE_NORMAL], &width, &height);
5277       
5278       gdk_gc_set_ts_origin (text->bg_gc,
5279                             (- text->first_onscreen_hor_pixel) % width,
5280                             (- text->first_onscreen_ver_pixel) % height);
5281
5282       gdk_draw_rectangle (text->text_area, text->bg_gc, TRUE,
5283                           area->x, area->y, area->width, area->height);
5284     }
5285   else
5286     gdk_window_clear_area (text->text_area, area->x, area->y, area->width, area->height);
5287 }
5288
5289 static void
5290 expose_text (GtkText* text, GdkRectangle *area, gboolean cursor)
5291 {
5292   GList *cache = text->line_start_cache;
5293   gint pixels = - text->first_cut_pixels;
5294   gint min_y = MAX (0, area->y);
5295   gint max_y = MAX (0, area->y + area->height);
5296   gint height;
5297   
5298   gdk_window_get_size (text->text_area, NULL, &height);
5299   max_y = MIN (max_y, height);
5300   
5301   TDEBUG (("in expose x=%d y=%d w=%d h=%d\n", area->x, area->y, area->width, area->height));
5302   
5303   clear_area (text, area);
5304   
5305   for (; pixels < height; cache = cache->next)
5306     {
5307       if (pixels < max_y && (pixels + (gint)LINE_HEIGHT(CACHE_DATA(cache))) >= min_y)
5308         {
5309           draw_line (text, pixels, &CACHE_DATA(cache));
5310           
5311           if (CACHE_DATA(cache).wraps)
5312             draw_line_wrap (text, pixels + CACHE_DATA(cache).font_ascent);
5313         }
5314       
5315       if (cursor && GTK_WIDGET_HAS_FOCUS (text))
5316         {
5317           if (CACHE_DATA(cache).start.index <= text->cursor_mark.index &&
5318               CACHE_DATA(cache).end.index >= text->cursor_mark.index)
5319             {
5320               /* We undraw and draw the cursor here to get the drawn
5321                * level right ... FIXME - maybe the second parameter
5322                * of draw_cursor should work differently
5323                */
5324               undraw_cursor (text, FALSE);
5325               draw_cursor (text, FALSE);
5326             }
5327         }
5328       
5329       pixels += LINE_HEIGHT(CACHE_DATA(cache));
5330       
5331       if (!cache->next)
5332         {
5333           fetch_lines_forward (text, 1);
5334           
5335           if (!cache->next)
5336             break;
5337         }
5338     }
5339 }
5340
5341 static void 
5342 gtk_text_update_text    (GtkEditable       *editable,
5343                          gint               start_pos,
5344                          gint               end_pos)
5345 {
5346   GtkText *text = GTK_TEXT (editable);
5347   
5348   GList *cache = text->line_start_cache;
5349   gint pixels = - text->first_cut_pixels;
5350   GdkRectangle area;
5351   gint width;
5352   gint height;
5353   
5354   if (end_pos < 0)
5355     end_pos = TEXT_LENGTH (text);
5356   
5357   if (end_pos < start_pos)
5358     return;
5359   
5360   gdk_window_get_size (text->text_area, &width, &height);
5361   area.x = 0;
5362   area.y = -1;
5363   area.width = width;
5364   area.height = 0;
5365   
5366   TDEBUG (("in expose span start=%d stop=%d\n", start_pos, end_pos));
5367   
5368   for (; pixels < height; cache = cache->next)
5369     {
5370       if (CACHE_DATA(cache).start.index < end_pos)
5371         {
5372           if (CACHE_DATA(cache).end.index >= start_pos)
5373             {
5374               if (area.y < 0)
5375                 area.y = MAX(0,pixels);
5376               area.height = pixels + LINE_HEIGHT(CACHE_DATA(cache)) - area.y;
5377             }
5378         }
5379       else
5380         break;
5381       
5382       pixels += LINE_HEIGHT(CACHE_DATA(cache));
5383       
5384       if (!cache->next)
5385         {
5386           fetch_lines_forward (text, 1);
5387           
5388           if (!cache->next)
5389             break;
5390         }
5391     }
5392   
5393   if (area.y >= 0)
5394     expose_text (text, &area, TRUE);
5395 }
5396
5397 static void
5398 recompute_geometry (GtkText* text)
5399 {
5400   GtkPropertyMark mark, start_mark;
5401   GList *new_lines;
5402   gint height;
5403   gint width;
5404   
5405   free_cache (text);
5406   
5407   mark = start_mark = set_vertical_scroll (text);
5408
5409   /* We need a real start of a line when calling fetch_lines().
5410    * not the start of a wrapped line.
5411    */
5412   while (mark.index > 0 &&
5413          GTK_TEXT_INDEX (text, mark.index - 1) != LINE_DELIM)
5414     decrement_mark (&mark);
5415
5416   gdk_window_get_size (text->text_area, &width, &height);
5417
5418   /* Fetch an entire line, to make sure that we get all the text
5419    * we backed over above, in addition to enough text to fill up
5420    * the space vertically
5421    */
5422
5423   new_lines = fetch_lines (text,
5424                            &mark,
5425                            NULL,
5426                            FetchLinesCount,
5427                            1);
5428
5429   mark = CACHE_DATA (g_list_last (new_lines)).end;
5430   if (!LAST_INDEX (text, mark))
5431     {
5432       advance_mark (&mark);
5433
5434       new_lines = g_list_concat (new_lines, 
5435                                  fetch_lines (text,
5436                                               &mark,
5437                                               NULL,
5438                                               FetchLinesPixels,
5439                                               height + text->first_cut_pixels));
5440     }
5441
5442   /* Now work forward to the actual first onscreen line */
5443
5444   while (CACHE_DATA (new_lines).start.index < start_mark.index)
5445     new_lines = new_lines->next;
5446   
5447   text->line_start_cache = new_lines;
5448   
5449   find_cursor (text, TRUE);
5450 }
5451
5452 /**********************************************************************/
5453 /*                            Selection                               */
5454 /**********************************************************************/
5455
5456 static void 
5457 gtk_text_set_selection  (GtkEditable   *editable,
5458                          gint           start,
5459                          gint           end)
5460 {
5461   GtkText *text = GTK_TEXT (editable);
5462   
5463   guint start1, end1, start2, end2;
5464   
5465   if (end < 0)
5466     end = TEXT_LENGTH (text);
5467   
5468   start1 = MIN(start,end);
5469   end1 = MAX(start,end);
5470   start2 = MIN(editable->selection_start_pos, editable->selection_end_pos);
5471   end2 = MAX(editable->selection_start_pos, editable->selection_end_pos);
5472   
5473   if (start2 < start1)
5474     {
5475       guint tmp;
5476       
5477       tmp = start1; start1 = start2; start2 = tmp;
5478       tmp = end1;   end1   = end2;   end2   = tmp;
5479     }
5480   
5481   undraw_cursor (text, FALSE);
5482   editable->selection_start_pos = start;
5483   editable->selection_end_pos = end;
5484   draw_cursor (text, FALSE);
5485   
5486   /* Expose only what changed */
5487   
5488   if (start1 < start2)
5489     gtk_text_update_text (editable, start1, MIN(end1, start2));
5490   
5491   if (end2 > end1)
5492     gtk_text_update_text (editable, MAX(end1, start2), end2);
5493   else if (end2 < end1)
5494     gtk_text_update_text (editable, end2, end1);
5495 }
5496
5497
5498 /**********************************************************************/
5499 /*                              Debug                                 */
5500 /**********************************************************************/
5501
5502 #ifdef DEBUG_GTK_TEXT
5503 static void
5504 gtk_text_show_cache_line (GtkText *text, GList *cache,
5505                           const char* what, const char* func, gint line)
5506 {
5507   LineParams *lp = &CACHE_DATA(cache);
5508   gint i;
5509   
5510   if (cache == text->line_start_cache)
5511     g_message ("Line Start Cache: ");
5512   
5513   if (cache == text->current_line)
5514     g_message("Current Line: ");
5515   
5516   g_message ("%s:%d: cache line %s s=%d,e=%d,lh=%d (",
5517              func,
5518              line,
5519              what,
5520              lp->start.index,
5521              lp->end.index,
5522              LINE_HEIGHT(*lp));
5523   
5524   for (i = lp->start.index; i < (lp->end.index + lp->wraps); i += 1)
5525     g_message ("%c", GTK_TEXT_INDEX (text, i));
5526   
5527   g_message (")\n");
5528 }
5529
5530 static void
5531 gtk_text_show_cache (GtkText *text, const char* func, gint line)
5532 {
5533   GList *l = text->line_start_cache;
5534   
5535   if (!l) {
5536     return;
5537   }
5538   
5539   /* back up to the absolute beginning of the line cache */
5540   while (l->prev)
5541     l = l->prev;
5542   
5543   g_message ("*** line cache ***\n");
5544   for (; l; l = l->next)
5545     gtk_text_show_cache_line (text, l, "all", func, line);
5546 }
5547
5548 static void
5549 gtk_text_assert_mark (GtkText         *text,
5550                       GtkPropertyMark *mark,
5551                       GtkPropertyMark *before,
5552                       GtkPropertyMark *after,
5553                       const gchar     *msg,
5554                       const gchar     *where,
5555                       gint             line)
5556 {
5557   GtkPropertyMark correct_mark = find_mark (text, mark->index);
5558   
5559   if (mark->offset != correct_mark.offset ||
5560       mark->property != correct_mark.property)
5561     g_warning ("incorrect %s text property marker in %s:%d, index %d -- bad!", where, msg, line, mark->index);
5562 }
5563
5564 static void
5565 gtk_text_assert (GtkText         *text,
5566                  const gchar     *msg,
5567                  gint             line)
5568 {
5569   GList* cache = text->line_start_cache;
5570   GtkPropertyMark* before_mark = NULL;
5571   GtkPropertyMark* after_mark = NULL;
5572   
5573   gtk_text_show_props (text, msg, line);
5574   
5575   for (; cache->prev; cache = cache->prev)
5576     /* nothing */;
5577   
5578   g_message ("*** line markers ***\n");
5579   
5580   for (; cache; cache = cache->next)
5581     {
5582       after_mark = &CACHE_DATA(cache).end;
5583       gtk_text_assert_mark (text, &CACHE_DATA(cache).start, before_mark, after_mark, msg, "start", line);
5584       before_mark = &CACHE_DATA(cache).start;
5585       
5586       if (cache->next)
5587         after_mark = &CACHE_DATA(cache->next).start;
5588       else
5589         after_mark = NULL;
5590       
5591       gtk_text_assert_mark (text, &CACHE_DATA(cache).end, before_mark, after_mark, msg, "end", line);
5592       before_mark = &CACHE_DATA(cache).end;
5593     }
5594 }
5595
5596 static void
5597 gtk_text_show_adj (GtkText *text,
5598                    GtkAdjustment *adj,
5599                    const char* what,
5600                    const char* func,
5601                    gint line)
5602 {
5603   g_message ("*** adjustment ***\n");
5604   
5605   g_message ("%s:%d: %s adjustment l=%.1f u=%.1f v=%.1f si=%.1f pi=%.1f ps=%.1f\n",
5606              func,
5607              line,
5608              what,
5609              adj->lower,
5610              adj->upper,
5611              adj->value,
5612              adj->step_increment,
5613              adj->page_increment,
5614              adj->page_size);
5615 }
5616
5617 static void
5618 gtk_text_show_props (GtkText *text,
5619                      const char* msg,
5620                      int line)
5621 {
5622   GList* props = text->text_properties;
5623   int proplen = 0;
5624   
5625   g_message ("%s:%d: ", msg, line);
5626   
5627   for (; props; props = props->next)
5628     {
5629       TextProperty *p = (TextProperty*)props->data;
5630       
5631       proplen += p->length;
5632
5633       g_message ("[%d,%p,", p->length, p);
5634       if (p->flags & PROPERTY_FONT)
5635         g_message ("%p,", p->font);
5636       else
5637         g_message ("-,");
5638       if (p->flags & PROPERTY_FOREGROUND)
5639         g_message ("%ld, ", p->fore_color.pixel);
5640       else
5641         g_message ("-,");
5642       if (p->flags & PROPERTY_BACKGROUND)
5643         g_message ("%ld] ", p->back_color.pixel);
5644       else
5645         g_message ("-] ");
5646     }
5647   
5648   g_message ("\n");
5649   
5650   if (proplen - 1 != TEXT_LENGTH(text))
5651     g_warning ("incorrect property list length in %s:%d -- bad!", msg, line);
5652 }
5653 #endif