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