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