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