Blame docs/text_widget.txt

Packit Service fb6fa5
Date: Sun, 14 Sep 1997 20:17:06 -0700 (PDT)
Packit Service fb6fa5
From: Josh MacDonald <jmacd@CS.Berkeley.EDU>
Packit Service fb6fa5
To: gnome@athena.nuclecu.unam.mx, gtk-list@redhat.com
Packit Service fb6fa5
Subject: [gtk-list] gtktext widget internal documentation
Packit Service fb6fa5
Packit Service fb6fa5
Packit Service fb6fa5
Pete convinced me to just write up the text widget and let someone else
Packit Service fb6fa5
finish it.  I'm pretty busy and have other commitments now.  Sorry.  I think
Packit Service fb6fa5
I'm not the most qualified for some of the remaining work anyway, because I
Packit Service fb6fa5
don't really know Gtk and it's event model very well.  Most of the work so 
Packit Service fb6fa5
far was possible without knowing Gtk all that well, it was simply a data 
Packit Service fb6fa5
structure exercise (though after reading this you might say it was a fairly
Packit Service fb6fa5
complicated data structure exercise).  I'm happy to answer questions.
Packit Service fb6fa5
Packit Service fb6fa5
-josh
Packit Service fb6fa5
Packit Service fb6fa5
Packit Service fb6fa5
High level description:
Packit Service fb6fa5
Packit Service fb6fa5
There are several layers of data structure to the widget.  They are
Packit Service fb6fa5
separated from each other as much as possible.  The first is a gapped
Packit Service fb6fa5
text segment similar to the data structure Emacs uses for representing
Packit Service fb6fa5
text.  Then there is a property list, which stores text properties for
Packit Service fb6fa5
various ranges of text.  There is no direct relation between the text
Packit Service fb6fa5
property list and the gapped text segment.  Finally there is a drawn
Packit Service fb6fa5
line parameter cache to speed calculations when drawing and redrawing
Packit Service fb6fa5
lines on screen.  In addition to these data structures, there are
Packit Service fb6fa5
structures to help iterate over text in the buffer.
Packit Service fb6fa5
Packit Service fb6fa5
The gapped text segment is quite simple.  It's parameters are (all
Packit Service fb6fa5
parameters I mention here are in the structure GtkText):
Packit Service fb6fa5
Packit Service fb6fa5
  guchar* text;
Packit Service fb6fa5
  guint text_len;
Packit Service fb6fa5
  guint gap_position;
Packit Service fb6fa5
  guint gap_size;
Packit Service fb6fa5
  guint text_end;
Packit Service fb6fa5
Packit Service fb6fa5
TEXT is the buffer, TEXT_LEN is its allocated length.  TEXT_END is the
Packit Service fb6fa5
length of the text, including the gap.  GAP_POSITION is the start of
Packit Service fb6fa5
the gap, and GAP_SIZE is the gap's length.  Therefore, TEXT_END -
Packit Service fb6fa5
GAP_SIZE is the length of the text in the buffer.  The macro
Packit Service fb6fa5
TEXT_LENGTH returns this value.  To get the value of a character in
Packit Service fb6fa5
the buffer, use the macro TEXT_INDEX(TEXT,INDEX).  This macro tests
Packit Service fb6fa5
whether the index is less than the GAP_POSITION and returns
Packit Service fb6fa5
TEXT[INDEX] or returns TEXT[GAP_SIZE+INDEX].  The function
Packit Service fb6fa5
MOVE_GAP_TO_POINT positions the gap to a particular index.  The
Packit Service fb6fa5
function MAKE_FORWARD_SPACE lengthens the gap to provide room for a
Packit Service fb6fa5
certain number of characters.
Packit Service fb6fa5
Packit Service fb6fa5
The property list is a doubly linked list (GList) of text property
Packit Service fb6fa5
data for each contiguous set of characters with similar properties.
Packit Service fb6fa5
The data field of the GList points to a TextProperty structure, which
Packit Service fb6fa5
contains:
Packit Service fb6fa5
Packit Service fb6fa5
  TextFont* font;
Packit Service fb6fa5
  GdkColor* back_color;
Packit Service fb6fa5
  GdkColor* fore_color;
Packit Service fb6fa5
  guint length;
Packit Service fb6fa5
Packit Service fb6fa5
Currently, only font and color data are contained in the property
Packit Service fb6fa5
list, but it can be extended by modifying the INSERT_TEXT_PROPERTY,
Packit Service fb6fa5
TEXT_PROPERTIES_EQUAL, and a few other procedures.  The text property
Packit Service fb6fa5
structure does not contain an absolute offset, only a length.  As a
Packit Service fb6fa5
result, inserting a character into the buffer simply requires moving
Packit Service fb6fa5
the gap to the correct position, making room in the buffer, and either
Packit Service fb6fa5
inserting a new property or extending the old one.  This logic is done
Packit Service fb6fa5
by INSERT_TEXT_PROPERTY.  A similar procedure exists to delete from
Packit Service fb6fa5
the text property list, DELETE_TEXT_PROPERTY.  Since the property
Packit Service fb6fa5
structure doesn't contain an offset, insertion into the list is an
Packit Service fb6fa5
O(1) operation.  All such operations act on the insertion point, which
Packit Service fb6fa5
is the POINT field of the GtkText structure.
Packit Service fb6fa5
Packit Service fb6fa5
The GtkPropertyMark structure is used for keeping track of the mapping
Packit Service fb6fa5
between absolute buffer offsets and positions in the property list.
Packit Service fb6fa5
These will be referred to as property marks.  Generally, there are
Packit Service fb6fa5
four property marks the system keeps track of.  Two are trivial, the
Packit Service fb6fa5
beginning and the end of the buffer are easy to find.  The other two
Packit Service fb6fa5
are the insertion point (POINT) and the cursor point (CURSOR_MARK).
Packit Service fb6fa5
All operations on the text buffer are done using a property mark as a
Packit Service fb6fa5
sort of cursor to keep track of the alignment of the property list and
Packit Service fb6fa5
the absolute buffer offset.  The GtkPropertyMark structure contains:
Packit Service fb6fa5
Packit Service fb6fa5
  GList* property;
Packit Service fb6fa5
  guint offset;
Packit Service fb6fa5
  guint index;
Packit Service fb6fa5
Packit Service fb6fa5
PROPERTY is a pointer at the current property list element.  INDEX is
Packit Service fb6fa5
the absolute buffer index, and OFFSET is the offset of INDEX from the
Packit Service fb6fa5
beginning of PROPERTY.  It is essential to keep property marks valid,
Packit Service fb6fa5
or else you will have the wrong text properties at each property mark
Packit Service fb6fa5
transition.  An important point is that all property marks are invalid
Packit Service fb6fa5
after a buffer modification unless care is taken to keep them
Packit Service fb6fa5
accurate.  That is the difficulty of the insert and delete operations,
Packit Service fb6fa5
because as the next section describes, line data is cached and by
Packit Service fb6fa5
necessity contains text property marks.  The functions for operating
Packit Service fb6fa5
and computing property marks are:
Packit Service fb6fa5
Packit Service fb6fa5
 void advance_mark     (GtkPropertyMark* mark);
Packit Service fb6fa5
 void decrement_mark   (GtkPropertyMark* mark);
Packit Service fb6fa5
 void advance_mark_n   (GtkPropertyMark* mark, gint n);
Packit Service fb6fa5
 void decrement_mark_n (GtkPropertyMark* mark, gint n);
Packit Service fb6fa5
 void move_mark_n      (GtkPropertyMark* mark, gint n);
Packit Service fb6fa5
Packit Service fb6fa5
 GtkPropertyMark find_mark      (GtkText* text, guint mark_position);
Packit Service fb6fa5
 GtkPropertyMark find_mark_near (GtkText* text, guint mark_position,
Packit Service fb6fa5
                                 const GtkPropertyMark* near);
Packit Service fb6fa5
Packit Service fb6fa5
ADVANCE_MARK and DECREMENT_MARK modify the mark by plus or minus one
Packit Service fb6fa5
buffer index.  ADVANCE_MARK_N and DECREMENT_MARK_N modify the mark by
Packit Service fb6fa5
plus or minus N indices.  MOVE_MARK_N accepts a positive or negative
Packit Service fb6fa5
argument.  FIND_MARK returns a mark at MARK_POSITION using a linear
Packit Service fb6fa5
search from the nearest known property mark (the beginning, the end,
Packit Service fb6fa5
the point, etc).  FIND_MARK_NEAR also does a linear search, but
Packit Service fb6fa5
searches from the NEAR argument.  A number of macros exist at the top
Packit Service fb6fa5
of the file for doing things like getting the current text property,
Packit Service fb6fa5
or some component of the current property.  See the MARK_* macros.
Packit Service fb6fa5
Packit Service fb6fa5
Next there is a LineParams structure which contains all the
Packit Service fb6fa5
information necessary to draw one line of text on screen.  When I say
Packit Service fb6fa5
"line" here, I do not mean one line of text separated by newlines,
Packit Service fb6fa5
rather I mean one row of text on screen.  It is a matter of policy how
Packit Service fb6fa5
visible lines are chosen and there are currently two policies,
Packit Service fb6fa5
line-wrap and no-line-wrap.  I suspect it would not be difficult to
Packit Service fb6fa5
implement new policies for doing such things as justification.  The
Packit Service fb6fa5
LineParams structure includes the following fields:
Packit Service fb6fa5
Packit Service fb6fa5
  guint font_ascent;
Packit Service fb6fa5
  guint font_descent;
Packit Service fb6fa5
  guint pixel_width;
Packit Service fb6fa5
  guint displayable_chars;
Packit Service fb6fa5
  guint wraps : 1;
Packit Service fb6fa5
Packit Service fb6fa5
  PrevTabCont tab_cont;
Packit Service fb6fa5
  PrevTabCont tab_cont_next;
Packit Service fb6fa5
Packit Service fb6fa5
  GtkPropertyMark start;
Packit Service fb6fa5
  GtkPropertyMark end;
Packit Service fb6fa5
Packit Service fb6fa5
FONT_ASCENT and FONT_DESCENT are the maximum ascent and descent of any
Packit Service fb6fa5
character in the line.  PIXEL_WIDTH is the number of pixels wide the
Packit Service fb6fa5
drawn region is, though I don't think it's actually being used
Packit Service fb6fa5
currently.  You may wish to remove this field, eventually, though I
Packit Service fb6fa5
suspect it will come in handy implementing horizontal scrolling.
Packit Service fb6fa5
DISPLAYABLE_CHARS is the number of characters in the line actually
Packit Service fb6fa5
drawn.  This may be less than the number of characters in the line
Packit Service fb6fa5
when line wrapping is off (see below).  The bitflag WRAPS tells
Packit Service fb6fa5
whether the next line is a continuation of this line.  START and END
Packit Service fb6fa5
are the marks at the beginning and end of the line.  Note that END is
Packit Service fb6fa5
the actual last character, not one past it, so the smallest line
Packit Service fb6fa5
(containing, for example, one newline) has START == END.  TAB_CONT and
Packit Service fb6fa5
TAB_CONT_NEXT are for computation of tab positions.  I will discuss
Packit Service fb6fa5
them later.
Packit Service fb6fa5
Packit Service fb6fa5
A point about the end of the buffer.  You may be tempted to consider
Packit Service fb6fa5
working with the buffer as an array of length TEXT_LENGTH(TEXT), but
Packit Service fb6fa5
you have to be careful that the editor allows you to position your
Packit Service fb6fa5
cursor at the last index of the buffer, one past the last character.
Packit Service fb6fa5
The macro LAST_INDEX(TEXT, MARK) returns true if MARK is positioned at
Packit Service fb6fa5
this index.  If you see or add a special case in the code for this
Packit Service fb6fa5
end-of-buffer case, make sure to use LAST_INDEX if you can.  Very
Packit Service fb6fa5
often, the last index is treated as a newline.
Packit Service fb6fa5
Packit Service fb6fa5
[ One way the last index is special is that, although it is always
Packit Service fb6fa5
  part of some property, it will never be part of a property of
Packit Service fb6fa5
  length 1 unless there are no other characters in the text. That
Packit Service fb6fa5
  is, its properties are always that of the preceding character,
Packit Service fb6fa5
  if any.
Packit Service fb6fa5
  
Packit Service fb6fa5
  There is a fair bit of special case code to maintain this condition -
Packit Service fb6fa5
  which is needed so that user has control over the properties of
Packit Service fb6fa5
  characters inserted at the last position. OWT 2/9/98 ]
Packit Service fb6fa5
Packit Service fb6fa5
Tab stops are variable width.  A list of tab stops is contained in the
Packit Service fb6fa5
GtkText structure:
Packit Service fb6fa5
Packit Service fb6fa5
  GList *tab_stops;
Packit Service fb6fa5
  gint default_tab_width;
Packit Service fb6fa5
Packit Service fb6fa5
The elements of tab_stops are integers casted to gpointer.  This is a
Packit Service fb6fa5
little bogus, but works.  For example:
Packit Service fb6fa5
Packit Service fb6fa5
  text->default_tab_width = 4;
Packit Service fb6fa5
  text->tab_stops = NULL;
Packit Service fb6fa5
  text->tab_stops = g_list_prepend (text->tab_stops, (void*)8);
Packit Service fb6fa5
  text->tab_stops = g_list_prepend (text->tab_stops, (void*)8);
Packit Service fb6fa5
Packit Service fb6fa5
is how these fields are initialized, currently.  This means that the
Packit Service fb6fa5
first two tabs occur at 8 and 16, and every 4 characters thereafter.
Packit Service fb6fa5
Tab stops are used in the computation of line geometry (to fill in a
Packit Service fb6fa5
LineParams structure), and the width of the space character in the
Packit Service fb6fa5
current font is used.  The PrevTabCont structure, of which two are
Packit Service fb6fa5
stored per line, is used to compute the geometry of lines which may
Packit Service fb6fa5
have wrapped and carried part of a tab with them:
Packit Service fb6fa5
Packit Service fb6fa5
  guint pixel_offset;
Packit Service fb6fa5
  TabStopMark tab_start;
Packit Service fb6fa5
Packit Service fb6fa5
PIXEL_OFFSET is the number of pixels at which the line should start,
Packit Service fb6fa5
and tab_start is a tab stop mark, which is similar to a property mark,
Packit Service fb6fa5
only it keeps track of the mapping between line position (column) and
Packit Service fb6fa5
the next tab stop.  A TabStopMark contains:
Packit Service fb6fa5
Packit Service fb6fa5
  GList* tab_stops;
Packit Service fb6fa5
  gint to_next_tab;
Packit Service fb6fa5
Packit Service fb6fa5
TAB_STOPS is a pointer into the TAB_STOPS field of the GtkText
Packit Service fb6fa5
structure.  TO_NEXT_TAB is the number of characters before the next
Packit Service fb6fa5
tab.  The functions ADVANCE_TAB_MARK and ADVANCE_TAB_MARK_N advance
Packit Service fb6fa5
these marks.  The LineParams structure contains two PrevTabCont
Packit Service fb6fa5
structures, which each contain a tab stop.  The first (TAB_CONT) is
Packit Service fb6fa5
for computing the beginning pixel offset, as mentioned above.  The
Packit Service fb6fa5
second (TAB_CONT_NEXT) is used to initialize the TAB_CONT field of the
Packit Service fb6fa5
next line if it wraps.
Packit Service fb6fa5
Packit Service fb6fa5
Since computing the parameters of a line are fairly complicated, I
Packit Service fb6fa5
have one interface that should be all you ever need to figure out
Packit Service fb6fa5
something about a line.  The function FIND_LINE_PARAMS computes the
Packit Service fb6fa5
parameters of a single line.  The function LINE_PARAMS_ITERATE is used
Packit Service fb6fa5
for computing the properties of some number (> 0) of sequential lines.
Packit Service fb6fa5
Packit Service fb6fa5
void
Packit Service fb6fa5
line_params_iterate (GtkText* text,
Packit Service fb6fa5
		     const GtkPropertyMark* mark0,
Packit Service fb6fa5
		     const PrevTabCont* tab_mark0,
Packit Service fb6fa5
		     gboolean alloc,
Packit Service fb6fa5
		     gpointer data,
Packit Service fb6fa5
		     LineIteratorFunction iter);
Packit Service fb6fa5
Packit Service fb6fa5
where LineIteratorFunction is:
Packit Service fb6fa5
Packit Service fb6fa5
typedef gint (*LineIteratorFunction) (GtkText* text,
Packit Service fb6fa5
                                      LineParams* lp,
Packit Service fb6fa5
                                      gpointer data);
Packit Service fb6fa5
Packit Service fb6fa5
The arguments are a text widget (TEXT), the property mark at the
Packit Service fb6fa5
beginning of the first line (MARK0), the tab stop mark at the
Packit Service fb6fa5
beginning of that line (TAB_MARK0), whether to heap-allocate the
Packit Service fb6fa5
LineParams structure (ALLOC), some client data (DATA), and a function
Packit Service fb6fa5
to call with the parameters of each line.  TAB_MARK0 may be NULL, but
Packit Service fb6fa5
if so MARK0 MUST BE A REAL LINE START (not a continued line start; it
Packit Service fb6fa5
is preceded by a newline).  If TAB_MARK0 is not NULL, MARK0 may be any
Packit Service fb6fa5
line start (continued or not).  See the code for examples.  The
Packit Service fb6fa5
function ITER is called with each LineParams computed.  If ALLOC was
Packit Service fb6fa5
true, LINE_PARAMS_ITERATE heap-allocates the LineParams and does not
Packit Service fb6fa5
free them.  Otherwise, no storage is permanently allocated.  ITER
Packit Service fb6fa5
should return TRUE when it wishes to continue no longer.
Packit Service fb6fa5
Packit Service fb6fa5
There are currently two uses of LINE_PARAMS_ITERATE:
Packit Service fb6fa5
Packit Service fb6fa5
* Compute the total buffer height for setting the parameters of the
Packit Service fb6fa5
  scroll bars.  This is done in SET_VERTICAL_SCROLL each time the
Packit Service fb6fa5
  window is resized.  When horizontal scrolling is added, depending on
Packit Service fb6fa5
  the policy chosen, the max line width can be computed here as well.
Packit Service fb6fa5
Packit Service fb6fa5
* Computing geometry of some pixel height worth of lines.  This is
Packit Service fb6fa5
  done in FETCH_LINES, FETCH_LINES_BACKWARD, FETCH_LINES_FORWARD, etc.
Packit Service fb6fa5
Packit Service fb6fa5
The GtkText structure contains a cache of the LineParams data for all
Packit Service fb6fa5
visible lines:
Packit Service fb6fa5
Packit Service fb6fa5
  GList *current_line;
Packit Service fb6fa5
  GList *line_start_cache;
Packit Service fb6fa5
Packit Service fb6fa5
  guint first_line_start_index;
Packit Service fb6fa5
  guint first_cut_pixels;
Packit Service fb6fa5
  guint first_onscreen_hor_pixel;
Packit Service fb6fa5
  guint first_onscreen_ver_pixel;
Packit Service fb6fa5
Packit Service fb6fa5
LINE_START_CACHE is a doubly linked list of LineParams.  CURRENT_LINE
Packit Service fb6fa5
is a transient piece of data which is set in various places such as
Packit Service fb6fa5
the mouse click code.  Generally, it is the line on which the cursor
Packit Service fb6fa5
property mark CURSOR_MARK is on.  LINE_START_CACHE points to the first
Packit Service fb6fa5
visible line and may contain PREV pointers if the cached data of
Packit Service fb6fa5
offscreen lines is kept around.  I haven't come up with a policy.  The
Packit Service fb6fa5
cache can keep more lines than are visible if desired, but the result
Packit Service fb6fa5
is that inserts and deletes will then become slower as the entire
Packit Service fb6fa5
cache has to be "corrected".  Right now it doesn't delete from the
Packit Service fb6fa5
cache (it should).  As a result, scrolling through the whole buffer
Packit Service fb6fa5
once will fill the cache with an entry for each line, and subsequent
Packit Service fb6fa5
modifications will be slower than they should
Packit Service fb6fa5
be. FIRST_LINE_START_INDEX is the index of the *REAL* line start of
Packit Service fb6fa5
the first line.  That is, if the first visible line is a continued
Packit Service fb6fa5
line, this is the index of the real line start (preceded by a
Packit Service fb6fa5
newline).  FIRST_CUT_PIXELS is the number of pixels which are not
Packit Service fb6fa5
drawn on the first visible line.  If FIRST_CUT_PIXELS is zero, the
Packit Service fb6fa5
whole line is visible.  FIRST_ONSCREEN_HOR_PIXEL is not used.
Packit Service fb6fa5
FIRST_ONSCREEN_VER_PIXEL is the absolute pixel which starts the
Packit Service fb6fa5
visible region.  This is used for setting the vertical scroll bar.
Packit Service fb6fa5
Packit Service fb6fa5
Other miscellaneous things in the GtkText structure:
Packit Service fb6fa5
Packit Service fb6fa5
Gtk specific things:
Packit Service fb6fa5
Packit Service fb6fa5
  GtkWidget widget;
Packit Service fb6fa5
Packit Service fb6fa5
  GdkWindow *text_area;
Packit Service fb6fa5
Packit Service fb6fa5
  GtkAdjustment *hadj;
Packit Service fb6fa5
  GtkAdjustment *vadj;
Packit Service fb6fa5
Packit Service fb6fa5
  GdkGC *gc;
Packit Service fb6fa5
Packit Service fb6fa5
  GdkPixmap* line_wrap_bitmap;
Packit Service fb6fa5
  GdkPixmap* line_arrow_bitmap;
Packit Service fb6fa5
Packit Service fb6fa5
These are pretty self explanatory, especially if you know Gtk.
Packit Service fb6fa5
LINE_WRAP_BITMAP and LINE_ARROW_BITMAP are two bitmaps used to
Packit Service fb6fa5
indicate that a line wraps and is continued offscreen, respectively.
Packit Service fb6fa5
Packit Service fb6fa5
Some flags:
Packit Service fb6fa5
Packit Service fb6fa5
  guint has_cursor : 1;
Packit Service fb6fa5
  guint is_editable : 1;
Packit Service fb6fa5
  guint line_wrap : 1;
Packit Service fb6fa5
  guint freeze : 1;
Packit Service fb6fa5
  guint has_selection : 1;
Packit Service fb6fa5
  guint own_selection : 1;
Packit Service fb6fa5
Packit Service fb6fa5
HAS_CURSOR is true iff the cursor is visible.  IS_EDITABLE is true iff
Packit Service fb6fa5
the user is allowed to modify the buffer.  If IS_EDITABLE is false,
Packit Service fb6fa5
HAS_CURSOR is guaranteed to be false.  If IS_EDITABLE is true,
Packit Service fb6fa5
HAS_CURSOR starts out false and is set to true the first time the user
Packit Service fb6fa5
clicks in the window.  LINE_WRAP is where the line-wrap policy is
Packit Service fb6fa5
set.  True means wrap lines, false means continue lines offscreen,
Packit Service fb6fa5
horizontally.
Packit Service fb6fa5
Packit Service fb6fa5
The text properties list:
Packit Service fb6fa5
Packit Service fb6fa5
  GList *text_properties;
Packit Service fb6fa5
  GList *text_properties_end;
Packit Service fb6fa5
Packit Service fb6fa5
A scratch area used for constructing a contiguous piece of the buffer
Packit Service fb6fa5
which may otherwise span the gap.  It is not strictly necessary
Packit Service fb6fa5
but simplifies the drawing code because it does not need to deal with
Packit Service fb6fa5
the gap.
Packit Service fb6fa5
Packit Service fb6fa5
  guchar* scratch_buffer;
Packit Service fb6fa5
  guint   scratch_buffer_len;
Packit Service fb6fa5
Packit Service fb6fa5
The last vertical scrollbar position.  Currently this looks the same
Packit Service fb6fa5
as FIRST_ONSCREEN_VER_PIXEL.  I can't remember why I have two values.
Packit Service fb6fa5
Perhaps someone should clean this up.
Packit Service fb6fa5
Packit Service fb6fa5
  gint last_ver_value;
Packit Service fb6fa5
Packit Service fb6fa5
The cursor:
Packit Service fb6fa5
Packit Service fb6fa5
  gint            cursor_pos_x;
Packit Service fb6fa5
  gint            cursor_pos_y;
Packit Service fb6fa5
  GtkPropertyMark cursor_mark;
Packit Service fb6fa5
  gchar           cursor_char;
Packit Service fb6fa5
  gchar           cursor_char_offset;
Packit Service fb6fa5
  gint            cursor_virtual_x;
Packit Service fb6fa5
  gint            cursor_drawn_level;
Packit Service fb6fa5
Packit Service fb6fa5
CURSOR_POS_X and CURSOR_POS_Y are the screen coordinates of the
Packit Service fb6fa5
cursor.  CURSOR_MARK is the buffer position.  CURSOR_CHAR is
Packit Service fb6fa5
TEXT_INDEX (TEXT, CURSOR_MARK.INDEX) if a drawable character, or 0 if
Packit Service fb6fa5
it is whitespace, which is treated specially.  CURSOR_CHAR_OFFSET is
Packit Service fb6fa5
the pixel offset above the base of the line at which it should be
Packit Service fb6fa5
drawn.  Note that the base of the line is not the "baseline" in the
Packit Service fb6fa5
traditional font metric sense.  A line (LineParams) is
Packit Service fb6fa5
FONT_ASCENT+FONT_DESCENT high (use the macro LINE_HEIGHT).  The
Packit Service fb6fa5
"baseline" is FONT_DESCENT below the base of the line.  I think this
Packit Service fb6fa5
requires a drawing.
Packit Service fb6fa5
Packit Service fb6fa5
0                      AAAAAAA
Packit Service fb6fa5
1                      AAAAAAA
Packit Service fb6fa5
2                     AAAAAAAAA
Packit Service fb6fa5
3                     AAAAAAAAA
Packit Service fb6fa5
4                    AAAAA AAAAA
Packit Service fb6fa5
5                    AAAAA AAAAA
Packit Service fb6fa5
6                   AAAAA   AAAAA
Packit Service fb6fa5
7                  AAAAA     AAAAA
Packit Service fb6fa5
8                  AAAAA     AAAAA
Packit Service fb6fa5
9                 AAAAAAAAAAAAAAAAA
Packit Service fb6fa5
10                AAAAAAAAAAAAAAAAA
Packit Service fb6fa5
11               AAAAA         AAAAA
Packit Service fb6fa5
12               AAAAA         AAAAA
Packit Service fb6fa5
13              AAAAAA         AAAAAA
Packit Service fb6fa5
14______________AAAAA___________AAAAA__________________________________
Packit Service fb6fa5
15
Packit Service fb6fa5
16
Packit Service fb6fa5
17
Packit Service fb6fa5
18
Packit Service fb6fa5
19
Packit Service fb6fa5
20
Packit Service fb6fa5
Packit Service fb6fa5
This line is 20 pixels high, has FONT_ASCENT=14, FONT_DESCENT=6.  It's
Packit Service fb6fa5
"base" is at y=20.  Characters are drawn at y=14.  The LINE_START
Packit Service fb6fa5
macro returns the pixel height.  The LINE_CONTAINS macro is true if
Packit Service fb6fa5
the line contains a certain buffer index.  The LINE_STARTS_AT macro is
Packit Service fb6fa5
true if the line starts at a certain buffer index.  The
Packit Service fb6fa5
LINE_START_PIXEL is the pixel offset the line should be drawn at,
Packit Service fb6fa5
according the the tab continuation of the previous line.
Packit Service fb6fa5
Packit Service fb6fa5
Exposure and drawing:
Packit Service fb6fa5
Packit Service fb6fa5
Exposure is handled from the EXPOSE_TEXT function.  It assumes that
Packit Service fb6fa5
the LINE_START_CACHE and all its parameters are accurate and simply
Packit Service fb6fa5
exposes any line which is in the exposure region.  It calls the
Packit Service fb6fa5
CLEAR_AREA function to clear the background and/or lay down a pixmap
Packit Service fb6fa5
background.  The text widget has a scrollable pixmap background, which
Packit Service fb6fa5
is implemented in CLEAR_AREA.  CLEAR_AREA does the math to figure out
Packit Service fb6fa5
how to tile the pixmap itself so that it can scroll the text with a
Packit Service fb6fa5
copy area call.  If the CURSOR argument to EXPOSE_TEXT is true, it
Packit Service fb6fa5
also draws the cursor.
Packit Service fb6fa5
Packit Service fb6fa5
The function DRAW_LINE draws a single line, doing all the tab and
Packit Service fb6fa5
color computations necessary.  The function DRAW_LINE_WRAP draws the
Packit Service fb6fa5
line wrap bitmap at the end of the line if it wraps.  TEXT_EXPOSE will
Packit Service fb6fa5
expand the cached line data list if it has to by calling
Packit Service fb6fa5
FETCH_LINES_FORWARD.  The functions DRAW_CURSOR and UNDRAW_CURSOR draw
Packit Service fb6fa5
and undraw the cursor.  They count the number of draws and undraws so
Packit Service fb6fa5
that the cursor may be undrawn even if the cursor is already undrawn
Packit Service fb6fa5
and the re-draw will not occur too early.  This is useful in handling
Packit Service fb6fa5
scrolling.
Packit Service fb6fa5
Packit Service fb6fa5
Handling of the cursor is a little messed up, I should add.  It has to
Packit Service fb6fa5
be undrawn and drawn at various places.  Something better needs to be
Packit Service fb6fa5
done about this, because it currently doesn't do the right thing in
Packit Service fb6fa5
certain places.  I can't remember where very well.  Look for the calls
Packit Service fb6fa5
to DRAW_CURSOR and UNDRAW_CURSOR.
Packit Service fb6fa5
Packit Service fb6fa5
RECOMPUTE_GEOMETRY is called when the geometry of the window changes
Packit Service fb6fa5
or when it is first drawn.  This is probably not done right.  My
Packit Service fb6fa5
biggest weakness in writing this code is that I've never written a
Packit Service fb6fa5
widget before so I got most of the event handling stuff wrong as far
Packit Service fb6fa5
as Gtk is concerned.  Fortunately, most of the code is unrelated and
Packit Service fb6fa5
simply an exercise in data structure manipulation.
Packit Service fb6fa5
Packit Service fb6fa5
Scrolling:
Packit Service fb6fa5
Packit Service fb6fa5
Scrolling is fairly straightforward.  It looks at the top line, and
Packit Service fb6fa5
advances it pixel by pixel until the FIRST_CUT_PIXELS equals the line
Packit Service fb6fa5
height and then advances the LINE_START_CACHE.  When it runs out of
Packit Service fb6fa5
lines it fetches more.  The function SCROLL_INT is used to scroll from
Packit Service fb6fa5
inside the code, it calls the appropriate functions and handles
Packit Service fb6fa5
updating the scroll bars.  It dispatches a change event which causes
Packit Service fb6fa5
Gtk to call the correct scroll action, which then enters SCROLL_UP or
Packit Service fb6fa5
SCROLL_DOWN.  Careful with the cursor during these changes.
Packit Service fb6fa5
Packit Service fb6fa5
Insertion, deletion:
Packit Service fb6fa5
Packit Service fb6fa5
There's some confusion right now over what to do with the cursor when
Packit Service fb6fa5
it's offscreen due to scrolling.  This is a policy decision.  I don't
Packit Service fb6fa5
know what's best.  Spencer criticized me for forcing it to stay
Packit Service fb6fa5
onscreen.  It shouldn't be hard to make stuff work with the cursor
Packit Service fb6fa5
offscreen.
Packit Service fb6fa5
Packit Service fb6fa5
Currently I've got functions to do insertion and deletion of a single
Packit Service fb6fa5
character.  It's fairly complicated.  In order to do efficient pasting
Packit Service fb6fa5
into the buffer, or write code that modifies the buffer while the
Packit Service fb6fa5
buffer is drawn, it needs to do multiple characters at at time.  This
Packit Service fb6fa5
is the hardest part of what remains.  Currently, gtk_text_insert does
Packit Service fb6fa5
not re-expose the modified lines.  It needs to.  Pete did this wrong at
Packit Service fb6fa5
one point and I disabled modification completely, I don't know what
Packit Service fb6fa5
the current state of things are.  The functions
Packit Service fb6fa5
INSERT_CHAR_LINE_EXPOSE and DELETE_CHAR_LINE_EXPOSE do the work.
Packit Service fb6fa5
Here's pseudo code for insert.  Delete is quite similar.
Packit Service fb6fa5
Packit Service fb6fa5
  insert character into the buffer
Packit Service fb6fa5
  update the text property list
Packit Service fb6fa5
  move the point
Packit Service fb6fa5
  undraw the cursor
Packit Service fb6fa5
  correct all LineParams cache entries after the insertion point
Packit Service fb6fa5
  compute the new height of the modified line
Packit Service fb6fa5
  compare with the old height of the modified line
Packit Service fb6fa5
  remove the old LineParams from the cache
Packit Service fb6fa5
  insert the new LineParams into the cache
Packit Service fb6fa5
  if the lines are of different height, do a copy area to move the
Packit Service fb6fa5
    area below the insertion down
Packit Service fb6fa5
  expose the current line
Packit Service fb6fa5
  update the cursor mark
Packit Service fb6fa5
  redraw the cursor
Packit Service fb6fa5
Packit Service fb6fa5
What needs to be done:
Packit Service fb6fa5
Packit Service fb6fa5
Horizontal scrolling, robustness, testing, selection handling.  If you
Packit Service fb6fa5
want to work in the text widget pay attention to the debugging
Packit Service fb6fa5
facilities I've written at the end of gtktext.c.  I'm sorry I waited
Packit Service fb6fa5
so long to try and pass this off.  I'm super busy with school and
Packit Service fb6fa5
work, and when I have free time my highest priority is another version
Packit Service fb6fa5
of PRCS.
Packit Service fb6fa5
Packit Service fb6fa5
Feel free to ask me questions.