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