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