jtexttags.tcl
The
jtexttags.tcl library is distributed as part of the
jstools package.
It lets you:
- maintain a list of
tags to apply to newly inserted text,
- avoid logical conflicts in tags (e.g., tagging the same text
yellow and blue),
- work with the tags and marks in a text widget as a
unit (e.g. to
store them),
- automatically configure the visual appearance of certain kinds
of tags,
- keep track of arbitrary outofband
metainformation associated with documents, such as title, author, fax number,
or annotations, and
-
save and restore the complete contents of a text widget, including tags, marks
and metainformation.
This document describes
jtexttags.tcl version 4.1/4.4.
Accessing the Library
In order to use the
jtexttags.tcl library, it (and any other libraries it depends on) must be
in your Tcl
auto_path, described in
tclvars(n). Information about how to arrange that, and other conventions
common to
the
jstools libraries, is in
the
Usage section of
The jstools Libraries.
Author
Jay Sekora
js@aq.org
http://www.aq.org/~js/
Copyright
The library is copyright © 1992-1995 by Jay Sekora, but may be
freely redistributed under the conditions at the top of the file.
The tags handled by
jtexttags.tcl are of the format
category:attribute:value, where
category identifies what sort of function the tag serves,
attribute identifies what class of thing the tag controls, and
value is the particular value of that attribute for the tagged text.
This is rather abstract, but
category could be things like
display to control physical appearance of the text,
command to control actions taken when the text is clicked on,
structure to identify logical parts of a document, and so forth.
attribute names a kind of attribute relevant to the category; for instance,
display attributes include
font and
background; attributes of the hypothetical
structure category could include
chapter and
revision_date.
value is the particular value of an attribute for a chunk of text.
For instance, the following tags make sense:
display:font:lucidasans-12
display:bgstipple:gray25
display:background:blue
command:click:go_back
command:click:hide_text
structure:chapter:13
structure:author:js
A section of application code can look at the
category part of a tag to see whether it cares about a particular tag;
for instance, code handling bindings might care about
command tags but not about
display tags, while code that handles indexing might look at
structure tags.
Within each
category, a stretch of text, or the
current tag list (described below) can have only one tag with the same
attribute; for instance, a particular stretch of text can be tagged both
display:font:fixed and
display:background:blue, but it can't be tagged both
display:font:fixed and
display:font:12x24; likewise it can be tagged both
structure:chapter:13 and
structure:revision_date:1994.09.02, but not both
structure:chapter:13 and
structure:chapter:2. The
jtexttags.tcl procedures take care of replacing conflicting tags, but allowing
compatible tags to coexist.
The
display category is handled specially: the
jtexttags.tcl library automatically translates
display tags into tag configuration options; it uses
attribute as the text tag option to configure and
value as the value of that option. For instance, when you start
using the tag
display:font:variable, the
display:font:variable tag is automatically configured as with the statement
textwidget tag configure display:font:variable \
-font variable
(In practise, you may prefer to handle multiple fonts with the
jrichtext.tcl library, which uses tags like
richtext:font:roman and
richtext:font:italic; this lets you choose fonts dynamically, perhaps based on user
preferences. However, the
display tags are very handy for other display attributes, and are appropriate
for fonts in applications such as word processors where the specific
font displayed is a property of the document itself rather than
a user preference.)
You can also do a consistency check with
j:tag:configure_display_tags, forcing all
display tags in a text widget to be properly configured. This is
useful, for instance, after reading text and tags from a file,
or manipulating values in a
display tag with your own code.
The
jtexttags.tcl library does not by itself guarantee that all text in a text
widget has some kind of tag, so you can't assume, for instance,
that all text in a widget is tagged
display:font:something unless you take steps to make sure that is the case.
jtexttags.tcl keeps the display tags (only) that it manipulates at the bottom
of the tag stacking order, so the visual attributes of other tags
(e.g.
sel) will override the attributes set with the library.
Many of the procedures in this library use and maintain a
current tag list for each text widget they're used with. The
j:tag:insert_string procedure inserts text into a text widget with all the tags
in the current tag list (and no others). Typically, if you're
working with tagged text, you'll want
all text inserted into a text widget to be inserted by
j:tag:insert_string (or by
j:text:insert_string, which is smart enough to call
j:tag:insert_string when appropriate), so that the current tag list is applied
to newly inserted text.
This library allows you to associate arbitrary metainformation
with a text widget. This can be anything: the author's name,
preferred paper stock, general comments, etc. This metainformation
is in the form of a list of key-value pairs; metainformation
is created, stored, and retrieved by key. It is not an error
to try to retrieve the value of a nonexistent key; the empty
string is returned.
(The procedures in
jtagconvert.tcl may make use of some of this information if available; for
instance, when converting to HTML, the
body_background attribute, if it exists, is used to specify a background image.)
Some of the procedures in this library work with the nontextual
material stored in a text widget: tags and marks. Collectively,
I call this information the tag widget's
annotation.
The annotation of a text is stored as a list with two sublists.
The first sublist ([lindex
annotation 0]) represents the tags, and the second represents the marks.
The list of tags consists of a further twoelement sublist for
each active tag in the widget. The first element of each tag
sublist is the tag name, and the second element is a list containing
the beginning and ending indices of each range. The list of
marks consists of additional twoelement sublists for each mark;
the first element of each sublist is the mark name, and the second
is its index.
Here's an example annotation, reformatted for clarity:
{
{
{
richtext:font:italic {1.10 1.14}
} {
sel {1.0 2.0}
}
} {
{insert 1.22}
{current 1.0}
}
}
In this example, the 11th through 14th characters of the first
line are tagged
richtext:font:italic, the entire first line is selected, the insertion point is before
the 23d character on the first line, and the mouse (which controls
the
current tag) is over the first character on the first line.
Although the most recent version of Tk supports embedding windows
in text widgets (this is another form of annotation), this library
doesn't support text widgets with embdded windows. If you try
to use an annotation returned by
j:tag:get_annotation for a text widget containing embedded windows, you'll find
that the indices of tags and marks are likely to be incorrect.
The
j:tag:archive_text_widget and
j:tag:restore_text_widget procedures work with a file format for storing tagged text.
This format is simply a Tcl list whose first element is the
textual content of the widget, whose second element is the
annotation (as described above under
Text Annotation), and whose third element is the
metainformation (as described above under
Metainformation).
For instance, the annotation described above is from a text widget
archived as the following (slightly reformatted):
{This is a very simple test.
}
{{{richtext:font:italic {1.10 1.14}} {sel {1.0 2.0}}}
{{insert 1.22} {current 1.0}}}
The word `very' is tagged italic, and the insertion point is before
the word `test'.
Procedures
j:tag:insert_string - insert tagged text into text widget at insert point
j:tag:move - move to a particular position, setting current tag list appropriately
j:tag:set_tags - set tags to use when inserting new text
j:tag:clear_tags - clear tag list for inserting new text
j:tag:set_tag - add a tag to current tag list, overriding any conflicting
tag
j:tag:tag_text - apply tag to text, overriding any conflicting tag
j:tag:untag_text - remove tags matching pattern from text range
j:tag:configure_display_tags - configure display tags to display properly
j:tag:configure_fonts - configure standard fonts per user's preferences
j:tag:get_annotation - return non-text content (tags and marks) of text widget
j:tag:set_annotation - set non-text content (tags and marks) of a text widget
j:tag:get_metainfo - get metainformation for a widget, as list or a single value
j:tag:set_metainfo - set the value for a particular metainformation key
j:tag:delete_metainfo - delete a particular metainformation key and its value
j:tag:archive_text_widget - save text widget to file, including state
j:tag:restore_text_widget - read text widget from file, including state
See Also
jtext.tcl
jrichtext.tcl, especially
j:rt:textfonts
jfileio.tcl
Usage
j:tag:insert_string
w
text
Arguments
w is the text widget to insert into
text is the text to insert
Example
see
j:tag:set_tags
Description
This procedure inserts
text into
w at
w's insertion point, tagged with all the tags in the current tag
list, and no others.
(This procedure is used by
j:text:insert in
jtext.tcl if there's a tag list defined.)
Usage
j:tag:move
w
index
Arguments
w is the text widget to move in
index is the new position to move to
Description
This procedure sets
w's insertion point to
index, and sets the current tag list (applied to text inserted at the
new position) based on the current tags of the characters next
to
index. Any tags shared by the character at
index (after the insertion point) and the character just before
index will be added to the current tag list (with a couple of specialpurpose
exceptions). Font and display tags (tags whose names begin
with
richtext:) are inherited from the character before
index (unless
index is the beginning of the text widget, in which case they are
inherited from the character at
index).
The upshot of this is that if you have an blue italicised word
and you click at the end of it (after the last italicised character)
and type, you'll be typing in blue italics. If you click at
the beginning you won't.
If the word has some other kind of tag (say a hypertext link),
that tag will be applied to newlytyped text if you click in the
middle of the word (between two characters that both have it), but
not if you click at either end.
Usage
j:tag:set_tags
w
tag...
Arguments
t is the text widget whose tag list you're setting
tag... is one or more tags to set the tag list to
Example
text .t
pack .t
j:tag:set_tags .t display:foreground:red display:font:12x24
j:tag:insert_string .t "This is large red text.\n"
j:tag:set_tags .t display:background:yellow
j:tag:insert_string .t \
"This is plain text on a yellow background.\n"
j:tag:configure_display_tags .t
Description
This procedure sets the current tag list for text widget
t to to a list containing
tag... . Any text later inserted into
t with
j:tag:insert_string will be tagged with the
tags specified (and only those tags). (The
tag arguments should be in the
category:attribute:value format described above under
Using Tags.)
It's more usual (at least in my code :-) to use
j:tag:set_tag (with no
s on the end) to work with the tag list. That lets you set
or change a particular attribute without having to keep track
of everything that's in the tag list.
Any display tags in
tag... will be configured properly in the same fashion as by
j:tag:configure_display_tags.
Usage
j:tag:clear_tags
t
Arguments
t is the text widget whose tag list you're clearing
Description
This procedure clears the tag list completely for
t. This is different from setting it to an empty list, which
you can do by calling `j:tag:set_tags
t' with no
tag arguments. If you're using
jtext.tcl to insert text, setting the tag list to an empty list means
that newly inserted text won't have any tags at all, whereas clearing
the tag list will restore the normal Tk behaviour in which newly
inserted text inherits tags from the character to its left (under
Tk 3).
Usage
j:tag:set_tag
t
tag
Arguments
t is the text widget whose tag list you're setting
tag is a tag to add to the current tag list
Description
This procedure adds
tag to the current tag list for text widget
t, replacing any incompatible tag, i.e., a tag with the same
category and
attribute, but leaving any other tags in the tag list. (The
tag argument should be in the
category:attribute:value format described above under
Using Tags.)
New text later inserted with
j:tag:insert_string will be tagged with
tag, as well as any other (compatible) tags in the tag list.
If
tag is a display tag, it will be configured properly in the same
fashion as by
j:tag:configure_display_tags.
Usage
j:tag:clear_tag
t
pattern
Arguments
t is the text widget whose tag list you want to affect
pattern is a pattern selecting tags you want to remove from the tag
list
Description
This procedure removes any tags matching
pattern from the tag list, so new text inserted with
j:tag:insert_string won't receive them.
pattern is a matching pattern in the format used by Tcl's
string match command, such as `display:background:*' or `*:link:*' (or, of course, simply `sel' or `richtext:font:bold').
Typically,
pattern will only match one tag in the tag list; it's a pattern rather
than a simple string so that code using
j:tag:clear_tag doesn't need to know exactly what tags are in the current tag
list if it just wants to stop applying, for instance, a
display:background:... tag. However, you can match more than one tag; saying
`j:tag:clear_tag .text *' will remove all tags from
.text's tag list.
Usage
j:tag:tag_text
t
tag
first
last
Arguments
t is the text widget where you want to tag text
tag is a tag to be added to the text range
first is the index of the first character to be tagged
last is the index
after the last character to be tagged
Description
This procedure applies
tag to the range of text from
from to
to in text widget
t, removing any incompatible tag, i.e., a tag with the same
category and
attribute as
tag, but leaving any other tags that may be applied to the text.
(The
tag argument should be in the
category:attribute:value format described above under
Using Tags.)
This procedure does not affect the current tag list, so it won't
affect text later inserted with
j:tag:insert_string.
Usage
j:tag:untag_text
t
pattern
first
last
Arguments
t is the text widget with the text you want to untag
pattern is a pattern selecting tags you want to remove from the text
first is the index of the first character to be untagged
last is the index
after the last character to be untagged
Description
This procedure is used to selectively remove tags from a range
of text. It removes any tags that match
pattern from the range of text in
t from
first (inclusive) to
last (exclusive). See
j:text:clear_tag for details about
pattern.
You can remove all tags from a range of text by specifying `*' for
pattern.
Usage
j:tag:configure_display_tags
t
Argument
t is the text widget whose display tags you want to configure
Example
see
j:tag:set_tags
See Also
j:tag:configure_fonts
j:rt:textfonts in
jrichtext.tcl
Description
For all the current display tags in
t (i.e., tags of the form
display:attribute:value), this procedure configures the tag so it's appearance matches
the tag name. The actual command executed for each display
tag is
catch {
t tag configure display:attribute:value -attribute
value
}
t tag lower display:attribute:value
Display tags are maintained properly by the other procedures in
this library, so you should only need to call this procedure explicitly
after manipulating tags with explicit widget commands.
Usage
j:tag:configure_fonts
t
Argument
t is the text widget whose font tags you want to configure
Example
see
j:tag:set_tags
See Also
j:tag:configure_display_tags
j:rt:textfonts in
jrichtext.tcl
Description
This procedure sets richtext font tags for text widget
t according to the user's font preferences (as set on the
jstools Global Preferences panel). It should be called whenever
you will be displaying rich text in a text widget.
The correspondence between styles (some of which are HTMLstyle
logical styles) and font preferences is as follows:
{roman screen_roman_font}
{italic screen_italic_font}
{bold screen_bold_font}
{bolditalic screen_bolditalic_font}
{typewriter screen_monospace_font}
{heading0 screen_heading0_font}
{heading1 screen_heading1_font}
{heading2 screen_heading2_font}
{heading3 screen_heading3_font}
{heading4 screen_heading4_font}
{heading5 screen_heading5_font}
{l_em screen_italic_font}
{l_cite screen_italic_font}
{l_var screen_italic_font}
{l_dfn screen_italic_font}
{l_strong screen_bold_font}
{l_kbd screen_bold_font}
{l_code screen_monospace_font}
{l_samp screen_monospace_font}
where the actual tag is
richtext:font:style and the font is taken from the value of
$J_PREFS(preference).
Usage
j:tag:get_annotation
t
Arguments
t is the text widget whose state you need
Description
This procedure returns information about the current annotation
of text widget
t, describing all the active tags and marks in it, in the format
described above under
Text Annotation. It's used by
j:tag:archive_text_widget, and you can also use it to squirrel away the state of a text
widget temporarily.
Usage
j:tag:set_annotation
t
annotation
Arguments
t is the text widget whose state you want to change
annotation contains the tags and marks to apply to
t
Description
This procedure restores the tags and marks in
annotation to the text in text widget
t. All tags and marks in
annotation will be restored to their original locations. It then calls
j:tag:configure_display_tags to make sure display tags appear correct, and makes sure the
new insertion point is visible.
Since the locations of tags and marks are stored as absolute indices,
it probably won't be useful to call
j:tag:set_annotation when the text in a text widget is different from what it was
when the annotation was stored.
See
Text Annotation for a description of the format of
annotation.
Usage
j:tag:get_metainfo
t
[key]
[options]
Arguments
t is the text widget the metainformation is associated with
key, if given, is the particular key whose value you want
Options
-default
default
(default
{})
Examples
set status [j:tag:get_metainfo $w.t status]
if [string match Urgent $status] {
$w.msg configure -text Urgent -foreground red
} else {
$w.msg configure -text $status -foreground black
}
# following will not change the title if it exists,
# but will set it to Untitled otherwise:
j:tag:set_metainfo $t title \
[j:tag:get_metainfo $t title -default Untitled]
Description
If
key is given, this procedure will return the value corresponding
to
key in the metainformation associated with text widget
t. If
key does not occur in
t's metainformation (i.e. it hasn't been given a value yet), it
returns
default if specified, or the empty string.
If
key is not given, the procedure returns a list containing all the
currentlydefined metainformation for
t; this list contains twoelement sublists, where the first element
of each sublist is a key, and the second element is the corresponding
value.
Usage
j:tag:set_metainfo
t
key
value
Arguments
t is the text widget the metainformation is to be associated
with
key is the particular key whose value you're setting
value is the value for that key
Description
This procedure sets the value of a piece of metainformation associated
with text widget
t. The element indexed by
key is set to
value.
Usage
j:tag:delete_metainfo
t
key
Arguments
t is the text widget the metainformation is associated with
key is the particular key whose value you're deleting
Description
This procedure removes the piece of text widget
t's metainformation indexed by
key. Both the key and the value are deleted.
Usage
j:tag:archive_text_widget
t
filename
Arguments
t is the text widget to write
filename is the name of the file to write it to
Description
This procedure saves the contents of text widget
t, including all active tags and marks, into
filename. (It will generate an error if
filename can't be written.) It doesn't save information about the
current configuration of tags in the widget, but it does save
their names and positions.
The file format saved by
j:tag:archive_text_widget is a twoelement Tcl list whose first element is the textual
contents of the widget and whose second element is the annotation
(tags and marks) of the widget, in the form returned by
j:tag:get_annotation.
A text widget saved by this procedure can be restored with
j:tag:restore_text_widget, although tag configurations will have to be set explicitly.
Limitations Under Tk 4
Under Tk 4.0 or greater, this procedure should
not be used on text widgets with embedded windows, because (1)
information about the windows won't be saved, and (2) because
the windows take up a character position, but no character corresponds
to them, they will cause the positioning of tags to be inaccurate
when the text widget's contents are restored.
Usage
j:tag:restore_text_widget
t
filename
Arguments
t is the text widget to read
filename is the name of the file to read it from
Description
This procedure reads the contents of a text widget saved with
j:tag:archive_text_widget from file
filename into text widget
t. Text annotation, including marks and tags, is restored along
with the textual contents.
Tag configurations (the appearance and command bindings of tags)
is only restored for display tags (i.e., tags of the format
display:option:value). If you want other tags to be distinguished visually, you
need to configure them yourself. In particular, if you've used
the
jrichtext.tcl library to create multifont text, you'll need to use its
j:rt:textfonts procedure to configure the font tags properly.
Note that the
sel tag, which corresponds to the selection, and the
insert mark, which corresponds to the insertion point, are restored
by this procedure. If you don't want the restored text to have
the selection and the insertion point where they were when the
widget was archived, you should explicitly clear the selection
and set the insertion point after restoring the widget.
Bugs and Misfeatures
- It's unfortunate that the annotation format uses absolute rather
than relative positions; that makes a lot of things harder.
Future Directions
- For a few esoteric purposes, it would be useful to have an
`invisible' tag - text whose foreground was guaranteed to be the
same as its background. Even more useful would be a tag to
completely hide text, so it doesn't take up any space, either.
That can't be done in current versions of Tk, but for some
purposes making the text invisible and setting its font to
nil2 (in which all characters are only one pixel wide and two pixels
tall) would be good enough. This could be useful, for instance,
for folding outlines.