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