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