source: trunk/third/gtk/gtk/gtktext.c @ 17071

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