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