=head1 NAME

textblocks - additional text widget subcommand for Tk distribution.

=head1 SYNOPSIS

  .widgetName block subcommand ?opt opt ...?
  .widgetName configure -getFlags integer

=head2 What else is in the widget?

The other difference with the stock Text widget is the object oriented
dump. It dumps a list of objects which represent the contents of the
widget in the given range. The temporary position for this command is

   .widget block list from to

An object is a real object in Perl, and a list with a first element
being the I<class id> in TCL (the rest being the contests, in what
follows the first element of the rest is referenced as the first
element of the object).

The particular elements of the list represent:

=over 7

=item String

An object with contents being the string. Object class is
C<Tk::Text::String>, I<class id> is C<S>.

=item Marks

The contents is the name of the mark, the classes are
C<Tk::Text::MarkLeft> with I<id> C<L>, and C<Tk::Text::MarkRight> with
I<id> C<R>.

=item Tags

The contents is the name of the tag, the classes are
C<Tk::Text::TagOn> with I<id> C<T>, and C<Tk::Text::TagOff> with
I<id> C<t>.

=item Window

The contents is a list of the name of the window and the script to
create the window, the class is C<Tk::Text::EmbWin> with I<id> C<W>.

=item block

A block is dumped as a list of 

=over 10

=item type

The first element is a list of I<block name>, I<depth> and (optionally)
I<instance data>.

=item contents

The rest of the list represents the contents of the block in the same
form as the top-level dump, with exception for inter-leaf separators.

The inter-leaf separators are dumped as on object of class
C<Tk::Text::BlockSeparator>, with I<id> C<|>, the contents being the
"depth" of the separator. Note that the minimal possible depth is 1
for separators between leaves of the same parent.

=back

The object class in Perl is C<Tk::Text::Block>, the I<class id> is
C<B>.

=item Empty

An empty block is dumped in the same way with the object class being
C<Tk::Text::Empty>, the I<class id> being C<E>. The contenst is the
list of the block name, and (optionally) I<instance data>.

=back

=head2 What is a block?

A Block is an additional editable unit of information for inclusion
into widget. Block consists of I<block type>, I<block contents>, and,
possibly, I<block instance data>. I<Block contents> is a tree whose
leaves can contain anything that can be in a row in a text widget
(including other blocks). I<Block instance data> can be arbitrary data
as in a language variable.

I<Current implementation allows only trees with the distance from root
to leaves being the same for all the leaves.>

=head2 Examples of blocks

Here we provide several examples on the internal structure of blocks
that can implement the following objects. To see how the internal
structure (does not) correlate with the visual appearance, see
L<Display of blocks>.

=over 12

=item Button

    ___ Text to show when released
   /
   \___ Text to show when pressed

=item Multilevel sub-super-script

     __ First superscript
    /
   /\__	Second superscript
  /
  \    __ First subscript
   \  /
    \/___ Second subscript
     \
      \__ Third subscript

=item Radical

  ___ Text to show inside

=item Tabulation

  (Empty)

=back

=head2 Indices in blocks

A block occupies the following place in the index space: 1 index unit
for the start of the block, then all the leaves separated by 1 index
separators, and 1 index for the end of the block. Thus a block with
one empty leaf occupies 2 index units.

An I<empty> block (without leaves) occupies 1 unit of the index space.

Note that a block (however complicated) always fits in one line of the
index space. In particular, the usual bindings for C<up> and C<down>
will move you out of a block. (You should expect it, since leaves of a
block I<may> be placed one under another, but may be not.)

=head2 String representation of blocks

The commands that convert block contents to string (C<.t get>,
selection and search) see the contents of a block as a string of the
same size as the size of block in the index space. The start of the
block looks like C<{>, end like C<}>, interleaf separators like C<|>.

An I<empty> block looks like C<.>.

Note that a string representation of a block (however complicated)
always fits in one line.

=head2 Insertion in blocks

Insertion changes contents of the block if the insertion point is
inside the block, i.e.,
after starting index of the block and before the ending position (and
not inside some other block contained in the given one). Note that it
is impossible to be inside an empty block.

If inserted text contains no newlines, it is added to the leaf of tree
the insertion point is in (i.e., at or after the start of, and before
the end of). If it contains newlines, then the resulting leaf is split
into several at the inserted newlines. All the new leaves have the
same parent node as the initial leaf. Registered callback will be
called on insertion of newlines I<(not implemented)>.

=head2 Destruction of blocks

When the block is destructed, it contents is merged into the
surrounding environment. Block's head and tail disappear, interleaf
separators behave like newlines. I.e., if block was contained in
another block, interleaf separators of deceased block became interleaf
separators of the parent block, otherwise they became line separators
in the text widget.

=head2 Deletion of text in blocks

=over 5

=item *

If the deletion range does not include any index unit occupied by the
block, the block does not change.

=item *

If a range contains a block as a whole, block is deleted. Registered
callback will be called I<(not implemented)>. Same happens if the
range contains all the block except either head or tail index.

=item *

Otherwise, if the range includes head or tail of the block, the block is
destroyed, and what remains of the contents is merged into enclosing
structure as is described in L<Destruction of blocks>.

=item *

If a range is completely contained inside some leaf, the inner
structure of the block does not change (but if a leaf becomes empty,
registered callback will be called I<(not implemented)>).

=item *

If the range contains leaves separators, two survived leaves are
merged together. If their parents were different, they are also merged
together, and the process continues until the common parent is
met. This is exact opposite to the C<block split ??> subcommand that
would create this separator. Registered callbacks are called I<(not
implemented)>.

=back

=head2 Tags and blocks

Characters inside leaves can be tagged in the usual way. Interleaf
separators and tail of the block behave like end-of-lines when
tagged. The behaviour of tags on the start of block is undefined.

=head2 Bounding boxes

=over 5

=item *

If index in the text corresponds to a character in a leaf of a block,
it behaves as usual.

=item *

If the index corresponds to the interleaf separator or tail of a
block, the bounding box of the preceeding leaf is returned.

=item *

If the index corresponds to the head of the block, the bounding box
for the whole block is returned.

=back

Bounding box for anything I<invisible> is of size 0 and at the
upper-left corner of the block.

=head1 Display of blocks

The display of blocks is completely customizable. It is based on the
current configuration of the I<type> of displayed block.

=over 5

=item *

If no customization is registered for the block, then the tree
structure is ignored, all the leaves are drawn on the display one
under another, left justified, with wrap enabled, and the resulting
object is vertically centered about the middle of the ascent of the
default font. In what follows we refer to this behavior as I<default layout>.

=item *

If the block registers a layout callback, it will be called when the
block is about to be drawn. This procedure takes the information on
the size the leaves take on the display, and returns information on
what size the block takes on the display and what should be relative
offsets of leaves in the resulting rectangle. I<Details are subject to
change.> No clipping is performed.

=item *

Layout callback can also specify which leaves I<should not> be shown
at all, and what additional details to show in the rectangle.

=item * I<(Not implemented)>

Block can also specify that some leaves have some additional tags.

=back

=head1 Layout callback

Layout callback is the main component of the block.

I<Details are subject to change.>

A layout procedure takes a variable number of I<input arguments>. The
I<first element>
is a list containing the block name and widget name, (possibly with an
addition, say, if I<instance data> is present in the block, it will be
the next element). The I<second element> gives the horizontal offset of the start
of the block inside the ambient line (it can be different from the
absolute horizontal position if the block is inside some other
block). The I<remaining elements> specify the tree structure of the block and the
layout details of the leaves. Any child of the root node of the tree
provides one argument of the function. All the arguments are lists,
and

=over 5

=item *

if the child is a leaf, then the list consists of the I<repeat count>
(necessarily 1), current C<y> offset inside the block, width, I<total
width>, height, baseline, and some additional data on the leaf I<(last
part is not implemented, but it will contain horizontal stretchability
of the leaf, vertical one and stretchability of the baseline)>.

=item *

If the child is a node, the list elements are themselves lists that
are obtained using the same rules recursively: if children are leaves,
the elements are lists of numbers, otherwise lists of lists.

=item *

If block is configured to lie to the layout procedure about its contents, it
can present several lines or a whole subtree of itself in the same
form as a single leaf. In this case the I<repeat count> above can be
bigger than 1.

=back

B<Summary:>

=over 1


=item Input arguments:

  {bname wname ...} ho {rc yo w tw h b hs vs bs} ...

=item Abbreviations:

BlockNAME, WidgetNAME, Horizontal Offset, Repeat Count, Y Offset,
Width, Total Width, Baseline. hs, vs, bs are currently not used.

=back


The return value of the callback must be a list. The length of the
list (counting multiplicities, see below) must be at least the number of
leaves plus one. The first element of the list specifies the layout
information on the block as a whole, the others specify layout
information for the leaves and (possibly) additional elements to show.

There are three possible formats of the elements in this list. All
are lists of numbers (and should be of the form C<{dd ... dd}>)
of lengths 1, 7, or 13. The first number is either I<repeat count>, or
I<id> of additional line to show (i.e., the same data as returned by
C<textWidget block addline index> command). Lists of different length have
the following meaning:

=over 5

=item 1:

the child is not shown at all. This format is prohibited for the
layout information of the block as a whole and for additional
lines. The only element of the list is the I<repeat count>.

=item 7:

If the block as a whole is not laid out yet, this is the information
about it, and all the elements but what describe width,
height and baseline are ignored.  If not all the leaves are laid out
yet, the first element is the I<repeat count>, what means the number
of leaves that are described by this list (it can be 0).

The remaining elements of the list provide x and y offsets of
the upper-left corner inside the ambient block, width, I<total width>,
height, and baseline of the block as a whole, or of a group of leaves,
or a leaf. If it describes a group of leaves, the leaves are laid out
as by default layout procedure. Width, height, and baseline
information is used for displaying background of the line.

If all the leaves are laid out, then this list describes an additional
element to draw if the first element is greater than 0, i.e. it is
considered a I<Block ID> of an additional drawing element, and the
rest specifies the layout of this element. If it is 0, then this
element of the list is ignored exactly as in other cases.

=item 13:

I<(not implemented, but accepted by the widget):> the first 7 elements
have the same meaning as above, the last 6 provide stretchability
x and y position of upper-left corner, width, I<total width>, height,
and baseline.

=back

The I<total width> above is the width of the leaf including the space
reserved for showing the background of the leaf terminator.


B<Summary:>

=over 1

=item Return list elements:

  {Block Layout} {Leaf0 Layout} ...
        {Leafn Layout} {Additional Element0 Layout} ...
        {Additional Elementn Layout}

=item Layout Styles

=over 4

=item

Block Layout - C<{ig ig ig w ig h b}>

=item

Show one or more leaves - C<{rc xo yo w tw h b}>

=item

Hide one or more leaves - C<{rc}>

=item

Show Additional Elements - C<{bi xo yo w tw h b}>

(C<w> is not very important), put after leaves.

=back

=item Abbreviations:

IGnored, Width, Total Width, Height, Baseline, XOffset, YOffset,
Repeat Count, Block Id

=back

None of the returned date is used for clipping. However, it is used in
directing the mouse events and reporting bounding boxes.

=head2 Lying to layout callback

It is possible to configure a block to lie about its structure to the
layout callback. This can greatly simplify writing the callback in
interpreted languages, if the default layout procedure is satisfactory for
I<parts> of a block. 

Setting the C<-layoutdepth> and/or C<-layoutwidths> options for a block
forces the layout procedure to be called with agruments corresponding
to modified tree structure. 

If C<-layoutdepth> is set, the depth of the tree is modified to be the
given number. If the true depth of the tree is too small, tree is
extended from the root side, if it too deep, the subtrees on the
maximal allowed depth level are mocked as lines with I<repeat count>.

If C<-layoutwidths> is set, this can restrict the number of branches
going out of any node of the modified tree. The value of this option
should be a list, elements of this list are either numbers, or lists
of the form C<{min max}>. (A single C<number> is equivalent to a pair
C<{number number}>). N-th element of the list controls the number of
branches going out of nodes on the distance N from the root. If there
are too few branches, it adds branches that contain simulated groups
of leaves with I<repeat count> 0. If there are too many branches, it
groups several last brunches into one with appropriate I<repeat
count>. I<(Current implementation is buggy in interaction of
C<-layoutwidths> and C<-layoutdepth> if the depth of the node is less
than C<-layoutdepth>.)>


=head1 Block subcommands

=over 7

=item B<configure> I<blockName ?option value ... ?>

Standard procedure for creating and configuring block type. Currently
supported configuration options are

=over 14

=item B<-layoutcmd>

Layout callback.

=item B<-layoutdepth>

Depth of simulated tree for layout callback. Default -1.

=item B<-layoutwidths>

List of widths of levels of simulated tree for layout
callback. Default C<{}>.

=item B<-empty>

Boolean value that specifies that the blocks of this type contain no
leaves (I<empty> blocks).

=back

=item B<cget>

Standard configuration get procedure.

=item B<at> I<index>

Returns information in the block that starts at the given index or
"nothing" if there is no such block. Returned information is a list
consisting of block start index, block length in index space, block
name, block instance data, structure of the block. The latter is a
list (possibly of lists) with leaves being lengths of block leaves.

=item B<of> I<index>

Returns information in the innermost block that contains the given index or
empty list if there is no such block. See C<at> subcommand for
description of return value.


=item B<delete> I<blockName ?blockName ...?>

Deletes the information on I<block types>. Error is returned if the
are blocks of the given types in the widget.


=item B<insert> I<blockName index1 ?index2?>

Inserts block of the given type in the widget. The block contains one
node, and all the leaves are immediate children of this node. If
I<index2> is not specified, creates a block with one empty leaf,
otherwise moves the contents of the widget between I<index1> and
I<index2> into the tree.

In the latter case I<index1> and I<index2> should be outside of any
block, or properly inside the same block. The newlines (or,
correspondingly, interleaf separators of enclosing block) are
converted into interleaf separators of newly created block.  This is
prohibited for I<empty> blocks.

=item B<names>

Returns list of names of currently defined I<block types>.

=item B<split> I<index ?level?>

=over 5

=item *

If I<index> is outside of blocks, inserts newline.

=item *

Otherwise, if
I<level> is not specified, or is 0, is equivalent to insertion of
newline, i.e., splits the leaf into two with the same
parent.

=item *

If I<level> is positive, splits the leaf in two and changes the
structure of the tree.

=over 5

=item *

If I<level> is less than the distance from the leaf to the root,
splits the I<level>th parent (and all the parents of less order) in
two. The beginning of the split leaf goes to one node, the rest
(together with the leafs in the same node after the splitted one) goes
to another. Example of B<split 1> follows (the leaf marked B<X> is
split into two marked B<x>):

      ___		  ___
     / __       	 /  _
    / /         	/  /
 .-------X     	     .-------x
  \   \__	      \ \____x
   \_____	       \   \_
			\____

=item *

If I<level> is too big to proceed with the above procedure, the tree is
extended to the left first. The added nodes have one child each.

=back

=back


=item B<trim> I<index>

If the block at index has only one edge coming from the root, and this
edge does not end in a leaf, deletes the root. The resulting tree will
be 1 level more shallow. Returns error if out of block or the above
conditions are not met.

=item B<data> I<index ?data?>

Returns or sets I<instance data> information for the block at C<index>.

=item B<addline> I<index>

"Steals" the displayed structure of the line starting at
I<index>. Returns the I<id> of the resulting element. This I<id> can
be used in the return value of the layout callback to specify
additional elements used in drawing of the block. I<Beware that the
behaviour is undefined if the line contains something more complicated
than tagged characters.>

=item B<deletelines>

Deletes all the "stolen" lines.

=item B<list> I<from> I<to>

See L<What else is in the widget?>.

=back

=head1 Additional text widget options

=over 10

=item C<-getFlags>

The value is an integer, binary bits of this integer denote current
I<text representation> options. If no bits are set, the operations
that consider text contents as a string behave like the standard Tk
text widget.

If bit C<1> is set, the string representation of a text range
I<always> gives a string of the same length as the length of the
interval in the index space. If some text annotation results in an
empty string in the standard representation, it is represented as an
appropriate number of C<*>s if this bit is set. Currently this
concerns embedded windows only, which are represented as C<*>.

All undocumented bits are reserved and should be 0.

=back

=head1 Minitutorial

=head2 Example of a simple "pedestal" block

Suppose you are satisfied with the standard layout procedure, but want
to extend the resulting group of lines 10 points to the right (so when
part of block is selected, selection extends 10 points to the
right of the rightmost element in leaves), and want to add a blue
3D background of width 5 with "height" 2 around the resulting guy,
and draw the block on gray 3D background of width 2.

Note that it is not enough to use a tag even for gray background,
since the border of the tag is inside the rectangle that contains the
letter, and to get a correct pedestal look we want the border to
surround the letter, not to be drawn outside of them.

In what follows we use TCL as the language for callback. Of course,
the resulting code will be much simpler in some other languages.

First, we create the 2 new additional elements to display:

  .t tag configure backgr1 -background blue -border 2 -relief raised
  .t tag configure backgr2 -background gray90 -border 2 -relief raised
  .t insert 1.0 \n backgr1
  set backgrId1 [.t block addline 1.0]
  .t delete 1.0 1.0+1c
  .t insert 1.0 \n backgr2
  set backgrId2 [.t block addline 1.0]
  .t delete 1.0 1.0+1c

(or we could use existing ones, if possible). The third row creates a
row in the text widget that is empty, and has tag "backgr1". This row
will show the background only, and we will use it to show the raised
background behind the block. The fourth row memorizes the I<id> of this
additional line, and the fifth one returns the text in the widget to
its initial state. The line is not shown anymore, but the way to show
it is preserved in a safe place. The second element is created in the
same way.

Next, since we are satisfied with the way the leaves of the tree are
places on the screen (i.e., one under another), we do not want to be
concerned with internal structure of the block tree, so we ask the
widget to lie about internal structure of the tree:

  .t block config myBlock -layoutdepth 1 \
	-layoutwidths 1 -layoutcmd myLayoutCmd

Now the data given to C<myLayoutCmd> will be always the same, as if
the block consisted of one leaf only that is the direct child of the
root. So the arguments the C<myLayoutCmd> receives are the following
ones:

  proc myLayoutCmd {block x row} {
    global backgrId1 backgrId2
    set c [lindex $row 0]
    set w [lindex $row 2]
    set tw [lindex $row 3]
    set h [lindex $row 4]
    set b [lindex $row 5]

Now C<$c>, C<$w>, C<$tw>, C<$h>, C<$b> contain the count, width,
I<total width>, height and baseline of the contents of the block.
Next we extend the I<total width> of the row.

    set tw [expr $tw+10]
    set tw2 [expr $tw+4]
    set tw1 [expr $tw+10]

We will not replace the width of the row, so mouse events could be
directed to outside of the block if clicked in the extended area. (In
fact they won't, since we will extend the size of the ambient block.)
Next we calculated widths of additonal rows,

    set h2 [expr $h+4]
    set h1 [expr $h+10]
    set b2 [expr $b+2]
    set b1 [expr $b+5]

and their height and baselines. All is prepared now to layout the rows:

    set addrow1 [list $backgrId1 0 0 $tw1 $tw1 $h1 $b1]
    set addrow2 [list $backgrId2 3 3 $tw2 $tw2 $h2 $b2]

Note that the same list $addrow1 is good for specifying the size of the
total block, since the first element of the list (that is $backgrId)
is ignored in the information for the whole block. Now we need to
move the real contents of the block 5 points to the right and down
with  respect to the rectangle occupied by the
block, and return the calculated information:

    set row [list $c 5 5 $w $tw $h $b]
    return [list $addrow1 $row $addrow1 $addrow2]
  }

Note that it is vital to put addrow2 after addrow1, since it should be
drawn after the addrow1 for it to appear to be on top of addrow1.

That's all! Now you can create a binding for insertion of empty block
into the widget (this binding supposes that C<bindtags> are reverted):

  bind .text <Control-Meta-m> {
    %W block insert myBlock insert
    %W mark set index index-1c
    break
  }

This binding moves the insertion point inside the block, so you are
ready to fill block with whatever information you need.

Sample code in the distribution of extended text widget
provides other examples of blocks: superSub, Fraction, and so on.

=head2 Example of a simple "tabulation" block

Here we describe how to code blocks that contain no editable
information, but provide geometry management. Consider an example of
tabulation: if we implement tabulation as a block, the size of this
block should change depending on the position of the start, but the
only editing operation should be the deletion of the block as a whole.

While it is possible to implement this kind of block using the same
types of blocks as in the previous section, we will need a lot of code
change in the bindings if we use this approach: a usual block takes at
least 2 positions in the index space, so it is possible to make an
insertion inside this block. Thus we either need correction of
keybindings that move the insertion point, or should correct the the
insertion code to check whether the insertion happens inside a block
of this type. If we do not do this, we need additional decision how to
handle blocks of this type that have not-empty contents.

The solution is to use "empty" blocks that take only 1 position in the
index space, so it is impossible to insert text inside them. We make
block type empty by using configuration option C<-empty>:

  .t block configure Tab -empty on -layoutcmd {layoutTab 5 35}

Note the spaces in the name of layoutcmd: No quoting is performed
during the call, so C<5> and C<35> become first two arguments of
C<layoutTab> procedure. This procedure can be as simple as follows:

  proc layoutTab {min mult block x} {
    global backgrId2
    set w [expr $min + $mult - ($x + $min - 1) % $mult - 1]
    set totblock [list $backgrId2 0 0 $w $w 5 3]
    return [list $totblock $totblock]
  }

The only "working" row is the C<set w> one. It calculates the width
of the block using the following rule: 

=over 5

=item

the width is at least $min, and the right end is on the position that
is proportional to $mult counting from the left of the line.

=back

The last row returns a list of length 2. The first element is the
description of the block as a whole, the next one is necesssarily the
description of an additional element (since there are no "regular"
leaves in the tree). This additional element is the same size as the
block itself, and is used only to provide some visual feedback. We use
I<id> $backgrId2 created elsewhere (say, by the code in the previous
example), and ask for the rectangle to be 3 points above the baseline,
and 2 points below. 

=head1 AUTHOR

Ilya Zakharevich <ilya@math.ohio-state.edu>

=head1 AVAILABILITY

L<ftp://ftp.math.ohio-state.edu/pub/users/ilya>.
