# jtexttags.tcl

## Introduction

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 2001.02.10.

## Usage

### 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.

## Credits and Copyright

### Author

Jay Sekora
`js@aq.org`
`http://www.aq.org/~js/`

### Copyright

The library is copyright © 1992-2001 by Jay Sekora, but may be freely redistributed under the conditions at the top of the file.

## Using Tags

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 `::jstools::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.

## The Current Tag List

Many of the procedures in this library use and maintain a current tag list for each text widget they're used with. The `::jstools::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 `::jstools::tag::insert_string` (or by `j:text:insert_string`, which is smart enough to call `::jstools::tag::insert_string` when appropriate), so that the current tag list is applied to newly inserted text.

## Metainformation

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.)

## Text Annotation

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 `::jstools::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.

## File Format

The `::jstools::tag::archive_text_widget` and `::jstools::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'.

## Overview

### Procedures

`::jstools::tag::insert_string` - insert tagged text into text widget at insert point
`::jstools::tag::move` - move to a particular position, setting current tag list appropriately
`::jstools::tag::set_tags` - set tags to use when inserting new text
`::jstools::tag::clear_tags` - clear tag list for inserting new text
`::jstools::tag::set_tag` - add a tag to current tag list, overriding any conflicting tag
`::jstools::tag::tag_text` - apply tag to text, overriding any conflicting tag
`::jstools::tag::untag_text` - remove tags matching pattern from text range
`::jstools::tag::configure_display_tags` - configure display tags to display properly
`::jstools::tag::configure_fonts` - configure standard fonts per user's preferences
`::jstools::tag::get_annotation` - return non-text content (tags and marks) of text widget
`::jstools::tag::set_annotation` - set non-text content (tags and marks) of a text widget
`::jstools::tag::get_metainfo` - get metainformation for a widget, as list or a single value
`::jstools::tag::set_metainfo` - set the value for a particular metainformation key
`::jstools::tag::delete_metainfo` - delete a particular metainformation key and its value
`::jstools::tag::archive_text_widget` - save text widget to file, including state
`::jstools::tag::restore_text_widget` - read text widget from file, including state

### See Also

jtext.tcl
jrichtext.tcl, especially `j:rt:textfonts`
jfileio.tcl

## ::jstools::tag::insert_string

### Usage

`::jstools::tag::insert_string` w`` text

### Arguments

w is the text widget to insert into
text is the text to insert

### Example

see `::jstools::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.)

## ::jstools::tag::move

### Usage

`::jstools::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.

## ::jstools::tag::set_tags

### Usage

`::jstools::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
::jstools::tag::set_tags .t display:foreground:red display:font:12x24
::jstools::tag::insert_string .t "This is large red text.\n"
::jstools::tag::set_tags .t display:background:yellow
::jstools::tag::insert_string .t \
"This is plain text on a yellow background.\n"
::jstools::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 `::jstools::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 `::jstools::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 `::jstools::tag::configure_display_tags`.

## ::jstools::tag::clear_tags

### Usage

`::jstools::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 ``::jstools::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).

## ::jstools::tag::set_tag

### Usage

`::jstools::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 `::jstools::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 `::jstools::tag::configure_display_tags`.

## ::jstools::tag::clear_tag

### Usage

`::jstools::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 `::jstools::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 `::jstools::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 ``::jstools::tag::clear_tag .text *`' will remove all tags from `.text`'s tag list.

## ::jstools::tag::tag_text

### Usage

`::jstools::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 `::jstools::tag::insert_string`.

## ::jstools::tag::untag_text

### Usage

`::jstools::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 `::jstools::tag::clear_tag` for details about pattern.

You can remove all tags from a range of text by specifying ``*`' for pattern.

## ::jstools::tag::configure_display_tags

### Usage

`::jstools::tag::configure_display_tags` t

### Argument

t is the text widget whose display tags you want to configure

### Example

see `::jstools::tag::set_tags`

### See Also

`::jstools::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.

## ::jstools::tag::configure_fonts

### Usage

`::jstools::tag::configure_fonts` t

### Argument

t is the text widget whose font tags you want to configure

### Example

see `::jstools::tag::set_tags`

### See Also

`::jstools::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`).`

## ::jstools::tag::get_annotation

### Usage

`::jstools::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 `::jstools::tag::archive_text_widget`, and you can also use it to squirrel away the state of a text widget temporarily.

## ::jstools::tag::set_annotation

### Usage

`::jstools::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 `::jstools::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 `::jstools::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.

## ::jstools::tag::get_metainfo

### Usage

`::jstools::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 [::jstools::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:
::jstools::tag::set_metainfo $t title \
[::jstools::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.

## ::jstools::tag::set_metainfo

### Usage

`::jstools::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.

## ::jstools::tag::delete_metainfo

### Usage

`::jstools::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.

## ::jstools::tag::archive_text_widget

### Usage

`::jstools::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 `::jstools::tag::archive_text_widget` is a threeelement Tcl list whose first element is the textual contents of the widget, whose second element is the annotation (tags and marks) of the widget, in the form returned by `::jstools::tag::get_annotation`, and whose third element is the `metainformation' associated with the text widget by procedures like `::jstools::tag::set_metainfo`, represented as a list of two-element sublists.

A text widget saved by this procedure can be restored with `::jstools::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 or images, because (1) information about the windows and images won't be saved, and (2) because they 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.

## ::jstools::tag::restore_text_widget

### Usage

`::jstools::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 `::jstools::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.

`
`