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