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