# 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, and

* save and restore the complete contents of a text widget, including tags and marks.

This document describes jtexttags.tcl version 4.0/4.0.

## 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@calumet.org`

`http://shore.net/~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.

## 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`, `revision_date`, and maybe `online`. 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.

###

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

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

## File Format

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 and whose second element is the annotation, as described above under Text Annotation.

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

`j:tag:insert_string` - insert tagged text into text widget at insert point

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

## j:tag:insert_string

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

## j:tag:set_tags

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

## j:tag:clear_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).

## j:tag:set_tag

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

## j:tag:clear_tag

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

## j:tag:tag_text

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

## j:tag:untag_text

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

## j:tag:configure_display_tags

### Usage

`j:tag:configure_display_tags` t

### Arguments

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

### Example

see `j:tag:set_tags`

### See Also

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

## j:tag:get_annotation

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

## j:tag:set_annotation

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

## j:tag:archive_text_widget

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

## j:tag:restore_text_widget

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

``