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