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