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