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