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