Additional Command 'watch'

pathName watch ??-always? commandPrefix?

Monitors widget modifications, it causes a Tcl command to be executed whenever certain widget modifications are done. In general only user (GUI) modifications will be watched. But if option -always is specified then all modifications will be watched. In fact the triggering depends on the way how a certain modification will be executed:

  • If option -always is not specified then the delete command triggers only if the argument index1 (the starting point of the deletion) is specified with "insert" (the insertion cursor).

  • If option -always is not specified then the insert command triggers only if the argument index (the starting point of the insertion) is specified with "insert" (the insertion cursor).

  • If option -always is not specified then The mark set command triggers only if the argument markName (the name of the mark) is specified with "insert" (the insertion cursor).

  • If option -always is not specified then the replace command triggers only if the argument index1 (the starting point of the replacement) is specified with "insert" (the insertion cursor).

  • edit undo|redo will trigger the corresponding undo|redo event.

  • A resize or content change of a displayed image will trigger event image.

  • Any change of a displayed window (resize, map, or unmap) will trigger event window.

Additionally any of the following commands may trigger because the view may change:

  • clear|load
  • configure
  • edit redo|undo
  • image configure|create
  • mark gravity|set|unset
  • see
  • tag add|delete|lower|raise|remove
  • window configure|create
  • xview arg...
  • yview arg...

Also destroying an embedded window, or embedded image, may trigger due to a changed view.

Note that the delete, and the insert operation will be handled in the same way as the replace operation, this means that firstly the deletion part triggers, and secondly the insertion part will be triggered. In case of delete the insertion part is empty (zero characters), and in case of insert the deletion range is empty.

CommandPrefix will be resolved using the usual namespace resolution rules used by commands. If the command does not exist, an error will be thrown (except the command argument is empty). When a certain widget modification triggers, a number of arguments are appended to commandPrefix so that the actual command is as follows:


commandPrefix pathName op index1 index2 info userFlag

UserFlag informs whether this is a user (GUI) modification. Note that this flag is meaningful only if op is delete, or insert.

Op indicates which operation has been performed:

insert

Zero ore more characters have been inserted, this event will be triggered after the insertion has been realized. Index1 and index2 are specifying the range of the new characters after insertion, this range can be empty. Info will contain five items:

  1. The inserted characters.
  2. A list of tags applied to the character just before the inserted characters.
  3. A list of tags applied to the character just after the inserted characters.
  4. A list of tags applied to the newly inserted characters.
  5. A boolean flag indicating whether this is the final part of a multi-insert/replace.

It should be avoided to call commands inside this event which will insert or delete as long as final is false, the result can be unexpected. Such commands should be postponed, either with the after command, or until final state is true.

This event will also be triggered for delete, and replace operations, in these cases zero characters may be inserted, and index1 refers to the start position of the deletion.

delete

Zero or more characters will be deleted, this event will be triggered before the deletion has been realized. Index1 and index2 are specifying the range of the characters to be deleted, this range can be empty. Info will contain six items:

  1. The deleted characters.
  2. A list of tags applied to the character just before the deleted characters.
  3. A list of tags applied to the character just after the deleted characters.
  4. A list of tage applied to the first deleted character.
  5. A list of tage applied to the last deleted character.
  6. A boolean flag indicating whether this is the final part of a multi-delete/replace.

This event will also be triggered for insert, and replace operations, in these cases the deletion range may be empty, and index1 refers to the start position of the insertion.

It should be avoided to call commands inside this event which will insert or delete, the result can be unexpected. Such commands should be postponed, either with the after command, or until the corresponding insert event will be triggered.

cursor

The position of the insertion cursor has been moved. Index1 is the old character position, and can be empty, index2 is the new character position of the cursor. Here info contains a list of tags which would be applied if the user (GUI) is inserting a character at the new cursor position.

view

The view of the widget has changed (scrolling the widget, moving the cursor, the see command has been executed, or any other modifying command has been performed – see the list of modifiying commands above). Here index1 specifies the pixel position of the upper left corner in the form @x,y, and index2 specifies the pixel position of the lower right corner. Info does not have a meaning here and will be empty.

undo

An undo action has been performed. Info contains two elements. The first is the performed undo command, one of: delete, image, insert, mark, tag, or window. The second is a boolean flag indicating whether this is the final part of a multi-undo action.

It is not allowed to perform any textual modification, nor is it allowed to reset the undo/redo stack, an error will be thrown.

redo

A redo action has been performed. Info contains two elements. The first is the performed redo command, one of: delete, image, insert, mark, tag, or window. The second is a boolean flag indicating whether this is the final part of a multi-redo action.

It is not allowed to perform any textual modification, nor is it allowed to reset the undo/redo stack, an error will be thrown.

image

An image has been resized, or the content has changed. Info contains the name of the affected image as first element. In case of a change in size the width and height of the image before resizing will be the second and third element of info.

window

Either a window has changed the display state (mapped or unmapped), or the size of the window has changed. Info contains the name of the affected window as first element. And if the size of the window has changed, then info will provide the old width and height with a second and third element.

PathName is the name of the widget where the modification has been done. In case of insert/delete also the modifications in a different peer will be triggered for this widget, provided that the range of the modification is inside the displayed range. It is guaranteed that the widget, where the insert/delete has been done, will be triggered at first, but the order of triggering other peers is unspecified.

If commandPrefix is not specified, then the watch of modifications will be terminated. Otherwise it will replace any existing script, but if the first character is “+” then the new script augments an existing script.

This command returns the command prefix which has been set before this call, this can be empty if a command prefix was not set.

This is a big help for the implementation of editors, especially the support of syntax highlighting is important, and the watch command makes this support relatively easy. Furthermore it supports the implementation of many other features like line numbering (see stackoverflow). Watching the insertion cursor allows a clear distinction between user (GUI) actions and programmed actions, a proper developed editor is in general not using the "insert" mark for edit operations.

The events are designed in a way so that the replace operation does not need a special handling, and in case of a delete operation the user (GUI) will receive two states: the state before deletion, and the state after deletion (with the additional triggering of insert). The same applies to insert, the state before insertion is done will be received (with triggering delete), and the state after insertion will be received.

The implementation of the watch command does not require any change or extension of other functions, it is completely independent. Of course the triggering has been added at a few places.

A few changes in the Tcl implementation (library/text.tcl) were required, because any edit operation in this file has to trigger the watch command.

Below is an example for the usage of the watch command. This widget shows the line numbers, and is highlighting the cursor line.