717 lines
26 KiB
HTML
717 lines
26 KiB
HTML
<html>
|
|
<head>
|
|
<title>Wcb Programmer's Guide</title>
|
|
|
|
<meta name="Author" content="Csaba Nemethi">
|
|
<meta name="Keywords" content="callback, widget, Tk entry, tile entry,
|
|
BWidget Entry, spinbox, tile combobox, listbox, tablelist, text, ctext">
|
|
|
|
<style type="text/css">
|
|
<!--
|
|
pre {color: #006400}
|
|
-->
|
|
</style>
|
|
</head>
|
|
|
|
<body bgcolor=#ffffff>
|
|
<div align=center>
|
|
<h1>Wcb Programmer's Guide</h1>
|
|
|
|
<h3>by</h3>
|
|
|
|
<h2>Csaba Nemethi</h2>
|
|
|
|
<address>
|
|
<a href="mailto:csaba.nemethi@t-online.de">csaba.nemethi@t-online.de</a>
|
|
</address>
|
|
</div>
|
|
|
|
|
|
<p><hr>
|
|
|
|
|
|
<a name="contents"></a><h2>Contents</h2>
|
|
|
|
|
|
<h4><a href="#overview">Overview</a></h4>
|
|
|
|
<ul>
|
|
<li><a href="#ov_problems">Some common problems</a>
|
|
<li><a href="#ov_wcb">The Wcb package </a>
|
|
<li><a href="#ov_get">How to get it?</a>
|
|
<li><a href="#ov_install">How to install it?</a>
|
|
<li><a href="#ov_use">How to use it?</a>
|
|
</ul>
|
|
|
|
<h4><a href="#examples">Examples</a></h4>
|
|
|
|
<ul>
|
|
<li><a href="#ex_entry">Some before-<code>insert</code> callbacks for entry
|
|
widgets</a>
|
|
<li><a href="#ex_listbox1">A <code>selset</code> callback for a listbox</a>
|
|
<li><a href="#ex_listbox2">An <code>activate</code> callback for a listbox</a>
|
|
<li><a href="#ex_text">Seven callbacks for a text widget</a>
|
|
</ul>
|
|
|
|
<div align=center>
|
|
<p><a href="index.html">Start page</a>
|
|
</div>
|
|
|
|
|
|
<p><hr>
|
|
|
|
|
|
<h2><a name="overview"></a>Overview</h2>
|
|
|
|
|
|
<h3><a name="ov_problems"></a>Some common problems</h3>
|
|
|
|
<p>Many Tcl/Tk programmers are confronted with questions like the following:
|
|
|
|
<ul>
|
|
<li>How to restrict the set of characters that the user can type or paste
|
|
into a Tk entry, tile entry, BWidget Entry, spinbox, tile combobox, text
|
|
or ctext widget?
|
|
<li>How to manipulate the user input characters before they are inserted into
|
|
one of these widgets? In the case of a text or ctext widget:
|
|
How to change the font, colors, or other attributes of the input
|
|
characters?
|
|
<li>How to set a limit for the number of characters that can be typed or
|
|
pasted into a Tk entry, tile entry, BWidget Entry, spinbox, or tile
|
|
combobox widget?
|
|
<li>How to protect some parts of the text contained in a Tk entry, tile
|
|
entry, BWidget Entry, spinbox, tile combobox, text, or ctext widget from
|
|
being changed by the user?
|
|
<li>How to define notifications to be triggered automatically after text is
|
|
inserted into or deleted from one of these widgets?
|
|
<li>How to define some actions to be invoked automatically whenever the
|
|
insertion cursor in a Tk entry, tile entry, BWidget Entry, spinbox, tile
|
|
combobox, text, or ctext widget is moved?
|
|
<li>How to define a command to be called automatically when selecting a
|
|
listbox element, a tablelist row or cell, or a range of characters in a
|
|
text or ctext widget?
|
|
<li>How to protect any or all items of a listbox or a range of characters in
|
|
a text or ctext widget from being selected?
|
|
</ul>
|
|
|
|
<p>In most books, FAQs, newsgroup articles, and widget sets, you can find
|
|
<i>individual</i> solutions to some of the above problems by means of widget
|
|
bindings. This approach is based on adding new binding tags or modifying
|
|
some of the existing ones, which quite often proves to be incomplete.
|
|
|
|
<p>The Tk core addresses just a few of the above problems: In Tk 8.1 the
|
|
<code><<ListboxSelect>></code> virtual event for listbox widgets
|
|
was introduced, Tk versions 8.3 and higher support widget options for entry
|
|
validation, and the spinbox widget (introduced in Tk 8.4) provides the same
|
|
validation facility. Finally, Tk 8.4 supports the <code>disabled</code>
|
|
state for listbox widgets, as well as the <code>modified</code> flag, the
|
|
<code><<Modified>></code> and
|
|
<code><<Selection>></code> virtual events, and an undo/redo
|
|
mechanism for text widgets. However, also these improvements are of
|
|
<i>individual</i> nature.
|
|
|
|
|
|
<h3><a name="ov_wcb"></a>The Wcb package</h3>
|
|
|
|
<p>The <b>W</b>idget <b>c</b>all<b>b</b>ack package Wcb provides a completely
|
|
different, <i>general</i> solution to the above problems: Based on
|
|
redefining the Tcl command corresponding to a widget, the main Wcb procedure
|
|
<code><a href="wcbRef.html#callback">callback</a></code> enables you to
|
|
associate arbitrary commands with some Tk entry, tile entry, BWidget Entry,
|
|
spinbox, tile combobox, listbox, tablelist, text, and ctext widget
|
|
operations. These commands will be invoked automatically in the global
|
|
scope whenever the respective widget operation is executed. You can
|
|
request that these commands be called either before or after executing the
|
|
respective widget operation, i.e., you can define both <b>before-</b> and
|
|
<b>after-callbacks</b>. From within a before-callback, you can cancel the
|
|
respective widget command by invoking the procedure <code><a href=
|
|
"wcbRef.html#cancel">cancel</a></code>, or modify its arguments by calling
|
|
<code><a href="wcbRef.html#extend">extend</a></code> or <code><a href=
|
|
"wcbRef.html#replace">replace</a></code>.
|
|
|
|
<p>Besides these (and four other) general-purpose commands, the Wcb package
|
|
exports the utility procedures <code><a href=
|
|
"wcbRef.html#change">changeEntryText</a></code>, <code><a href=
|
|
"wcbRef.html#entrylen">postInsertEntryLen</a></code>, and <code><a href=
|
|
"wcbRef.html#entrytext">postInsertEntryText</a></code> for Tk entry, tile
|
|
entry, BWidget Entry, spinbox, and tile combobox widgets, as well as some
|
|
before-<code>insert</code> callbacks for <a href="wcbRef.html#entrycb">Tk
|
|
entry, tile entry, BWidget Entry, spinbox, tile combobox</a>, <a href=
|
|
"wcbRef.html#textcb">text, and ctext</a> widgets, which you can use directly
|
|
or modify to suit your needs. To learn how to do this, have a look at the
|
|
<a href="#examples">Examples</a> section below.
|
|
|
|
<p>The Wcb package is implemented in pure Tcl/Tk code, which makes it
|
|
completely platform-independent and very easy to install. It requires
|
|
version 8.0 or higher of both Tcl and Tk.
|
|
|
|
|
|
<h3><a name="ov_get"></a>How to get it?</h3>
|
|
|
|
<p>Wcb is available for free download from the Web page
|
|
|
|
<blockquote><address>
|
|
<a href="http://www.nemethi.de">http://www.nemethi.de</a>
|
|
</address></blockquote>
|
|
|
|
<p>The distribution file is <code>wcb3.1.tar.gz</code> for UNIX and
|
|
<code>wcb3_1.zip</code> for Windows. These files contain the same
|
|
information, except for the additional carriage return character preceding the
|
|
linefeed at the end of each line in the text files for Windows.
|
|
|
|
|
|
<h3><a name="ov_install"></a>How to install it?</h3>
|
|
|
|
<p>Install the package as a subdirectory of one of the directories given by the
|
|
<code>auto_path</code> variable. For example, you can install it as a
|
|
directory at the same level as the Tcl and Tk script libraries. The
|
|
locations of these library directories are given by the
|
|
<code>tcl_library</code> and <code>tk_library</code> variables, respectively.
|
|
|
|
<p>To install Wcb <i>on UNIX</i>, <code>cd</code> to the desired directory and
|
|
unpack the distribution file <code>wcb3.1.tar.gz</code>:
|
|
|
|
<blockquote><pre>
|
|
gunzip -c wcb3.1.tar.gz | tar -xf -
|
|
</pre></blockquote>
|
|
|
|
<p>This command will create a directory named <code>wcb3.1</code>, with the
|
|
subdirectories <code>demos</code>, <code>doc</code>, and <code>scripts</code>.
|
|
|
|
<p><i>On Windows</i>, use WinZip or some other program capable of unpacking the
|
|
distribution file <code>wcb3_1.zip</code> into the directory
|
|
<code>wcb3.1</code>, with the subdirectories <code>demos</code>,
|
|
<code>doc</code>, and <code>scripts</code>.
|
|
|
|
|
|
<h3><a name="ov_use"></a>How to use it?</h3>
|
|
|
|
<p>To be able to access the commands and variables defined in the package Wcb,
|
|
your scripts must contain one of the lines
|
|
|
|
<blockquote><pre>
|
|
package require Wcb
|
|
package require wcb
|
|
</pre></blockquote>
|
|
|
|
<p>You can use either one of the two statements above because the file
|
|
<code>wcb.tcl</code> contains both lines
|
|
|
|
<blockquote><pre>
|
|
package provide Wcb ...
|
|
package provide wcb ...
|
|
</pre></blockquote>
|
|
|
|
<p>You are free to remove one of these two lines from <code>wcb.tcl</code> if
|
|
you want to prevent the package from making itself known under two different
|
|
names. Of course, by doing so you restrict the argument of
|
|
<code>package require</code> to a single name. Notice that the
|
|
<a href="#examples">examples</a> below use the statement <code>package
|
|
require Wcb</code>.
|
|
|
|
<p>Since the package Wcb is implemented in its own namespace called
|
|
<code>wcb</code>, you must either invoke the
|
|
|
|
<blockquote><pre>
|
|
namespace import wcb::<i>pattern</i> ?wcb::<i>pattern ...</i>?
|
|
</pre></blockquote>
|
|
|
|
<p>command to import the <i>procedures</i> you need, or use qualified names
|
|
like <code>wcb::callback</code>. In the examples below we have chosen the
|
|
latter approach.
|
|
|
|
<p>To access Wcb <i>variables</i>, you <i>must</i> use qualified names.
|
|
There are only two Wcb variables that are designed to be accessed outside the
|
|
namespace <code>wcb</code>:
|
|
|
|
<ul>
|
|
<li>The variable <code>wcb::version</code> holds the current version number
|
|
of the Wcb package.
|
|
<li>The variable <code>wcb::library</code> holds the location of the Wcb
|
|
installation directory.
|
|
</ul>
|
|
|
|
<div align=center>
|
|
<p><a href="#contents">Contents</a>
|
|
<a href="index.html">Start page</a>
|
|
</div>
|
|
|
|
|
|
<p><hr>
|
|
|
|
|
|
<h2><a name="examples"></a>Examples</h2>
|
|
|
|
|
|
<h3><a name="ex_entry"></a>Some before-<code><font size=4>insert</font></code>
|
|
callbacks for entry widgets</h3>
|
|
|
|
<p>The script <code>entrytest.tcl</code> in the <code>demos</code> directory
|
|
creates three entry widgets with the constraints shown in the following figure:
|
|
|
|
<blockquote>
|
|
<p><img src="entrytest.gif" alt="entrytest - Please load this image!"
|
|
width=318 height=269>
|
|
</blockquote>
|
|
|
|
<p>For the topmost entry <code>.e1</code> we define two
|
|
before-<code>insert</code> callbacks contained in the Wcb package:
|
|
|
|
<blockquote><pre>
|
|
wcb::callback .e1 before insert wcb::checkStrForAlnum \
|
|
wcb::convStrToUpper
|
|
</pre></blockquote>
|
|
|
|
<p>To force the second entry <code>.e2</code> to accept only integers of
|
|
maximal length 10, we use again two before-<code>insert</code> callbacks from
|
|
Wcb:
|
|
|
|
<blockquote><pre>
|
|
wcb::callback .e2 before insert {wcb::checkEntryLen 10} \
|
|
wcb::checkEntryForInt
|
|
</pre></blockquote>
|
|
|
|
<p>And finally, here are the two callbacks for the third entry widget
|
|
<code>.e3</code>:
|
|
|
|
<blockquote><pre>
|
|
wcb::callback .e3 before insert {wcb::checkEntryLen 10} \
|
|
checkNumber
|
|
|
|
#
|
|
# Callback procedure checkNumber
|
|
#
|
|
proc checkNumber {w idx str} {
|
|
set newText [wcb::postInsertEntryText $w $idx $str]
|
|
if {![regexp {^[0-9]*\.?[0-9]?[0-9]?$} $newText]} {
|
|
wcb::cancel
|
|
}
|
|
}
|
|
</pre></blockquote>
|
|
|
|
<p>This last example also shows the arguments of the callbacks declared with
|
|
the <code><a href="wcbRef.html#callback">wcb::callback</a></code>
|
|
command: Whenever a callback is invoked, the name of the original Tcl
|
|
command for the widget as well as the arguments of the respective widget
|
|
operation are automatically appended to it as parameters. Since we
|
|
defined <code>checkNumber</code> as a before-callback for the
|
|
<code>insert</code> subcommand, its last three arguments must be: the name of
|
|
the original entry widget command (<code>w</code>), the index
|
|
(<code>idx</code>), and the string (<code>str</code>) to be inserted just
|
|
before the character indicated by the index.
|
|
|
|
<p>Notice that in the argument list of a Wcb callback, the name of the original
|
|
Tcl widget command can be be preceded by any number of additional
|
|
arguments. The procedure <code><a href=
|
|
"wcbRef.html#entrycb">wcb::checkEntryLen</a></code> is an example of such a
|
|
callback.
|
|
|
|
<p>The command <code><a href=
|
|
"wcbRef.html#entrytext">wcb::postInsertEntryText</a></code> invoked in the
|
|
procedure <code>checkNumber</code> returns the text that would be contained in
|
|
the entry widget <code>w</code> after inserting the string <code>str</code>
|
|
before the character indicated by the index <code>idx</code>. If this
|
|
text is not (the starting part of) an unsigned real number with at most two
|
|
digits after the decimal point, then we call the procedure <code><a href=
|
|
"wcbRef.html#cancel">wcb::cancel</a></code>, which aborts the
|
|
<code>insert</code> command.
|
|
|
|
<p>Without the constraint that the content of the third entry must not start
|
|
with a sign, we could have used the callback procedure <code><a href=
|
|
"wcbRef.html#entrycb">wcb::checkEntryForFixed</a></code> instead of
|
|
<code>checkNumber</code>:
|
|
|
|
<blockquote><pre>
|
|
wcb::callback .e3 before insert {wcb::checkEntryLen 10} \
|
|
{wcb::checkEntryForFixed 2}
|
|
</pre></blockquote>
|
|
|
|
|
|
<h3><a name="ex_listbox1"></a>A <code><font size=4>selset</font></code>
|
|
callback for a listbox</h3>
|
|
|
|
<p>In the case of a listbox, you will probably most often want to define a
|
|
callback for the <code>selection set</code> widget
|
|
subcommand. In most cases it does not matter whether this is a before- or
|
|
after-callback. Please note that the <code><a href=
|
|
"wcbRef.html#callback">wcb::callback</a></code> command expects the abbreviated
|
|
form <code>selset</code> as parameter. Similarly, you must pass
|
|
<code>selclear</code> to this command when defining a callback for the
|
|
<code>selection clear</code> listbox operation.
|
|
|
|
<p>In the following example we build a listbox <code>.lb</code> containing the
|
|
names of the bitmap files in the subdirectory <code>images</code> of the
|
|
directory <code>demos</code> in the Tk library directory. Whenever an
|
|
item is selected, the callback procedure <code>showBitmap</code> will
|
|
display the corresponding bitmap in the label <code>.picture</code>.
|
|
|
|
<blockquote>
|
|
<p><img src="listboxtest1.gif" alt="listboxtest1 - Please load this image!"
|
|
width=183 height=173>
|
|
</blockquote>
|
|
|
|
<p>Here is the relevant code fragment from the script
|
|
<code>listboxtest1.tcl</code>, contained in the <code>demos</code> directory:
|
|
|
|
<blockquote><pre>
|
|
set dirName [file join $tk_library demos images]
|
|
|
|
#
|
|
# Frame .spacer and listbox .lb
|
|
#
|
|
frame .spacer -width 10
|
|
listbox .lb -height 0 -width 0 -background gray98
|
|
set pattern [file join $dirName *.bmp]
|
|
foreach pathName [lsort [glob $pattern]] {
|
|
.lb insert end [file tail $pathName]
|
|
}
|
|
|
|
#
|
|
# Label .picture
|
|
#
|
|
label .picture -relief sunken
|
|
|
|
#
|
|
# Define a before-selset callback for .lb
|
|
#
|
|
wcb::callback .lb before selset showBitmap
|
|
|
|
#
|
|
# Callback procedure showBitmap
|
|
#
|
|
proc showBitmap {w first args} {
|
|
global dirName
|
|
set pathName [file join $dirName [$w get $first]]
|
|
.picture configure -bitmap @$pathName
|
|
}
|
|
</pre></blockquote>
|
|
|
|
<p>Recall that the <code>selection set</code> listbox operation
|
|
takes as arguments one or two indices, which will be passed automatically to
|
|
the callback as parameters, along with the name of the original Tcl command
|
|
associated with the listbox widget. For this reason, the arguments of the
|
|
callback procedure <code>showBitmap</code> are: the name of the original
|
|
listbox widget command (<code>w</code>), the first index (<code>first</code>),
|
|
as well as the <code>args</code> keyword representing the empty list or the
|
|
optional second index passed to the <code>selection set</code>
|
|
command.
|
|
|
|
|
|
<h3><a name="ex_listbox2"></a>An <code><font size=4>activate</font></code>
|
|
callback for a listbox</h3>
|
|
|
|
<p>The listbox used in the preceding example has the default selection mode
|
|
<code>browse</code>, hence the before-<code>selset</code> callback
|
|
<code>showBitmap</code> will be fired every time the mouse is dragged from one
|
|
element to another, with button 1 down. But what happens if we want to
|
|
display not only the bitmaps but also the photo images contained in the
|
|
subdirectory <code>images</code> of the directory <code>demos</code> in the Tk
|
|
library directory? Loading a photo image is a much more complex operation
|
|
than loading a bitmap, which can have the effect that some images cannot be
|
|
displayed quickly enough to follow the mouse when browsing with it within the
|
|
listbox.
|
|
|
|
<blockquote>
|
|
<p><img src="listboxtest2.gif" alt="listboxtest2 - Please load this image!"
|
|
width=195 height=241>
|
|
</blockquote>
|
|
|
|
<p>To solve this problem, we can either change the selection mode to have the
|
|
less common value <code>single</code>, or arrange for the images not to be
|
|
displayed when browsing with the mouse but when releasing its button 1.
|
|
The second method can be implemented with the aid of an <code>activate</code>
|
|
callback, as shown in the following code fragment taken from the script
|
|
<code>listboxtest2.tcl</code>, contained in the <code>demos</code> directory:
|
|
|
|
<blockquote><pre>
|
|
set dirName [file join $tk_library demos images]
|
|
image create photo photoImage
|
|
|
|
#
|
|
# Frame .spacer and listbox .lb
|
|
#
|
|
frame .spacer -width 10
|
|
listbox .lb -height 0 -width 0 -background gray98
|
|
set pattern [file join $dirName *]
|
|
foreach pathName [lsort [glob $pattern]] {
|
|
.lb insert end [file tail $pathName]
|
|
}
|
|
|
|
#
|
|
# Label .picture
|
|
#
|
|
label .picture -relief sunken
|
|
|
|
#
|
|
# Define a before-activate callback for .lb
|
|
#
|
|
wcb::callback .lb before activate showPicture
|
|
|
|
#
|
|
# Callback procedure showPicture
|
|
#
|
|
proc showPicture {w idx} {
|
|
set leafName [$w get $idx]
|
|
|
|
#
|
|
# When traversing the listbox with the arrow keys, the value
|
|
# of idx can become -1 or the number of listbox elements,
|
|
# hence the value of leafName can be an empty string:
|
|
#
|
|
if {[string compare $leafName ""] == 0} {
|
|
return ""
|
|
}
|
|
|
|
global dirName
|
|
set pathName [file join $dirName $leafName]
|
|
if {[string compare [file extension $pathName] ".bmp"] == 0} {
|
|
.picture configure -bitmap @$pathName -image ""
|
|
} else {
|
|
photoImage configure -file $pathName
|
|
.picture configure -bitmap "" -image photoImage
|
|
}
|
|
}
|
|
</pre></blockquote>
|
|
|
|
|
|
<h3><a name="ex_text"></a>Seven callbacks for a text widget</h3>
|
|
|
|
<p>The script <code>texttest1.tcl</code> in the <code>demos</code> directory
|
|
creates the text widget shown in the following figure:
|
|
|
|
<blockquote>
|
|
<p><img src="texttest1.gif" alt="texttest1 - Please load this image!"
|
|
width=366 height=260>
|
|
</blockquote>
|
|
|
|
<p>Here is the relevant code fragment:
|
|
|
|
<blockquote><pre>
|
|
#
|
|
# Text .txt
|
|
#
|
|
set width 50
|
|
text .txt -width $width -height 12 -setgrid true -wrap none -background white \
|
|
-font "Courier -12"
|
|
.txt tag configure prog -foreground red
|
|
.txt tag configure user -foreground DarkGreen
|
|
.txt insert end "Everything you type or paste into this window will\n" prog
|
|
.txt insert end "be displayed in dark green. You cannot make any\n" prog
|
|
.txt insert end "changes or selections in this red area, and will\n" prog
|
|
.txt insert end "not be able to send the message as long as any\n" prog
|
|
.txt insert end "line contains more than $width characters.\n" prog
|
|
.txt insert end "--------------------------------------------------\n" prog
|
|
set limit [.txt index insert]
|
|
|
|
#
|
|
# Label .pos displaying the current cursor position
|
|
#
|
|
label .pos -textvariable pos
|
|
|
|
#
|
|
# Button .send (actually, it does not send anything)
|
|
#
|
|
button .send -text Send -command exit
|
|
|
|
#
|
|
# Define 5 before- and 2 after-callbacks for .txt
|
|
#
|
|
wcb::callback .txt before insert protectRedArea changeColor
|
|
wcb::callback .txt before delete protectRedArea
|
|
wcb::callback .txt before selset protectRedArea
|
|
wcb::callback .txt before motion displayPos
|
|
wcb::callback .txt after insert "checkLines $width"
|
|
wcb::callback .txt after delete "checkLines $width"
|
|
|
|
#
|
|
# Callback procedure protectRedArea
|
|
#
|
|
# The parameters following w can be interpreted either as
|
|
# "index string ?tagList string tagList ...?" (for an insert
|
|
# callback), or as "from ?to?" (for a delete callback),
|
|
# or as "from ?to from to ...?" (for a selset callback).
|
|
#
|
|
proc protectRedArea {w idx args} {
|
|
global limit
|
|
if {[$w compare $idx < $limit]} {
|
|
wcb::cancel
|
|
}
|
|
}
|
|
|
|
#
|
|
# Callback procedure changeColor
|
|
#
|
|
proc changeColor {w args} {
|
|
wcb::extend user
|
|
}
|
|
|
|
#
|
|
# Callback procedure displayPos
|
|
#
|
|
proc displayPos {w idx} {
|
|
set index [$w index $idx]
|
|
scan $index "%d.%d" line column
|
|
incr column
|
|
|
|
global pos
|
|
set pos [format "Line: %d Column: %d" $line $column]
|
|
}
|
|
|
|
#
|
|
# Callback procedure checkLines
|
|
#
|
|
# The parameter args can be interpreted both as "index
|
|
# string ?tagList string tagList ...?" (for an insert
|
|
# callback) and as "from ?to?" (for a delete callback).
|
|
#
|
|
proc checkLines {maxCharsPerLine w args} {
|
|
#
|
|
# Display the new cursor position
|
|
#
|
|
displayPos $w insert
|
|
|
|
#
|
|
# Disable or enable the .send button
|
|
#
|
|
scan [$w index end] "%d" lastLine
|
|
for {set line 1} {$line < $lastLine} {incr line} {
|
|
scan [$w index $line.end] "%d.%d" dummy charsInLine
|
|
if {$charsInLine > $maxCharsPerLine} {
|
|
.send configure -state disabled
|
|
return ""
|
|
}
|
|
}
|
|
.send configure -state normal
|
|
}
|
|
|
|
. . .
|
|
|
|
displayPos .txt insert
|
|
focus .txt
|
|
</pre></blockquote>
|
|
|
|
<p>The procedure <code>protectRedArea</code> is a before-<code>insert</code>,
|
|
before-<code>delete</code>, and before-<code>selset</code> callback. It
|
|
checks whether the attempted change would affect the text area displayed in
|
|
red; if this is the case, it calls the procedure <code><a href=
|
|
"wcbRef.html#cancel">wcb::cancel</a></code>, which aborts the
|
|
<code>insert</code>, <code>delete</code>, or <code>tag add
|
|
sel</code> command, respectively.
|
|
|
|
<p>The before-<code>insert</code> callback <code>changeColor</code> invokes the
|
|
<code><a href="wcbRef.html#extend">wcb::extend</a></code> command to append the
|
|
<code>user</code> tag to the argument list of the <code>insert</code> command,
|
|
thus changing the foreground color of the characters entered by the user to
|
|
<code>DarkGreen</code>.
|
|
|
|
<p>The procedure <code>displayPos</code> displays the line and column
|
|
corresponding to the index passed to it as its second argument. This
|
|
index will be the target position of the insertion cursor when the procedure is
|
|
triggered automatically as a before-<code>motion</code> callback. As seen
|
|
in the <code>checkLines</code> procedure discussed below, it is also invoked
|
|
after performing an <code>insert</code> or <code>delete</code> operation; in
|
|
that case, its second argument will be the new position of the insertion cursor
|
|
after the execution of <code>insert</code> or <code>delete</code>. In
|
|
this way, we are able to keep track completely of the position of the insertion
|
|
cursor.
|
|
|
|
<p>It is interesting to see what happens if we register <code>displayPos</code>
|
|
as an after- instead of before-<code>motion</code> callback. Well, in
|
|
that case the procedure would have to ignore its second argument and we would
|
|
have to replace the line
|
|
|
|
<blockquote><pre>
|
|
set index [$w index $idx]
|
|
</pre></blockquote>
|
|
|
|
<p>with
|
|
|
|
<blockquote><pre>
|
|
set index [$w index insert]
|
|
</pre></blockquote>
|
|
|
|
<p>The reason is that the value of the <code>idx</code> argument passed to
|
|
<code>displayPos</code> can be, for instance, <code>insert+1c</code>, where
|
|
<code>insert</code> means the position of the insertion cursor <i>before</i>
|
|
moving it forward by one character. The after-<code>motion</code>
|
|
callback is, however, triggered <i>after</i> the insertion cursor has been
|
|
moved, and at that time the <code>insert</code> mark already points to the new
|
|
cursor position. For this reason, <code>[$w index
|
|
$idx]</code> is not adequate to retrieve the position of the insertion
|
|
cursor within an after-<code>motion</code> callback.
|
|
|
|
<p>Our last procedure <code>checkLines</code> is both an
|
|
after-<code>insert</code> and after-<code>delete</code> callback. After
|
|
calling <code>displayPos</code> to display the new cursor position, it disables
|
|
or enables the <code>.send</code> button, depending upon whether any line of
|
|
the text widget contains more than <code>$maxCharsPerLine</code> characters.
|
|
|
|
<p>Instead of just disabling the <code>.send</code> button if any line has more
|
|
than <code>$maxCharsPerLine</code> characters, we can even prevent the user
|
|
from entering lines that are longer than allowed. The script
|
|
<code>texttest2.tcl</code> in the <code>demos</code> directory shows how this
|
|
can be achieved by combining widget callbacks with the undo mechanism for text
|
|
widgets, introduced in Tk 8.4. This script creates the text widget shown
|
|
in the following figure:
|
|
|
|
<blockquote>
|
|
<p><img src="texttest2.gif" alt="texttest2- Please load this image!"
|
|
width=366 height=260>
|
|
</blockquote>
|
|
|
|
<p>Most of the code contained in the script <code>texttest2.tcl</code> is
|
|
identical to the one in the previous example. The main difference is a
|
|
new line activating the undo mechanism for the text widget <code>.txt</code>
|
|
and a new version of the callback procedure <code>checkLines</code>:
|
|
|
|
<blockquote><pre>
|
|
. . .
|
|
|
|
.txt configure -undo yes
|
|
|
|
. . .
|
|
|
|
proc checkLines {maxCharsPerLine w args} {
|
|
#
|
|
# Undo the last insert or delete action if necessary
|
|
#
|
|
scan [$w index end] "%d" lastLine
|
|
for {set line 1} {$line < $lastLine} {incr line} {
|
|
scan [$w index $line.end] "%d.%d" dummy charsInLine
|
|
if {$charsInLine > $maxCharsPerLine} {
|
|
$w edit undo
|
|
bell
|
|
break
|
|
}
|
|
}
|
|
|
|
#
|
|
# Clear the undo and redo stacks, and display the new cursor position
|
|
#
|
|
$w edit reset
|
|
displayPos $w insert
|
|
}
|
|
|
|
. . .
|
|
</pre></blockquote>
|
|
|
|
<p>This version of the callback <code>checkLines</code> undoes the last edit
|
|
action if any text line contains more characters than the allowed
|
|
maximum. For this reason, we had to move the invocation of the
|
|
<code>displayPos</code> procedure to the end of the callback, because the
|
|
<code>edit undo</code> command might change the position of the insertion
|
|
cursor.
|
|
|
|
<p>Note that we could have implemented this example also without making use of
|
|
the undo mechanism for text widgets, by saving the last accepted contents of
|
|
the widget, along with the cursor position, and restoring them in case any line
|
|
gets longer than allowed. The resulting script would then run also with
|
|
Tk versions earlier than 8.4.
|
|
|
|
<div align=center>
|
|
<p><a href="#contents">Contents</a>
|
|
<a href="index.html">Start page</a>
|
|
</div>
|
|
</body>
|
|
</html>
|