arcadia 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. data/README +25 -14
  2. data/conf/LC/en-UK.LANG +3 -1
  3. data/conf/arcadia.conf +10 -0
  4. data/conf/arcadia.res.rb +29 -1
  5. data/ext/ae-editor/ae-editor.rb +239 -48
  6. data/ext/ae-file-history/ae-file-history.conf +11 -1
  7. data/ext/ae-file-history/ae-file-history.rb +120 -2
  8. data/ext/ae-ruby-debug/ae-ruby-debug.rb +6 -5
  9. data/ext/ae-subprocess-inspector/ae-subprocess-inspector.rb +7 -3
  10. data/ext/ae-term/ae-term.rb +1 -1
  11. data/lib/a-commons.rb +72 -56
  12. data/lib/a-contracts.rb +23 -1
  13. data/lib/a-core.rb +136 -41
  14. data/lib/a-tkcommons.rb +127 -36
  15. data/tcl/fsdialog/fsdialog.tcl +2 -2
  16. data/tcl/ptwidgets-1.1.0/COPYRIGHT +10 -0
  17. data/tcl/ptwidgets-1.1.0/ChangeLog +194 -0
  18. data/tcl/ptwidgets-1.1.0/README +50 -0
  19. data/tcl/ptwidgets-1.1.0/common/stacktrace.tcl +29 -0
  20. data/tcl/ptwidgets-1.1.0/common/tokenframe.tcl +200 -0
  21. data/tcl/ptwidgets-1.1.0/doc/img/toggleswitch_off.png +0 -0
  22. data/tcl/ptwidgets-1.1.0/doc/img/toggleswitch_on.png +0 -0
  23. data/tcl/ptwidgets-1.1.0/doc/img/tokenentry.png +0 -0
  24. data/tcl/ptwidgets-1.1.0/doc/img/tokensearch_popup_example.png +0 -0
  25. data/tcl/ptwidgets-1.1.0/doc/img/tokensearch_popup_example2.png +0 -0
  26. data/tcl/ptwidgets-1.1.0/doc/img/wmarkentry.png +0 -0
  27. data/tcl/ptwidgets-1.1.0/doc/toggleswitch.html +402 -0
  28. data/tcl/ptwidgets-1.1.0/doc/tokenentry.html +1366 -0
  29. data/tcl/ptwidgets-1.1.0/doc/tokensearch.html +1549 -0
  30. data/tcl/ptwidgets-1.1.0/doc/wmarkentry.html +634 -0
  31. data/tcl/ptwidgets-1.1.0/library/toggleswitch.tcl +432 -0
  32. data/tcl/ptwidgets-1.1.0/library/tokenentry.tcl +2208 -0
  33. data/tcl/ptwidgets-1.1.0/library/tokensearch.tcl +2488 -0
  34. data/tcl/ptwidgets-1.1.0/library/wmarkentry.tcl +630 -0
  35. data/tcl/ptwidgets-1.1.0/pkgIndex.tcl +10 -0
  36. data/tcl/ptwidgets-1.1.0/test/Makefile +3 -0
  37. data/tcl/ptwidgets-1.1.0/test/run.tcl +3 -0
  38. data/tcl/ptwidgets-1.1.0/test/test.tcl +89 -0
  39. data/tcl/ptwidgets-1.1.0/test/toggleswitch.test +562 -0
  40. data/tcl/ptwidgets-1.1.0/test/tokenentry.test +1023 -0
  41. data/tcl/ptwidgets-1.1.0/test/tokensearch.test +1023 -0
  42. data/tcl/ptwidgets-1.1.0/test/wmarkentry.test +1325 -0
  43. data/tcl/themes/altTheme.tcl +101 -0
  44. data/tcl/themes/aquaTheme.tcl +59 -0
  45. data/tcl/themes/clamTheme.tcl +140 -0
  46. data/tcl/themes/classicTheme.tcl +108 -0
  47. data/tcl/themes/pkgIndex.tcl +3 -0
  48. data/tcl/themes/ttk.tcl +176 -0
  49. data/tcl/themes/vistaTheme.tcl +224 -0
  50. data/tcl/themes/winTheme.tcl +80 -0
  51. data/tcl/themes/xpTheme.tcl +65 -0
  52. data/tcl/tkfbox/folder.gif +0 -0
  53. data/tcl/tkfbox/textfile.gif +0 -0
  54. data/tcl/tkfbox/tkfbox.tcl +1 -0
  55. data/tcl/tkfbox/tkfbox.tcl~ +1 -0
  56. data/tcl/tkfbox/updir.xbm +1 -0
  57. metadata +43 -2
@@ -0,0 +1,432 @@
1
+ #===============================================================
2
+ # Main toggleswitch package module
3
+ #
4
+ # Copyright (c) 2011-2012 Trevor Williams (phase1geo@gmail.com)
5
+ #===============================================================
6
+
7
+ package provide toggleswitch 1.0
8
+
9
+ namespace eval toggleswitch {
10
+
11
+ array set data {}
12
+
13
+ array set widget_options {
14
+ -borderwidth {borderWidth BorderWidth}
15
+ -command {command Command}
16
+ -cursor {cursor Cursor}
17
+ -font {font Font}
18
+ -offbackground {offBackground Background}
19
+ -offforeground {offForeground Foreground}
20
+ -offvalue {offValue Value}
21
+ -onbackground {onBackground Background}
22
+ -onforeground {onForeground Foreground}
23
+ -onvalue {onValue Value}
24
+ -relief {relief Relief}
25
+ -state {state State}
26
+ -takefocus {takeFocus TakeFocus}
27
+ -variable {variable Variable}
28
+ }
29
+
30
+ ###########################################################################
31
+ # Main procedure to create the on/off switch.
32
+ proc toggleswitch {w args} {
33
+
34
+ variable data
35
+ variable widget_options
36
+
37
+ # Create window
38
+ frame $w -class ToggleSwitch -takefocus 0
39
+ frame $w.on -relief sunken -takefocus 0
40
+ frame $w.off -relief sunken -takefocus 0
41
+
42
+ pack [ttk::label $w.on.l -text " ON" -width 4 -takefocus 0] -fill both -expand yes
43
+ pack [ttk::label $w.off.l -text " OFF" -width 4 -takefocus 0] -fill both -expand yes
44
+
45
+ grid columnconfigure $w 0 -weight 1
46
+ grid columnconfigure $w 1 -weight 1
47
+ grid $w.on -row 0 -column 0 -sticky news
48
+ grid $w.off -row 0 -column 1 -sticky news
49
+
50
+ # Create switch
51
+ update
52
+ ttk::frame $w.sw -relief raised -takefocus 1 -width [winfo reqwidth $w.on] -height [winfo reqheight $w.on]
53
+
54
+ # Default in the off position
55
+ place $w.sw -x 0 -y 0
56
+
57
+ # Initialize options
58
+ # Initialize default options
59
+ if {[array size data] == 0} {
60
+ option add *ToggleSwitch.offBackground white widgetDefault
61
+ option add *ToggleSwitch.onBackground blue widgetDefault
62
+ option add *ToggleSwitch.offForeground grey widgetDefault
63
+ option add *ToggleSwitch.onForeground white widgetDefault
64
+ option add *ToggleSwitch.borderWidth 1 widgetDefault
65
+ option add *ToggleSwitch.command "" widgetDefault
66
+ option add *ToggleSwitch.cursor "" widgetDefault
67
+ option add *ToggleSwitch.font "" widgetDefault
68
+ option add *ToggleSwitch.height 18 widgetDefault
69
+ option add *ToggleSwitch.offValue 0 widgetDefault
70
+ option add *ToggleSwitch.onValue 1 widgetDefault
71
+ option add *ToggleSwitch.relief flat widgetDefault
72
+ option add *ToggleSwitch.state normal widgetDefault
73
+ option add *ToggleSwitch.takeFocus 1 widgetDefault
74
+ option add *ToggleSwitch.variable "" widgetDefault
75
+ option add *ToggleSwitch.width 60 widgetDefault
76
+ }
77
+
78
+ # Initialize the options array
79
+ foreach opt [array names widget_options] {
80
+ set data($w,$opt) [option get $w [lindex $widget_options($opt) 0] [lindex $widget_options($opt) 1]]
81
+ }
82
+
83
+ # Set the default value to off (0)
84
+ set data($w,value) 0
85
+
86
+ # Add the bindings
87
+ bind $w.on <Button-1> "toggleswitch::off $w"
88
+ bind $w.on.l <Button-1> "toggleswitch::off $w"
89
+ bind $w.off <Button-1> "toggleswitch::on $w"
90
+ bind $w.off.l <Button-1> "toggleswitch::on $w"
91
+ bind $w.sw <ButtonPress-1> "toggleswitch::press $w %X"
92
+ bind $w.sw <B1-Motion> "toggleswitch::slide $w %X"
93
+ bind $w.sw <ButtonRelease-1> "toggleswitch::release $w %X"
94
+ bind $w.sw <space> "toggleswitch::toggle $w"
95
+ bind $w.sw <FocusOut> "toggleswitch::focus_next $w"
96
+
97
+ # Configure the widget
98
+ eval "configure 1 $w $args"
99
+
100
+ # Rename and alias the tokenentry window
101
+ rename ::$w $w
102
+ interp alias {} ::$w {} toggleswitch::widget_cmd $w
103
+
104
+ return $w
105
+
106
+ }
107
+
108
+ ###########################################################################
109
+ # Changes focus to the next window after w.
110
+ proc focus_next {w} {
111
+
112
+ # Change the focus
113
+ focus [tk_focusNext $w.sw]
114
+
115
+ }
116
+
117
+ ###########################################################################
118
+ # Procedure called when the user clicks on the off position. Moves the
119
+ # switch frame to display the on label.
120
+ proc on {w {from_program 0}} {
121
+
122
+ variable data
123
+
124
+ if {($from_program == 1) || ($data($w,-state) eq "normal")} {
125
+
126
+ # Move the switch frame to display the on position
127
+ place $w.sw -x [winfo reqwidth $w.on] -y 0
128
+
129
+ # Set the current value to the value of -onvalue
130
+ set data($w,value) 1
131
+
132
+ # Set the global variable, if it is set
133
+ if {$data($w,-variable) ne ""} {
134
+ upvar #0 $data($w,-variable) var
135
+ set var $data($w,-onvalue)
136
+ }
137
+
138
+ # If a command is specified, execute it now
139
+ if {$data($w,-command) ne ""} {
140
+ eval "$data($w,-command)"
141
+ }
142
+
143
+ }
144
+
145
+ }
146
+
147
+ ###########################################################################
148
+ # Procedure called when the user clicks on the on position. Moves the
149
+ # switch frame to display the off label.
150
+ proc off {w {from_program 0}} {
151
+
152
+ variable data
153
+
154
+ if {($from_program == 1) || ($data($w,-state) eq "normal")} {
155
+
156
+ # Move the switch frame to display the off position
157
+ place $w.sw -x 0 -y 0
158
+
159
+ # Set the current value to the value of -offvalue
160
+ set data($w,value) 0
161
+
162
+ # Set the global variable, if it is set
163
+ if {$data($w,-variable) ne ""} {
164
+ upvar #0 $data($w,-variable) var
165
+ set var $data($w,-offvalue)
166
+ }
167
+
168
+ # If a command is specified, execute it now
169
+ if {$data($w,-command) ne ""} {
170
+ eval "$data($w,-command)"
171
+ }
172
+
173
+ }
174
+
175
+ }
176
+
177
+ ###########################################################################
178
+ # Procedure called when the left button is pressed. Records the position
179
+ # of the cursor within switch frame.
180
+ proc press {w x} {
181
+
182
+ variable data
183
+
184
+ set data($w,switchx) [expr $x - [winfo rootx $w.sw]]
185
+
186
+ }
187
+
188
+ ###########################################################################
189
+ # Procedure called when the mouse cursor is moved when the left-button is
190
+ # pressed. Moves the switch frame to match the motion of the mouse while
191
+ # keeping the switch frame within the bounds of the switch.
192
+ proc slide {w x} {
193
+
194
+ variable data
195
+
196
+ if {$data($w,-state) eq "normal"} {
197
+
198
+ set next_x [expr ($x - $data($w,switchx)) - [winfo rootx $w]]
199
+
200
+ if {$next_x < 0} {
201
+ set next_x 0
202
+ } elseif {[expr $next_x + [winfo width $w.sw]] > [winfo width $w]} {
203
+ set next_x [expr [winfo width $w] - [winfo width $w.sw]]
204
+ }
205
+
206
+ place $w.sw -x $next_x -y 0
207
+
208
+ }
209
+
210
+ }
211
+
212
+ ###########################################################################
213
+ # Procedure called when the left-button is released. Causes the switch
214
+ # frame to go to either the on or off position based on the location of
215
+ # of the switch frame.
216
+ proc release {w x} {
217
+
218
+ variable data
219
+
220
+ if {$data($w,-state) eq "normal"} {
221
+
222
+ set next_x [expr ($x - $data($w,switchx)) - [winfo rootx $w]]
223
+
224
+ if {$next_x < [expr [winfo width $w.sw] / 2]} {
225
+ off $w
226
+ } else {
227
+ on $w
228
+ }
229
+
230
+ }
231
+
232
+ }
233
+
234
+ ###########################################################################
235
+ # Procedure to handle all of the user command requests.
236
+ proc widget_cmd {w args} {
237
+
238
+ if {[llength $args] == 0} {
239
+ return -code error "toggleswitch widget called without a command"
240
+ }
241
+
242
+ set cmd [lindex $args 0]
243
+ set opts [lrange $args 1 end]
244
+
245
+ switch $cmd {
246
+ configure { return [eval "toggleswitch::configure 0 $w $opts"] }
247
+ cget { return [eval "toggleswitch::cget $w $opts"] }
248
+ switchoff { eval "toggleswitch::switchoff $w $opts" }
249
+ invoke { eval "toggleswitch::invoke $w $opts" }
250
+ switchon { eval "toggleswitch::switchon $w $opts" }
251
+ toggle { eval "toggleswitch::toggle $w $opts" }
252
+ default { return -code error "Unknown toggleswitch command ($cmd)" }
253
+ }
254
+
255
+ }
256
+
257
+ #-------------------------------------------------------------------------
258
+
259
+ ###########################################################################
260
+ # Procedure handles the configuration command.
261
+ proc configure {initialize w args} {
262
+
263
+ variable widget_options
264
+ variable data
265
+
266
+ if {([llength $args] == 0) && !$initialize} {
267
+
268
+ set results [list]
269
+
270
+ foreach opt [lsort [array names widget_options]] {
271
+ if {[llength $widget_options($opt)] == 2} {
272
+ set opt_name [lindex $widget_options($opt) 0]
273
+ set opt_class [lindex $widget_options($opt) 1]
274
+ set opt_default [option get $w $opt_name $opt_class]
275
+ if {[info exists data($w,$opt)]} {
276
+ lappend results [list $opt $opt_name $opt_class $opt_default $data($w,$opt)]
277
+ } else {
278
+ lappend results [list $opt $opt_name $opt_class $opt_default ""]
279
+ }
280
+ }
281
+ }
282
+
283
+ return $results
284
+
285
+ } elseif {([llength $args] == 1) && !$initialize} {
286
+
287
+ set opt [lindex $args 0]
288
+
289
+ if {[info exists widget_options($opt)]} {
290
+ if {[llength $widget_options($opt)] == 1} {
291
+ set opt [lindex $widget_options($opt) 0]
292
+ }
293
+ set opt_name [lindex $widget_options($opt) 0]
294
+ set opt_class [lindex $widget_options($opt) 1]
295
+ set opt_default [option get $w $opt_name $opt_class]
296
+ if {[info exists data($w,$opt)]} {
297
+ return [list $opt $opt_name $opt_class $opt_default $data($w,$opt)]
298
+ } else {
299
+ return [list $opt $opt_name $opt_class $opt_default ""]
300
+ }
301
+ }
302
+
303
+ return -code error "ToggleSwitch configuration option [lindex $args 0] does not exist"
304
+
305
+ } else {
306
+
307
+ # Parse the arguments
308
+ foreach {name value} $args {
309
+ if {[info exists data($w,$name)]} {
310
+ set data($w,$name) $value
311
+ } else {
312
+ return -code error "Illegal option given to the toggleswitch configure command ($name)"
313
+ }
314
+ }
315
+
316
+ # Set the current value
317
+ if {$data($w,-variable) ne ""} {
318
+ upvar #0 $data($w,-variable) var
319
+ if {$var eq $data($w,-onvalue)} {
320
+ set data($w,value) 1
321
+ on $w 1
322
+ } else {
323
+ set data($w,value) 0
324
+ off $w 1
325
+ }
326
+ }
327
+
328
+ # Update the widget states
329
+ if {$data($w,-state) eq "normal"} {
330
+ $w.on configure -background $data($w,-onbackground)
331
+ $w.on.l configure -background $data($w,-onbackground) -foreground $data($w,-onforeground)
332
+ $w.off configure -background $data($w,-offbackground)
333
+ $w.off.l configure -background $data($w,-offbackground) -foreground $data($w,-offforeground)
334
+ } else {
335
+ $w.on configure -background grey
336
+ $w.on.l configure -background grey -foreground white
337
+ $w.off configure -background white
338
+ $w.off.l configure -background white -foreground grey
339
+ }
340
+
341
+ # Set the cursor
342
+ $w configure -cursor $data($w,-cursor)
343
+
344
+ }
345
+
346
+ }
347
+
348
+ ###########################################################################
349
+ # Procedure that handles the cget command.
350
+ proc cget {w args} {
351
+
352
+ variable data
353
+
354
+ if {[llength $args] != 1} {
355
+ return -code error "Incorrect number of parameters given to the toggleswitch cget command"
356
+ }
357
+
358
+ if {[info exists data($w,[lindex $args 0])]} {
359
+ return $data($w,[lindex $args 0])
360
+ } else {
361
+ return -code error "Illegal option given to the toggleswitch cget command ([lindex $args 0])"
362
+ }
363
+
364
+ }
365
+
366
+ ###########################################################################
367
+ # Procedure which turns the switch to the off position.
368
+ proc switchoff {w args} {
369
+
370
+ variable data
371
+
372
+ if {[llength $args] != 0} {
373
+ return -code error "Incorrect number of parameters given to the toggleswitch::switchoff command"
374
+ }
375
+
376
+ # Switch to the off position
377
+ off $w 1
378
+
379
+ }
380
+
381
+ ###########################################################################
382
+ # Procedure which invokes the widget.
383
+ proc invoke {w args} {
384
+
385
+ variable data
386
+
387
+ if {[llength $args] != 0} {
388
+ return -code error "Incorrect number of parameters given to the toggleswitch::invoke command"
389
+ }
390
+
391
+ if {$data($w,value)} {
392
+ on $w 1
393
+ } else {
394
+ off $w 1
395
+ }
396
+
397
+ }
398
+
399
+ ###########################################################################
400
+ # Procedure which turns the switch to the on position.
401
+ proc switchon {w args} {
402
+
403
+ variable data
404
+
405
+ if {[llength $args] != 0} {
406
+ return -code error "Incorrect number of parameters given to the toggleswitch::switchon command"
407
+ }
408
+
409
+ # Switch to the on position
410
+ on $w 1
411
+
412
+ }
413
+
414
+ ###########################################################################
415
+ # Procedure which toggles the current value of the switch.
416
+ proc toggle {w args} {
417
+
418
+ variable data
419
+
420
+ if {[llength $args] != 0} {
421
+ return -code error "Incorrect number of parameters given to the toggleswitch::toggle command"
422
+ }
423
+
424
+ if {$data($w,value)} {
425
+ off $w 1
426
+ } else {
427
+ on $w 1
428
+ }
429
+
430
+ }
431
+
432
+ }
@@ -0,0 +1,2208 @@
1
+ #===============================================================
2
+ # Main tokenentry package module
3
+ #
4
+ # Copyright (c) 2011-2012 Trevor Williams (phase1geo@gmail.com)
5
+ #===============================================================
6
+
7
+ package provide tokenentry 1.0
8
+
9
+ source [file join [tokenentry::DIR] common tokenframe.tcl]
10
+
11
+ namespace eval tokenentry {
12
+
13
+ array set token_index {}
14
+ array set active_token {}
15
+ array set options {}
16
+ array set dont_tokenize {}
17
+ array set old_focus {}
18
+ array set old_grab {}
19
+ array set dropdown_token {}
20
+ array set images {}
21
+ array set pressed_token {}
22
+ array set current_matches {}
23
+ array set token_count {}
24
+ array set token_shapes {}
25
+ array set state {}
26
+
27
+ array set text_options {
28
+ -background 1
29
+ -bg 1
30
+ -borderwidth 1
31
+ -bd 1
32
+ -exportselection 1
33
+ -font 1
34
+ -foreground 1
35
+ -fg 1
36
+ -highlightbackground 1
37
+ -highlightcolor 1
38
+ -highlightthickness 1
39
+ -insertbackground 1
40
+ -insertborderwidth 1
41
+ -insertofftime 1
42
+ -insertontime 1
43
+ -insertwidth 1
44
+ -padx 1
45
+ -pady 1
46
+ -relief 1
47
+ -selectbackground 1
48
+ -selectborderwidth 1
49
+ -selectforeground 1
50
+ -setgrid 1
51
+ -state 1
52
+ -takefocus 1
53
+ -xscrollcommand 1
54
+ -yscrollcommand 1
55
+ -autoseparators 1
56
+ }
57
+
58
+ array set widget_options {
59
+ -autoseparators {autoSeparators AutoSeparators}
60
+ -background {background Background}
61
+ -bg -background
62
+ -borderwidth {borderWidth BorderWidth}
63
+ -bd -borderwidth
64
+ -dropdownformatstring {dropDownFormatString DropDownFormatString}
65
+ -dropdownheight {dropDownHeight DropDownHeight}
66
+ -dropdownmaxheight {dropDownMaxHeight DropDownMaxHeight}
67
+ -exportselection {exportSelection ExportSelection}
68
+ -font {font Font}
69
+ -foreground {foreground Foreground}
70
+ -fg -foreground
71
+ -height {height Height}
72
+ -highlightbackground {highlightBackground HighlightBackground}
73
+ -highlightcolor {highlightColor HighlightColor}
74
+ -highlightthickness {highlightThickness HighlightThickness}
75
+ -insertbackground {insertBackground InsertBackground}
76
+ -insertborderwidth {insertBorderWidth InsertBorderWidth}
77
+ -insertofftime {insertOffTime InsertOffTime}
78
+ -insertontime {insertOnTime InsertOnTime}
79
+ -insertwidth {insertWidth InsertWidth}
80
+ -listvar {listVar ListVar}
81
+ -matchcase {matchCase MatchCase}
82
+ -matchdisplayindex {matchDisplayIndex MatchDisplayIndex}
83
+ -matchindex {matchIndex MatchIndex}
84
+ -matchmode {matchMode MatchMode}
85
+ -padx {padX Pad}
86
+ -pady {padY Pad}
87
+ -relief {relief Relief}
88
+ -selectbackground {selectBackground Background}
89
+ -selectborderwidth {selectBorderWidth BorderWidth}
90
+ -selectforeground {selectForeground Foreground}
91
+ -setgrid {setGrid SetGrid}
92
+ -state {state State}
93
+ -takefocus {takeFocus TakeFocus}
94
+ -tokenbg {tokenBackground TokenBackground}
95
+ -tokenbordercolor {tokenBorderColor TokenBorderColor}
96
+ -tokenfg {tokenForeground TokenForeground}
97
+ -tokenselectbg {tokenSelectBackground TokenSelectBackground}
98
+ -tokenselectbordercolor {tokenSelectBorderColor TokenSelectBorderColor}
99
+ -tokenselectfg {tokenSelectForeground TokenSelectForeground}
100
+ -tokenshape {tokenShape TokenShape}
101
+ -tokenvar {tokenVar TokenVar}
102
+ -watermark {watermark Watermark}
103
+ -watermarkforeground {watermarkForeground Foreground}
104
+ -width {width Width}
105
+ -wrap {wrap Wrap}
106
+ -xscrollcommand {xScrollCommand ScrollCommand}
107
+ -yscrollcommand {yScrollCommand ScrollCommand}
108
+ }
109
+
110
+ variable img_arrow
111
+ variable img_blank
112
+
113
+ set img_arrow [image create photo -data "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"]
114
+ set img_blank [image create photo -data "R0lGODlhBwAHAIAAAP///wAAACH5BAkKAAEAIf8 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"]
115
+
116
+ ###########################################################################
117
+ # Main procedure which creates the given window and initializes it.
118
+ proc tokenentry {w args} {
119
+
120
+ variable token_index
121
+ variable options
122
+ variable dont_tokenize
123
+ variable active_token
124
+ variable pressed_token
125
+ variable token_count
126
+ variable widget_options
127
+ variable dropdown_token
128
+ variable state
129
+
130
+ # The widget will be a frame
131
+ frame $w -class TokenEntry -takefocus 0
132
+
133
+ # Initially, we pack the frame with a text widget
134
+ text $w.txt -highlightthickness 0 -relief flat -bg white -spacing1 2 -spacing2 2 -spacing3 2 -takefocus 1
135
+
136
+ # Pack the text widget
137
+ pack $w.txt -side left -fill both -expand yes
138
+
139
+ # Create the popup window that might be used by this widget
140
+ toplevel $w.top
141
+ listbox $w.top.list -selectmode browse -background white -yscrollcommand "$w.top.vsb set" -exportselection 0 -borderwidth 0 -cursor top_left_arrow
142
+ ttk::scrollbar $w.top.vsb -command "$w.top.list yview"
143
+
144
+ pack $w.top.list -side left -fill both -expand y
145
+
146
+ # Handle the popup
147
+ wm overrideredirect $w.top 1
148
+ wm transient $w.top [winfo toplevel $w]
149
+ wm group $w.top [winfo parent $w]
150
+ wm withdraw $w.top
151
+
152
+ # Initialize default options
153
+ if {[array size token_index] == 0} {
154
+ foreach opt [array names widget_options] {
155
+ if {![catch "$w.txt configure $opt" rc]} {
156
+ if {[llength $widget_options($opt)] != 1} {
157
+ if {$opt eq "-wrap"} {
158
+ set default_value 0
159
+ } elseif {$opt eq "-height"} {
160
+ set default_value 1
161
+ } elseif {$opt eq "-background"} {
162
+ set default_value "white"
163
+ } elseif {$opt eq "-relief"} {
164
+ set default_value "ridge"
165
+ } else {
166
+ set default_value [lindex $rc 4]
167
+ }
168
+ option add *TokenEntry.[lindex $rc 1] $default_value widgetDefault
169
+ }
170
+ }
171
+ }
172
+ option add *TokenEntry.tokenForeground black widgetDefault
173
+ option add *TokenEntry.tokenBackground "light blue" widgetDefault
174
+ option add *TokenEntry.tokenBorderColor "light blue" widgetDefault
175
+ option add *TokenEntry.tokenSelectForeground white widgetDefault
176
+ option add *TokenEntry.tokenSelectBackground blue widgetDefault
177
+ option add *TokenEntry.tokenSelectBorderColor blue widgetDefault
178
+ option add *TokenEntry.dropDownHeight 0 widgetDefault
179
+ option add *TokenEntry.dropDownMaxHeight 5 widgetDefault
180
+ option add *TokenEntry.matchMode glob widgetDefault
181
+ option add *TokenEntry.matchIndex "" widgetDefault
182
+ option add *TokenEntry.matchCase 0 widgetDefault
183
+ option add *TokenEntry.matchDisplayIndex 0 widgetDefault
184
+ option add *TokenEntry.listVar "" widgetDefault
185
+ option add *TokenEntry.tokenVar "" widgetDefault
186
+ option add *TokenEntry.tokenShape pill widgetDefault
187
+ option add *TokenEntry.dropDownFormatString "%s" widgetDefault
188
+ option add *TokenEntry.watermark "" widgetDefault
189
+ option add *TokenEntry.watermarkForeground "light gray" widgetDefault
190
+ }
191
+
192
+ # Initialize variables
193
+ set token_index($w) 0
194
+ set active_token($w) ""
195
+ set dont_tokenize($w) 0
196
+ set dropdown_token($w) ""
197
+ set pressed_token($w) ""
198
+ set token_count($w) 0
199
+ set state($w) "unknown"
200
+
201
+ # Initialize the options array
202
+ foreach opt [array names widget_options] {
203
+ set options($w,$opt) [option get $w [lindex $widget_options($opt) 0] [lindex $widget_options($opt) 1]]
204
+ }
205
+
206
+ # Setup bindings
207
+ bind $w.txt <FocusOut> "set tokenentry::dont_tokenize($w) 0; tokenentry::tokenize $w"
208
+ bind $w.txt <Key-comma> "set tokenentry::dont_tokenize($w) 0; tokenentry::tokenize $w; break"
209
+ bind $w.txt <Return> "tokenentry::key_return $w; break"
210
+ bind $w.txt <Tab> "tokenentry::focus_next $w; break"
211
+ bind $w.txt <Left> {
212
+ tokenentry::key_left_right [winfo parent %W] left
213
+ if {[tokenentry::handle_text_movement [winfo parent %W]]} {
214
+ break
215
+ }
216
+ }
217
+ bind $w.txt <Right> {
218
+ tokenentry::key_left_right [winfo parent %W] right
219
+ if {[tokenentry::handle_text_movement [winfo parent %W]]} {
220
+ break
221
+ }
222
+ }
223
+ bind $w.txt <Down> {
224
+ tokenentry::key_down [winfo parent %W]
225
+ if {[tokenentry::handle_text_movement [winfo parent %W]]} {
226
+ break
227
+ }
228
+ }
229
+ bind $w.txt <Up> {
230
+ tokenentry::key_up [winfo parent %W]
231
+ if {[tokenentry::handle_text_movement [winfo parent %W]]} {
232
+ break
233
+ }
234
+ }
235
+ bind $w.txt <Escape> "tokenentry::close_dropdown $w"
236
+ bind $w.txt <Button-1> {
237
+ tokenentry::close_dropdown [winfo parent %W]
238
+ if {[tokenentry::handle_text_movement [winfo parent %W]]} {
239
+ break
240
+ }
241
+ }
242
+ bind $w.txt <B1-Motion> {
243
+ if {[tokenentry::handle_text_movement [winfo parent %W]]} {
244
+ break
245
+ }
246
+ }
247
+ bind $w.txt <B1-Leave> {
248
+ if {[tokenentry::handle_text_movement [winfo parent %W]]} {
249
+ break
250
+ }
251
+ }
252
+ bind $w.txt <Any-KeyPress> "tokenentry::keypress $w"
253
+ bind $w.top.list <Motion> "tokenentry::motion_dropdown $w %x %y"
254
+ bind $w.top.list <Button-1> "tokenentry::key_return $w; focus $w.txt"
255
+ bind $w.txt <<Modified>> "tokenentry::modified $w"
256
+ bind $w.txt <<Selection>> "tokenentry::handle_selection_change $w"
257
+ bind $w.txt <Destroy> "tokenentry::handle_destroy $w"
258
+ if {[tk windowingsystem] eq "aqua"} {
259
+ bind $w.txt <Command-x> "tokenentry::handle_cut $w"
260
+ bind $w.txt <Command-c> "tokenentry::handle_copy $w"
261
+ bind $w.txt <Command-v> "tokenentry::handle_paste $w"
262
+ } else {
263
+ bind $w.txt <Control-x> "tokenentry::handle_cut $w"
264
+ bind $w.txt <Control-c> "tokenentry::handle_copy $w"
265
+ bind $w.txt <Control-v> "tokenentry::handle_paste $w"
266
+ }
267
+
268
+ # Configure the widget
269
+ eval "configure 1 $w $args"
270
+
271
+ # Rename and alias the tokenentry window
272
+ rename ::$w $w
273
+ interp alias {} ::$w {} tokenentry::widget_cmd $w
274
+
275
+ return $w
276
+
277
+ }
278
+
279
+ ###########################################################################
280
+ # This procedure is called when the widget is destroyed.
281
+ proc handle_destroy {w} {
282
+
283
+ variable images
284
+
285
+ # Delete the images
286
+ foreach {key value} [array get images $w,*] {
287
+ image delete $value
288
+ }
289
+
290
+ # Delete the array values, themselves
291
+ array unset images $w,*
292
+
293
+ }
294
+
295
+ ###########################################################################
296
+ # Changes focus to the next window after w.
297
+ proc focus_next {w} {
298
+
299
+ # Change the focus
300
+ focus [tk_focusNext $w.txt]
301
+
302
+ }
303
+
304
+ ###########################################################################
305
+ # This procedure is called when the text widget is modified.
306
+ proc modified {w} {
307
+
308
+ variable token_count
309
+ variable options
310
+ variable state
311
+
312
+ if {[$w.txt edit modified]} {
313
+
314
+ set last_value [$w.txt get {insert - 1 chars} insert]
315
+
316
+ if {($last_value eq ",") || ($last_value eq "\n") || ($last_value eq "\t")} {
317
+ $w.txt delete {insert - 1 chars} insert
318
+ handle_state $w 1
319
+ }
320
+
321
+ # Reset the modified flag
322
+ $w.txt edit modified false
323
+
324
+ # Generate the TokenEntryModified event if our token count has changed
325
+ set tokens [llength [$w.txt window names]]
326
+ if {$token_count($w) != $tokens} {
327
+ set token_count($w) $tokens
328
+ if {$options($w,-tokenvar) ne ""} {
329
+ upvar #0 $options($w,-tokenvar) var
330
+ set var [get_tokens $w]
331
+ }
332
+ event generate $w <<TokenEntryModified>>
333
+ }
334
+
335
+ }
336
+
337
+ }
338
+
339
+ ###########################################################################
340
+ # If the selection of the text box changes, make sure that any selected
341
+ # tokens are updated appropriately.
342
+ proc handle_selection_change {w} {
343
+
344
+ # Don't allow the selection to contain tokens
345
+ foreach token [$w.txt window names] {
346
+ if {[lsearch [$w.txt tag names $token] sel] != -1} {
347
+ $w.txt tag remove sel $token
348
+ }
349
+ }
350
+
351
+ }
352
+
353
+ ###########################################################################
354
+ # Validation command.
355
+ proc validate {w str} {
356
+
357
+ variable options
358
+
359
+ if {$str eq ","} {
360
+ return 0
361
+ } elseif {$options($w,-validatecommand) ne ""} {
362
+ return [eval $options($w,-validatecommand) $str]
363
+ } else {
364
+ return 1
365
+ }
366
+
367
+ }
368
+
369
+ ###########################################################################
370
+ # Handles a left or right arrow key event.
371
+ proc key_left_right {w dir {token ""}} {
372
+
373
+ variable options
374
+ variable active_token
375
+
376
+ if {$token eq ""} {
377
+
378
+ # Don't do anything if the current insertion cursor is the beginning or end
379
+ if {(([$w.txt index insert] ne "1.0") || ($dir eq "right")) && \
380
+ (([$w.txt index insert] ne [$w.txt index end]) || ($dir eq "left"))} {
381
+
382
+ # Get the current insertion index
383
+ if {$dir eq "left"} {
384
+ set index [$w.txt index "insert - 1 chars"]
385
+ } else {
386
+ set index [$w.txt index insert]
387
+ }
388
+
389
+ # If a token exists at the given index, select it.
390
+ foreach token [$w.txt window names] {
391
+ if {[$w.txt index $token] eq $index} {
392
+ reverse_token $w $token
393
+ set active_token($w) $index
394
+ focus $token
395
+ return
396
+ }
397
+ }
398
+
399
+ }
400
+
401
+ } else {
402
+
403
+ # Clear the active token
404
+ set active_token($w) ""
405
+
406
+ # Deselect the token
407
+ reverse_token $w $token
408
+
409
+ # Close the dropdown listbox if it is opened
410
+ close_dropdown $w
411
+
412
+ # If the direction was a positive direction, increase the insertion cursor by one character
413
+ if {($dir eq "right") && ([$w.txt index $token] eq [$w.txt index insert])} {
414
+ $w.txt mark set insert "insert + 1 chars"
415
+ }
416
+
417
+ # Get the text entry field the focus
418
+ focus $w.txt
419
+
420
+ }
421
+
422
+ }
423
+
424
+ ###########################################################################
425
+ # This procedure is invoked when the user hits the down key when the text
426
+ # has the focus.
427
+ proc key_down {w {token ""}} {
428
+
429
+ if {[winfo ismapped $w.top]} {
430
+ tk::ListboxUpDown $w.top.list 1
431
+ return -code break
432
+ } elseif {$token ne ""} {
433
+ display_dropdown_items $w $token
434
+ }
435
+
436
+ }
437
+
438
+ ###########################################################################
439
+ # This procedure is invoked when the user hits the up key when the text
440
+ # has the focus.
441
+ proc key_up {w {token ""}} {
442
+
443
+ if {[winfo ismapped $w.top]} {
444
+ tk::ListboxUpDown $w.top.list -1
445
+ return -code break
446
+ } elseif {$token ne ""} {
447
+ close_dropdown $w
448
+ }
449
+
450
+ }
451
+
452
+ ###########################################################################
453
+ # This procedure is invoked when the user hits the return key when the
454
+ # text has the focus.
455
+ proc key_return {w {token ""}} {
456
+
457
+ variable dropdown_token
458
+ variable options
459
+ variable current_matches
460
+ variable dont_tokenize
461
+
462
+ # Allow the tokenization to occur
463
+ set dont_tokenize($w) 0
464
+
465
+ # If the dropdown window is shown, get the currently selected text and insert it into the textbox.
466
+ if {[winfo ismapped $w.top]} {
467
+
468
+ upvar #0 $options($w,-listvar) listvar
469
+
470
+ # Get the currently selected value
471
+ set value [lindex $listvar [lindex $current_matches($w) [$w.top.list curselection]] $options($w,-matchdisplayindex)]
472
+
473
+ # Figure out the position of the first character of text
474
+ set curr_index 1.0
475
+ set end_index [$w.txt index end]
476
+ while {($curr_index != $end_index) && ([$w.txt get $curr_index] eq "")} {
477
+ set curr_index [$w.txt index "$curr_index + 1 chars"]
478
+ }
479
+
480
+ # If the result is associated with a token, change the token text
481
+ if {$dropdown_token($w) ne ""} {
482
+ $dropdown_token($w).l1 configure -text $value
483
+ redraw_token $w $dropdown_token($w) 1
484
+ if {$options($w,-tokenvar) ne ""} {
485
+ upvar #0 $options($w,-tokenvar) var
486
+ set var [get_tokens $w]
487
+ }
488
+ event generate $w <<TokenEntryModified>>
489
+
490
+ # Otherwise, remove the current text and replace it with the given value
491
+ } else {
492
+ $w.txt delete $curr_index "$curr_index + [expr [string length $value] + 1] chars"
493
+ $w.txt insert $curr_index $value
494
+ tokenize $w
495
+ }
496
+
497
+ # Close the dropbox
498
+ close_dropdown $w
499
+
500
+ # If this return was hit for a token, detokenize the current token to make it editable
501
+ } elseif {$token ne ""} {
502
+
503
+ detokenize $w $token
504
+
505
+ } else {
506
+
507
+ # Tokenize the text
508
+ tokenize $w
509
+
510
+ }
511
+
512
+ }
513
+
514
+ ###########################################################################
515
+ # This procedure is called whenever the escape key is pressed when a token
516
+ # has the focus. This will cause the dropdown listbox to be closed if
517
+ # it is currently opened.
518
+ proc key_escape {w token} {
519
+
520
+ # Just close the dropdown listbox
521
+ close_dropdown $w
522
+
523
+ }
524
+
525
+ ###########################################################################
526
+ # This procedure is called whenever the user presses a key in the text box.
527
+ proc keypress {w} {
528
+
529
+ # Update the current state
530
+ handle_state $w 1
531
+
532
+ after idle [list tokenentry::handle_entry_key $w]
533
+
534
+ # Clear the listbox selection so that it's obvious what will happen if the user
535
+ # presses return.
536
+ $w.top.list see 0
537
+ $w.top.list selection clear 0 end
538
+ $w.top.list selection anchor 0
539
+ $w.top.list activate 0
540
+
541
+ }
542
+
543
+ ###########################################################################
544
+ # Populates and shows the listbox with the matching values. If there are no
545
+ # matching values, the listbox is closed.
546
+ proc handle_entry_key {w} {
547
+
548
+ variable options
549
+ variable current_matches
550
+ variable dont_tokenize
551
+
552
+ # Handle the current state
553
+ handle_state $w 1
554
+
555
+ # Make sure that we don't tokenize
556
+ set dont_tokenize($w) 1
557
+
558
+ # Get rid of any whitespace from around the value
559
+ set value [string trim [$w.txt get 1.0 end]]
560
+
561
+ # Clear the listbox
562
+ $w.top.list delete 0 end
563
+
564
+ # Populate the listbox with matching values
565
+ if {$value ne ""} {
566
+ if {$options($w,-listvar) ne ""} {
567
+ upvar #0 $options($w,-listvar) listvar
568
+ set cmdargs [list]
569
+ switch $options($w,-matchmode) {
570
+ glob {
571
+ lappend cmdargs "-glob"
572
+ set matchval "*$value*"
573
+ }
574
+ regexp {
575
+ lappend cmdargs "-regexp"
576
+ set matchval ".*$value.*"
577
+ }
578
+ default {
579
+ lappend cmdargs "-glob"
580
+ set matchval "*$value*"
581
+ }
582
+ }
583
+ if {!$options($w,-matchcase)} {
584
+ lappend cmdargs "-nocase"
585
+ }
586
+ if {[llength $options($w,-matchindex)] > 0} {
587
+ lappend cmdargs "-index" "$options($w,-matchindex)"
588
+ }
589
+ lappend cmdargs "-all"
590
+ foreach matchindex [set current_matches($w) [eval "lsearch $cmdargs {$listvar} {$matchval}"]] {
591
+ set match [eval "lindex {$listvar} $matchindex $options($w,-matchindex)"]
592
+ $w.top.list insert end [eval "format {$options($w,-dropdownformatstring)} $match"]
593
+ }
594
+ }
595
+ }
596
+
597
+ # If the listbox is not empty, show it
598
+ if {[$w.top.list size] > 0} {
599
+ open_dropdown $w
600
+ $w.top.list activate 0
601
+ $w.top.list selection set 0
602
+
603
+ } else {
604
+ close_dropdown $w
605
+ }
606
+
607
+ }
608
+
609
+ ###########################################################################
610
+ # Handles any sort of movement of the insertion cursor or selection within
611
+ # the text widget.
612
+ proc handle_text_movement {w} {
613
+
614
+ variable state
615
+
616
+ # If we are empty, always set the insertion cursor to 1.0
617
+ if {$state($w) eq "empty"} {
618
+ $w.txt mark set insert 1.0
619
+ $w.txt tag remove sel 1.0 end
620
+ focus $w.txt
621
+ return 1
622
+ }
623
+
624
+ return 0
625
+
626
+ }
627
+
628
+ ###########################################################################
629
+ # Handles a Control-x binding on the given widget.
630
+ proc handle_cut {w} {
631
+
632
+ if {[focus] eq $w} {
633
+ set select [$w tag ranges sel]
634
+ if {[llength $select] == 0} {
635
+ clipboard clear
636
+ clipboard append [$w.txt get 1.0 end]
637
+ # TBD - Need to delete only text
638
+ eval "$w.txt delete 1.0 end"
639
+ handle_state $w 1
640
+ } else {
641
+ clipboard clear
642
+ clipboard append [eval "$w.txt get $select"]
643
+ eval "$w.txt delete $select"
644
+ handle_state $w 1
645
+ }
646
+ } else {
647
+ clipboard clear
648
+ clipboard append [[focus].l1 cget -text]
649
+ eval "tokendelete $w $select"
650
+ }
651
+
652
+ }
653
+
654
+ ###########################################################################
655
+ # Handles a Control-c binding on the given widget.
656
+ proc handle_copy {w} {
657
+
658
+ if {[focus] eq $w} {
659
+ set select [$w.txt tag ranges sel]
660
+ if {[llength $select] == 0} {
661
+ clipboard clear
662
+ clipboard append [$w.txt get 1.0 end]
663
+ } else {
664
+ clipboard clear
665
+ clipboard append [eval "$w.txt get $select"]
666
+ }
667
+ } else {
668
+ clipboard clear
669
+ clipboard append [[focus].l1 cget -text]
670
+ }
671
+
672
+ }
673
+
674
+ ###########################################################################
675
+ # Handles a Control-v binding on the given widget.
676
+ proc handle_paste {w} {
677
+
678
+ # Handle the current state
679
+ handle_state $w 1
680
+
681
+ # Insert the clipboard text
682
+ $w.txt insert insert [clipboard get]
683
+
684
+ # Close the drop-down listbox
685
+ close_dropdown $w
686
+
687
+ }
688
+
689
+ ###########################################################################
690
+ # Redraws the given token.
691
+ proc redraw_token {w token resize} {
692
+
693
+ variable options
694
+ variable images
695
+ variable token_shapes
696
+
697
+ # Get the border color from the token
698
+ set usebc [$token.l2.top cget -bg]
699
+ set txt_bg [$w.txt cget -background]
700
+
701
+ # Figure out the width and height of the token text label
702
+ if {$resize} {
703
+ update idletasks
704
+ }
705
+ set l1_width [winfo reqwidth $token.l1]
706
+ set l1_height [winfo reqheight $token.l1]
707
+
708
+ # Get the needed shapes
709
+ set shape_left [lindex $token_shapes($token) 0]
710
+ set shape_right [lindex $token_shapes($token) end]
711
+
712
+ # Create the token images, if necessary
713
+ if {![info exists images($w,left,$l1_height,$usebc,$txt_bg,$shape_left)]} {
714
+ set images($w,left,$l1_height,$usebc,$txt_bg,$shape_left) [image create bitmap -data [eval "tokenframe::create_left $shape_left $l1_height"] -maskdata [eval "tokenframe::create_left_mask $shape_left $l1_height"] -foreground $usebc -background $txt_bg]
715
+ }
716
+ if {![info exists images($w,edge,$usebc)]} {
717
+ set images($w,edge,$usebc) [image create bitmap -data "#define edge_width 7\n#define edge_height 2\nstatic char edge_bits\[\] = {\n0x7f, 0x7f};" -foreground $usebc]
718
+ }
719
+ if {![info exists images($w,middle,$l1_width,$l1_height,$usebc)]} {
720
+ set images($w,middle,$l1_width,$l1_height,$usebc) [image create bitmap -data [tokenframe::create_middle $l1_width $l1_height] -foreground $usebc]
721
+ }
722
+ if {![info exists images($w,$l1_height,$usebc,$txt_bg,$shape_right)]} {
723
+ set images($w,right,$l1_height,$usebc,$txt_bg,$shape_right) [image create bitmap -data [eval "tokenframe::create_right $shape_right $l1_height"] -maskdata [eval "tokenframe::create_right_mask $shape_right $l1_height"] -foreground $usebc -background $txt_bg]
724
+ }
725
+
726
+ # Configure the label images
727
+ $token.ll configure -padx 0 -pady 0 -compound center -image $images($w,left,$l1_height,$usebc,$txt_bg,$shape_left)
728
+ $token.l1 configure -padx 0 -pady 0 -compound center -image $images($w,middle,$l1_width,$l1_height,$usebc)
729
+ $token.l2.top configure -padx 0 -pady 0 -compound center -image $images($w,edge,$usebc)
730
+ $token.l2.bot configure -padx 0 -pady 0 -compound center -image $images($w,edge,$usebc)
731
+ $token.lr configure -padx 0 -pady 0 -compound center -image $images($w,right,$l1_height,$usebc,$txt_bg,$shape_right)
732
+
733
+ }
734
+
735
+ ###########################################################################
736
+ # Reverses the color scheme of the given token.
737
+ proc reverse_token {w token} {
738
+
739
+ # Get the current colors
740
+ set a_bg [$token.l1 cget -bg]
741
+ set a_fg [$token.l1 cget -fg]
742
+ set a_bc [$token.l2.top cget -bg]
743
+ set b_bg [$token.ll cget -fg]
744
+ set b_fg [$token.lr cget -fg]
745
+ set b_bc [$token.l2.mid cget -fg]
746
+
747
+ # Reverse the color schemes
748
+ $token.ll configure -bg $b_bg -fg $a_bg
749
+ $token.l1 configure -bg $b_bg -fg $b_fg
750
+ $token.l2.top configure -bg $b_bc
751
+ $token.l2.mid configure -bg $b_bg -fg $a_bc
752
+ $token.l2.bot configure -bg $b_bc
753
+ $token.lr configure -bg $b_bg -fg $a_fg
754
+
755
+ # Redraw the token
756
+ redraw_token $w $token 0
757
+
758
+ }
759
+
760
+ ###########################################################################
761
+ # Creates a token and inserts it into the textbox.
762
+ proc create_token {w index fg bg bordercolor selectfg selectbg selectbordercolor value} {
763
+
764
+ variable options
765
+ variable token_index
766
+ variable img_blank
767
+ variable token_shapes
768
+
769
+ # Add the token (store the "store" colors and the select background color in unused slots)
770
+ set token [frame $w.txt.f$token_index($w) -relief flat]
771
+ label $token.ll -bd 0 -bg $bg -fg $selectbg
772
+ label $token.l1 -bd 0 -text $value -fg $fg -bg $bg -font [$w.txt cget -font]
773
+ frame $token.l2 -bg $bg
774
+ label $token.l2.top -bd 0 -bg $bordercolor -fg $selectbg
775
+ label $token.l2.mid -bd 0 -bg $bg -fg $selectbordercolor -image $img_blank
776
+ label $token.l2.bot -bd 0 -bg $bordercolor
777
+ label $token.lr -bd 0 -bg $bg -fg $selectfg
778
+
779
+ set token_shapes($token) $options($w,-tokenshape)
780
+
781
+ # Create the token frames
782
+ redraw_token $w $token 1
783
+
784
+ # Pack the labels
785
+ pack $token.l2.top -anchor n
786
+ pack $token.l2.mid -fill y -expand yes
787
+ pack $token.l2.bot -anchor s
788
+
789
+ pack $token.ll -side left
790
+ pack $token.l1 -side left
791
+ pack $token.l2 -side left -fill both
792
+ pack $token.lr -side left
793
+ $w.txt window create $index -window $token -padx 2
794
+
795
+ # Add bindings to the new token
796
+ bind $token <FocusOut> "tokenentry::deselect_token $w $token"
797
+ bind $token.l1 <ButtonPress-1> "tokenentry::handle_token_press $w $token %x %y"
798
+ bind $token.l1 <Motion> "tokenentry::handle_token_drag $w %x %y"
799
+ bind $token.l1 <ButtonRelease-1> "tokenentry::handle_token_release $w $token %x %y"
800
+ bind $token.l2.mid <Button-1> "tokenentry::handle_arrow_click $w $token %x %y"
801
+ bind $token <Enter> "tokenentry::handle_token_enter $w $token"
802
+ bind $token <Leave> "tokenentry::handle_token_leave $w $token"
803
+ bind $token <BackSpace> "tokenentry::delete_token $w $token"
804
+ bind $token <Left> "tokenentry::key_left_right $w left $token"
805
+ bind $token <Right> "tokenentry::key_left_right $w right $token"
806
+ bind $token <Down> "tokenentry::key_down $w $token"
807
+ bind $token <Up> "tokenentry::key_up $w $token"
808
+ bind $token <Return> "tokenentry::key_return $w $token"
809
+ bind $token <Escape> "tokenentry::key_escape $w $token"
810
+ if {[tk windowingsystem] eq "aqua"} {
811
+ bind $token <Command-x> "tokenentry::handle_cut $w"
812
+ bind $token <Command-c> "tokenentry::handle_copy $w"
813
+ } else {
814
+ bind $token <Control-x> "tokenentry::handle_cut $w"
815
+ bind $token <Control-c> "tokenentry::handle_copy $w"
816
+ }
817
+
818
+ incr token_index($w)
819
+
820
+ return $token
821
+
822
+ }
823
+
824
+ ###########################################################################
825
+ # Returns the text window position given an index.
826
+ proc index_to_position {w index} {
827
+
828
+ set indices [list]
829
+ foreach token [$w.txt window names] {
830
+ lappend indices [$w.txt index $token]
831
+ }
832
+
833
+ return [lindex [lsort -real $indices] $index]
834
+
835
+ }
836
+
837
+ ###########################################################################
838
+ # Returns true if the given token is currently selected; otherwise, returns
839
+ # false.
840
+ proc is_selected {token} {
841
+
842
+ if {[$token.l2.top cget -fg] eq [$token.l1 cget -bg]} {
843
+ return 1
844
+ } else {
845
+ return 0
846
+ }
847
+
848
+ }
849
+
850
+ ###########################################################################
851
+ # Creates a token out of the given text, deletes the text.
852
+ proc tokenize {w} {
853
+
854
+ variable token_index
855
+ variable options
856
+ variable dont_tokenize
857
+ variable img_blank
858
+ variable state
859
+
860
+ # If we are told to not tokenize, clear the dont_tokenize value and be done
861
+ if {$dont_tokenize($w)} {
862
+ set dont_tokenize($w) 0
863
+ return
864
+ }
865
+
866
+ # If our current state is empty, be done
867
+ if {$state($w) eq "empty"} {
868
+ return
869
+ }
870
+
871
+ # Get the current string in the entry field
872
+ set token_str [string trim [$w.txt get 1.0 end]]
873
+
874
+ # Figure out the position of the first character of text
875
+ set curr_index 1.0
876
+ set end_index [$w.txt index end]
877
+ while {($curr_index != $end_index) && ([$w.txt get $curr_index] eq "")} {
878
+ set curr_index [$w.txt index "$curr_index + 1 chars"]
879
+ }
880
+
881
+ # Create and add the token
882
+ if {$token_str ne ""} {
883
+ create_token $w $curr_index $options($w,-tokenfg) $options($w,-tokenbg) $options($w,-tokenbordercolor) \
884
+ $options($w,-tokenselectfg) $options($w,-tokenselectbg) $options($w,-tokenselectbordercolor) $token_str
885
+ }
886
+
887
+ # Clear the text field
888
+ $w.txt delete "$curr_index + 1 chars" "$curr_index + [expr [string length $token_str] + 1] chars"
889
+
890
+ # Make sure that the insertion cursor is visible
891
+ $w.txt see 1.0
892
+ update
893
+ $w.txt see insert
894
+
895
+ # Finally, close the dropdown window if it is currently opened
896
+ close_dropdown $w
897
+
898
+ }
899
+
900
+ ###########################################################################
901
+ # Deletes the token and replaces it with the original text.
902
+ proc detokenize {w token} {
903
+
904
+ variable dont_tokenize
905
+ variable active_token
906
+
907
+ # Change the focus to the textbox prior to deleting the token to avoid
908
+ # having the token be tokenized immediately
909
+ focus $w.txt
910
+
911
+ # Get some information from the token (position and text)
912
+ set token_pos [$w.txt index $token]
913
+ set token_text [$token.l1 cget -text]
914
+
915
+ # Delete the token
916
+ $w.txt delete $token
917
+
918
+ # Insert the label text
919
+ $w.txt insert $token_pos $token_text
920
+
921
+ # Set the selection to the inserted text
922
+ $w.txt tag add sel $token_pos "$token_pos + [string length $token_text] chars"
923
+
924
+ # Set the insertion cursor to the end of the text
925
+ $w.txt mark set insert "$token_pos + [string length $token_text] chars"
926
+
927
+ # Make sure that we don't tokenize this string
928
+ set dont_tokenize($w) 1
929
+
930
+ # Clear the active token
931
+ set active_token($w) ""
932
+
933
+ }
934
+
935
+ ###########################################################################
936
+ # Removes a token from the entry field.
937
+ proc delete_token {w token} {
938
+
939
+ variable options
940
+ variable active_token
941
+
942
+ set last_pos ""
943
+
944
+ foreach token [$w.txt window names] {
945
+ if {[$token.l1 cget -bg] eq $options($w,-tokenselectbg)} {
946
+ set last_pos [$w.txt index $token]
947
+ $w.txt delete $token
948
+ handle_state $w 1
949
+ }
950
+ }
951
+
952
+ # Set the insertion cursor to the end
953
+ if {$last_pos ne ""} {
954
+ $w.txt mark set insert $last_pos
955
+ }
956
+
957
+ # Clear the active token
958
+ set active_token($w) ""
959
+
960
+ # Set the focus back to the entry
961
+ focus $w.txt
962
+
963
+ }
964
+
965
+ ###########################################################################
966
+ # This procedure is called when the text token receives the focus. It
967
+ # deselects any currently selected tokens.
968
+ proc deselect_token {w token} {
969
+
970
+ variable options
971
+ variable img_blank
972
+ variable active_token
973
+
974
+ # If we are disabled, do nothing
975
+ if {$options($w,-state) eq "disabled"} {
976
+ return
977
+ }
978
+
979
+ # If we are selected, reverse the token
980
+ if {[is_selected $token]} {
981
+ reverse_token $w $token
982
+ }
983
+
984
+ # Clear the active token
985
+ set active_token($w) ""
986
+
987
+ # Clear the arrow image
988
+ $token.l2.mid configure -image $img_blank
989
+
990
+ }
991
+
992
+ ###########################################################################
993
+ # This procedure is called whenever a token is left-pressed. It allows a
994
+ # drag and drop option to move the token.
995
+ proc handle_token_press {w token x y} {
996
+
997
+ variable pressed_token
998
+ variable options
999
+ variable dont_tokenize
1000
+
1001
+ # If we are disabled, do nothing
1002
+ if {$options($w,-state) eq "disabled"} {
1003
+ return
1004
+ }
1005
+
1006
+ # Save the pressed token
1007
+ set pressed_token($w) [list $token $x]
1008
+
1009
+ # Close the dropdown list if it is opened
1010
+ close_dropdown $w
1011
+
1012
+ # If there is anything that needs to be tokenized, do it now
1013
+ set dont_tokenize($w) 0
1014
+ tokenize $w
1015
+
1016
+ }
1017
+
1018
+ ###########################################################################
1019
+ # This procedure is called whenever a token is moved.
1020
+ proc handle_token_drag {w x y} {
1021
+
1022
+ variable pressed_token
1023
+
1024
+ if {$pressed_token($w) ne ""} {
1025
+
1026
+ # Make sure that the text widget has the focus
1027
+ focus $w.txt
1028
+
1029
+ # Change the cursor to a hand
1030
+ [lindex $pressed_token($w) 0] configure -cursor left_side
1031
+
1032
+ set index [$w.txt index @[expr [winfo x [lindex $pressed_token($w) 0]] + $x + 8],$y]
1033
+
1034
+ # Get the current location and set the insertion cursor
1035
+ if {$x < [lindex $pressed_token($w) 1]} {
1036
+ $w.txt mark set insert $index
1037
+ } else {
1038
+ $w.txt mark set insert "$index + 1 chars"
1039
+ }
1040
+
1041
+ }
1042
+
1043
+ }
1044
+
1045
+ ###########################################################################
1046
+ # This procedure is called whenever a token is left-clicked. It changes
1047
+ # the state of the token.
1048
+ proc handle_token_release {w token x y} {
1049
+
1050
+ variable options
1051
+ variable pressed_token
1052
+ variable active_token
1053
+
1054
+ # If we are disabled, stop now.
1055
+ if {$options($w,-state) eq "disabled"} {
1056
+ return
1057
+ }
1058
+
1059
+ set start_index [$w.txt index $token]
1060
+ set end_index [$w.txt index @[expr [winfo x [lindex $pressed_token($w) 0]] + $x + 8],$y]
1061
+
1062
+ # If the token was not moved, treat the click as a selection/detokenization
1063
+ if {$start_index == $end_index} {
1064
+
1065
+ # If the token is currently selected, detokenize the selection
1066
+ if {$active_token($w) == [$w.txt index $token]} {
1067
+
1068
+ detokenize $w $token
1069
+
1070
+ # Clear the pressed token
1071
+ set pressed_token($w) ""
1072
+
1073
+ return
1074
+
1075
+ } else {
1076
+
1077
+ # Reverse the color scheme
1078
+ reverse_token $w $token
1079
+
1080
+ # Set the active token
1081
+ set active_token($w) [$w.txt index $token]
1082
+
1083
+ # Make sure that the current token keeps the focus
1084
+ focus $token
1085
+
1086
+ # Generate the TokenEntrySelected event
1087
+ event generate $w <<TokenEntrySelected>>
1088
+
1089
+ }
1090
+
1091
+ # Otherwise, the token has been dragged to a new position -- delete it and recreate it in the new position
1092
+ } else {
1093
+
1094
+ # Move the window to the new position
1095
+ if {$x < [lindex $pressed_token($w) 1]} {
1096
+ $w.txt window create $end_index -window $token -padx 2
1097
+ } else {
1098
+ $w.txt window create "$end_index + 1 chars" -window $token -padx 2
1099
+ }
1100
+
1101
+ # Delete the previous position if the starting position is less than the ending position
1102
+ if {$start_index < $end_index} {
1103
+ $w.txt delete $start_index
1104
+ }
1105
+
1106
+ # Update the tokenvar variable, if it has been set
1107
+ if {$options($w,-tokenvar) ne ""} {
1108
+ upvar #0 $options($w,-tokenvar) var
1109
+ set var [get_tokens $w]
1110
+ }
1111
+
1112
+ # Generate a TokenEntryModified event
1113
+ event generate $w <<TokenEntryModified>>
1114
+
1115
+ }
1116
+
1117
+ # Change cursor on pressed token to arrow
1118
+ [lindex $pressed_token($w) 0] configure -cursor top_left_arrow
1119
+
1120
+ # Clear the pressed token
1121
+ set pressed_token($w) ""
1122
+
1123
+ }
1124
+
1125
+ ###########################################################################
1126
+ # Populates the dropdown listbox with the items from the -listvar list
1127
+ # and displays the listbox.
1128
+ proc display_dropdown_items {w token} {
1129
+
1130
+ variable options
1131
+ variable current_matches
1132
+
1133
+ if {[info exists options($w,-listvar)] && ($options($w,-listvar) ne "")} {
1134
+
1135
+ upvar #0 $options($w,-listvar) listvar
1136
+
1137
+ # Remove all of the items from the dropdown list
1138
+ $w.top.list delete 0 end
1139
+
1140
+ # Populate the dropdown list with the list of items from listvar
1141
+ set current_matches($w) [list]
1142
+ foreach value $listvar {
1143
+ lappend current_matches($w) [$w.top.list size]
1144
+ if {$options($w,-matchindex) ne ""} {
1145
+ set value [lindex $value $options($w,-matchindex)]
1146
+ }
1147
+ $w.top.list insert end [eval "format {$options($w,-dropdownformatstring)} $value"]
1148
+ }
1149
+
1150
+ # Activate and select the first item in the list
1151
+ $w.top.list activate 0
1152
+ $w.top.list selection set 0
1153
+
1154
+ # Show the dropdown list
1155
+ open_dropdown $w $token
1156
+
1157
+ }
1158
+
1159
+ }
1160
+
1161
+ ###########################################################################
1162
+ # This procedure is called whenever an arrow token is left-clicked. It changes
1163
+ # the state of the token.
1164
+ proc handle_arrow_click {w token x y} {
1165
+
1166
+ variable active_token
1167
+ variable options
1168
+ variable img_blank
1169
+ variable current_matches
1170
+
1171
+ # If we are disabled, stop now.
1172
+ if {$options($w,-state) eq "disabled"} {
1173
+ return
1174
+ }
1175
+
1176
+ if {[$token.l2.mid cget -image] eq $img_blank} {
1177
+ handle_token_press $w $token $x $y
1178
+ handle_token_release $w $token $x $y
1179
+ } else {
1180
+ display_dropdown_items $w $token
1181
+ }
1182
+
1183
+ }
1184
+
1185
+ ###########################################################################
1186
+ # This procedirue is called when the cursor enters the arrow area.
1187
+ proc handle_token_enter {w token} {
1188
+
1189
+ variable options
1190
+ variable img_arrow
1191
+
1192
+ # If we are disabled, do nothing
1193
+ if {$options($w,-state) eq "disabled"} {
1194
+ return
1195
+ }
1196
+
1197
+ # Draw an arrow if we have an associated listvar and it is not empty
1198
+ if {$options($w,-listvar) ne ""} {
1199
+ upvar #0 $options($w,-listvar) listvar
1200
+ if {[llength $listvar] > 0} {
1201
+ $token.l2.mid configure -image $img_arrow
1202
+ }
1203
+ }
1204
+
1205
+ # Change the cursor
1206
+ $token configure -cursor top_left_arrow
1207
+
1208
+ }
1209
+
1210
+ ###########################################################################
1211
+ # This procedure is called when the cursor leaves the area for the arrow.
1212
+ proc handle_token_leave {w token} {
1213
+
1214
+ variable options
1215
+ variable img_arrow
1216
+ variable img_blank
1217
+
1218
+ # If we are disabled, do nothing
1219
+ if {$options($w,-state) eq "disabled"} {
1220
+ return
1221
+ }
1222
+
1223
+ if {([$token.l2.mid cget -image] eq $img_arrow) && ([$token.l2.mid cget -bg] eq $options($w,-tokenbg))} {
1224
+ $token.l2.mid configure -image $img_blank
1225
+ }
1226
+
1227
+ }
1228
+
1229
+ ###########################################################################
1230
+ # Calculates the geometry of the given window.
1231
+ proc compute_geometry {w} {
1232
+
1233
+ variable options
1234
+
1235
+ if {($options($w,-dropdownheight) == 0) && ($options($w,-dropdownmaxheight) != 0)} {
1236
+ set nitems [$w.top.list size]
1237
+ if {$nitems > $options($w,-dropdownmaxheight)} {
1238
+ $w.top.list configure -height $options($w,-dropdownmaxheight)
1239
+ } else {
1240
+ $w.top.list configure -height 0
1241
+ }
1242
+ update idletasks
1243
+ }
1244
+
1245
+ # Compute the height and width of the dropdown list
1246
+ set bd [$w.top cget -borderwidth]
1247
+ set height [expr [winfo reqheight $w.top] + $bd + $bd]
1248
+ set width [winfo width $w]
1249
+
1250
+ # Figure out where to place it on the screen
1251
+ set screen_width [winfo screenwidth $w]
1252
+ set screen_height [winfo screenheight $w]
1253
+ set rootx [winfo rootx $w]
1254
+ set rooty [winfo rooty $w]
1255
+ set vrootx [winfo vrootx $w]
1256
+ set vrooty [winfo vrooty $w]
1257
+
1258
+ set x [expr $rootx + $vrootx]
1259
+ set y [expr $rooty + $vrooty + [winfo reqheight $w] + 1]
1260
+ set bottom_edge [expr $y + $height]
1261
+
1262
+ # If it extends beyond our screen, trim the list and add a scrollbar
1263
+ if {$bottom_edge >= $screen_height} {
1264
+ set y [expr ($rooty - $height - 1) + $vrooty]
1265
+ if {$y < 0} {
1266
+ if {$rooty > [expr $screen_height / 2]} {
1267
+ set y 1
1268
+ set height [expr $rooty - 1 - $y]
1269
+ } else {
1270
+ set y [expr $rooty + $vrooty + [winfo reqheight $w] + 1]
1271
+ set height [expr $screen_height - $y]
1272
+ }
1273
+ handle_scrollbar $w crop
1274
+ }
1275
+ }
1276
+
1277
+ if {$y < 0} {
1278
+ set y 0
1279
+ set height $screen_height
1280
+ }
1281
+
1282
+ set geometry [format "=%dx%d+%d+%d" $width $height $x $y]
1283
+
1284
+ return $geometry
1285
+
1286
+ }
1287
+
1288
+ ###########################################################################
1289
+ # Hides/Displays the scrollbar in the dropdown listbox.
1290
+ proc handle_scrollbar {w {action "unknown"}} {
1291
+
1292
+ variable options
1293
+
1294
+ if {$options($w,-dropdownheight) == 0} {
1295
+ set hlimit $options($w,-dropdownmaxheight)
1296
+ } else {
1297
+ set hlimit $options($w,-dropdownheight)
1298
+ }
1299
+
1300
+ switch $action {
1301
+ "grow" {
1302
+ if {($hlimit > 0) && ([$w.top.list size] > $hlimit)} {
1303
+ pack forget $w.top.list
1304
+ pack $w.top.vsb -side right -fill y -expand n
1305
+ pack $w.top.list -side left -fill both -expand y
1306
+ }
1307
+ }
1308
+ "shrink" {
1309
+ if {($hlimit > 0) && ([$w.top.list size] <= $hlimit)} {
1310
+ pack forget $w.top.vsb
1311
+ }
1312
+ }
1313
+ "crop" {
1314
+ pack forget $w.top.list
1315
+ pack $w.top.vsb -side right -fill y -expand n
1316
+ pack $w.top.list -side left -fill both -expand y
1317
+ }
1318
+ default {
1319
+ if {($hlimit > 0) && ([$w.top.list size] > $hlimit)} {
1320
+ pack forget $w.top.list
1321
+ pack $w.top.vsb -side right -fill y -expand n
1322
+ pack $w.top.list -side left -fill both -expand y
1323
+ } else {
1324
+ pack forget $w.top.vsb
1325
+ }
1326
+ }
1327
+ }
1328
+
1329
+ return ""
1330
+
1331
+ }
1332
+
1333
+ ###########################################################################
1334
+ # This procedure is invoked by various events and displays the dropdown
1335
+ # listbox containing a list of selectable values.
1336
+ proc open_dropdown {w {token ""}} {
1337
+
1338
+ variable options
1339
+ variable old_focus
1340
+ variable old_grab
1341
+ variable dropdown_token
1342
+
1343
+ # If the user has not provided any values to display, skip opening the window
1344
+ if {$options($w,-listvar) ne ""} {
1345
+ upvar #0 $options($w,-listvar) listvar
1346
+ if {[llength $listvar] == 0} {
1347
+ return 0
1348
+ }
1349
+ }
1350
+
1351
+ # Update the scrollbar appropriately
1352
+ handle_scrollbar $w
1353
+
1354
+ # Compute the geometry of the window to pop up, set it, and force the window manager to
1355
+ # take notice
1356
+ set geometry [compute_geometry $w]
1357
+ wm geometry $w.top $geometry
1358
+ update idletasks
1359
+
1360
+ # If we are already open, stop
1361
+ if {[winfo ismapped $w.top]} {
1362
+ return 0
1363
+ }
1364
+
1365
+ # Set the reason
1366
+ set dropdown_token($w) $token
1367
+
1368
+ # Save the current focus
1369
+ set old_focus($w) [focus]
1370
+
1371
+ # Make the list pop up
1372
+ wm deiconify $w.top
1373
+ update idletasks
1374
+ raise $w.top
1375
+
1376
+ # Force the focus so we can handle keypress events for traversal
1377
+ if {$token eq ""} {
1378
+ focus -force $w.txt
1379
+ } else {
1380
+ focus -force $token
1381
+ }
1382
+
1383
+ # Save the current grab state
1384
+ set status "none"
1385
+ set grab [grab current $w]
1386
+ if {$grab != ""} {
1387
+ set status [grab status $grab]
1388
+ }
1389
+ set old_grab($w) [list $grab $status]
1390
+ unset grab status
1391
+
1392
+ grab -global $w
1393
+
1394
+ # Fake the listbox into thinking it has focus.
1395
+ event generate $w.top.list <B1-Enter>
1396
+
1397
+ return 1
1398
+
1399
+ }
1400
+
1401
+ ###########################################################################
1402
+ # This procedure is invoked when the user hits the Escape key or makes a
1403
+ # a listbox selection. It removes the dropdown listbox and returns the focus
1404
+ # to the text box.
1405
+ proc close_dropdown {w} {
1406
+
1407
+ variable old_focus
1408
+ variable old_grab
1409
+ variable dropdown_token
1410
+
1411
+ # If the window is already unmapped, stop
1412
+ if {![winfo ismapped $w.top]} {
1413
+ return 0
1414
+ }
1415
+
1416
+ catch { focus $old_focus($w) } result
1417
+ catch { grab release $w }
1418
+ catch {
1419
+ set status [lindex $old_grab($w) 1]
1420
+ if {$status eq "global"} {
1421
+ grab -global [lindex $old_grab($w) 0]
1422
+ } elseif {$status eq "local"} {
1423
+ grab [lindex $old_grab($w) 0]
1424
+ }
1425
+ unset status
1426
+ }
1427
+
1428
+ # Clear the reason
1429
+ set dropdown_token($w) ""
1430
+
1431
+ # Hide the listbox
1432
+ wm withdraw $w.top
1433
+
1434
+ # Magic Tcl stuff (see tk.tcl in the distribution lib directory)
1435
+ tk::CancelRepeat
1436
+
1437
+ return 1
1438
+
1439
+ }
1440
+
1441
+ ###########################################################################
1442
+ # This is called whenever the cursor moves over the listbox.
1443
+ proc motion_dropdown {w x y} {
1444
+
1445
+ # Set the cursor
1446
+ $w.top.list configure -cursor ""
1447
+
1448
+ # Clear the selections
1449
+ $w.top.list selection clear 0 end
1450
+
1451
+ # Set the selection to the current index
1452
+ $w.top.list selection set @$x,$y
1453
+
1454
+ }
1455
+
1456
+ ###########################################################################
1457
+ # Handles the current state of the widget (empty/non-empty) and handles
1458
+ # any watermark display (or removal of the display).
1459
+ proc handle_state {w keyed} {
1460
+
1461
+ variable state
1462
+ variable options
1463
+
1464
+ # If we are in the empty state
1465
+ if {$state($w) eq "empty"} {
1466
+
1467
+ $w.txt delete 1.0 end
1468
+
1469
+ if {$keyed} {
1470
+ set state($w) "non-empty"
1471
+ $w.txt configure -foreground $options($w,-foreground)
1472
+ } else {
1473
+ $w.txt configure -foreground $options($w,-watermarkforeground)
1474
+ $w.txt insert end $options($w,-watermark)
1475
+ $w.txt mark set insert 1.0
1476
+ }
1477
+
1478
+ # Otherwise, we are in the not-empty state
1479
+ } elseif {$state($w) eq "non-empty"} {
1480
+
1481
+ # If the widget is empty, set the state to empty and fill it with the
1482
+ # empty string.
1483
+ if {([string trim [$w.txt get 1.0 end]] eq "") && ([llength [$w.txt window names]] == 0)} {
1484
+ set state($w) "empty"
1485
+ $w.txt configure -foreground $options($w,-watermarkforeground)
1486
+ $w.txt insert end $options($w,-watermark)
1487
+ $w.txt mark set insert 1.0
1488
+ }
1489
+
1490
+ }
1491
+
1492
+ }
1493
+
1494
+ ###########################################################################
1495
+ # Returns a sorted list of all of the token values.
1496
+ proc get_tokens {w} {
1497
+
1498
+ set tokens [list]
1499
+ set indices [list]
1500
+
1501
+ foreach token [$w.txt window names] {
1502
+ lappend indices [list [$w.txt index $token] $token]
1503
+ }
1504
+
1505
+ foreach index [lsort -real -index 0 $indices] {
1506
+ lappend tokens [[lindex $index 1].l1 cget -text]
1507
+ }
1508
+
1509
+ return $tokens
1510
+
1511
+ }
1512
+
1513
+ ###########################################################################
1514
+ # Converts an entry index to a text index.
1515
+ proc entry_to_text_index {w index} {
1516
+
1517
+ set offset ""
1518
+ if {[regexp {(.+)\s*\-\s*(\d+)$} $index -> index offset]} {
1519
+ set offset " - $offset chars"
1520
+ }
1521
+
1522
+ if {[string is integer $index]} {
1523
+ return "1.$index$offset"
1524
+ } elseif {$index eq "anchor"} {
1525
+ return -code error "Illegal tokenentry index ($index)"
1526
+ } elseif {$index eq "end"} {
1527
+ return "1.end$offset"
1528
+ } elseif {$index eq "insert"} {
1529
+ return "[$w.txt index insert]$offset"
1530
+ } elseif {$index eq "sel.first"} {
1531
+ return "[lindex [$w.txt tag ranges sel] 0]$offset"
1532
+ } elseif {$index eq "sel.last"} {
1533
+ return "[lindex [$w.txt tag ranges sel] 1]$offset"
1534
+ } else {
1535
+ return -code error "Illegal tokenentry index ($index)"
1536
+ }
1537
+
1538
+ }
1539
+
1540
+ ###########################################################################
1541
+ # Handles all commands.
1542
+ proc widget_cmd {w args} {
1543
+
1544
+ if {[llength $args] == 0} {
1545
+ return -code error "tokenentry widget called without a command"
1546
+ }
1547
+
1548
+ set cmd [lindex $args 0]
1549
+ set opts [lrange $args 1 end]
1550
+
1551
+ switch $cmd {
1552
+ configure { eval "tokenentry::configure 0 $w $opts" }
1553
+ cget { return [eval "tokenentry::cget $w $opts"] }
1554
+ tokenindex { return [eval "tokenentry::tokenindex $w $opts"] }
1555
+ tokenselection { eval "tokenentry::tokenselection $w $opts" }
1556
+ tokenget { return [eval "tokenentry::get_tokens $w"] }
1557
+ tokenconfigure { eval "tokenentry::tokenconfigure $w $opts" }
1558
+ tokencget { return [eval "tokenentry::tokencget $w $opts" }
1559
+ tokeninsert { eval "tokenentry::tokeninsert $w $opts" }
1560
+ tokendelete { eval "tokenentry::tokendelete $w $opts" }
1561
+ entryget { return [$w.txt get 1.0 end] }
1562
+ insert { return [eval "tokenentry::insert $w $opts"] }
1563
+ bbox { return [eval "tokenentry::bbox $w $opts"] }
1564
+ delete { eval "tokenentry::delete $w $opts" }
1565
+ get { return [eval "tokenentry::get $w $opts"] }
1566
+ icursor { eval "tokenentry::icursor $w $opts" }
1567
+ index { return [eval "tokenentry::index $w $opts"] }
1568
+ insert { eval "tokenentry::insert $w $opts" }
1569
+ scan { return [eval "tokenentry::scan $w $opts"] }
1570
+ selection { return [eval "tokenentry::selection $w $opts"] }
1571
+ validate { return [eval "tokenentry::validate $w $opts"] }
1572
+ xview { return [eval "tokenentry::xview $w $opts"] }
1573
+ default { return -code error "Unknown tokenentry command ($cmd)" }
1574
+ }
1575
+
1576
+ }
1577
+
1578
+ ###########################################################################
1579
+ # USER COMMANDS
1580
+ ###########################################################################
1581
+
1582
+ ###########################################################################
1583
+ # Main configuration routine.
1584
+ proc configure {initialize w args} {
1585
+
1586
+ variable options
1587
+ variable text_options
1588
+ variable widget_options
1589
+ variable state
1590
+
1591
+ if {([llength $args] == 0) && !$initialize} {
1592
+
1593
+ set results [list]
1594
+
1595
+ foreach opt [lsort [array names widget_options]] {
1596
+ if {[llength $widget_options($opt)] == 2} {
1597
+ set opt_name [lindex $widget_options($opt) 0]
1598
+ set opt_class [lindex $widget_options($opt) 1]
1599
+ set opt_default [option get $w $opt_name $opt_class]
1600
+ if {[info exists text_options($opt)]} {
1601
+ lappend results [list $opt $opt_name $opt_class $opt_default [$w.txt cget $opt]]
1602
+ } elseif {[info exists options($w,$opt)]} {
1603
+ lappend results [list $opt $opt_name $opt_class $opt_default $options($w,$opt)]
1604
+ } else {
1605
+ lappend results [list $opt $opt_name $opt_class $opt_default ""]
1606
+ }
1607
+ }
1608
+ }
1609
+
1610
+ return $results
1611
+
1612
+ } elseif {([llength $args] == 1) && !$initialize} {
1613
+
1614
+ set opt [lindex $args 0]
1615
+
1616
+ if {[info exists widget_options($opt)]} {
1617
+ if {[llength $widget_options($opt)] == 1} {
1618
+ set opt [lindex $widget_options($opt) 0]
1619
+ }
1620
+ set opt_name [lindex $widget_options($opt) 0]
1621
+ set opt_class [lindex $widget_options($opt) 1]
1622
+ set opt_default [option get $w $opt_name $opt_class]
1623
+ if {[info exists text_options($opt)]} {
1624
+ return [list $opt $opt_name $opt_class $opt_default [$w.txt cget $opt]]
1625
+ } elseif {[info exists options($w,$opt)]} {
1626
+ return [list $opt $opt_name $opt_class $opt_default $options($w,$opt)]
1627
+ } else {
1628
+ return [list $opt $opt_name $opt_class $opt_default ""]
1629
+ }
1630
+ }
1631
+
1632
+ return -code error "TokenEntry configuration option [lindex $args 0] does not exist"
1633
+
1634
+ } else {
1635
+
1636
+ # Save the original contents
1637
+ array set orig_options [array get options]
1638
+
1639
+ # Parse the arguments
1640
+ foreach {name value} $args {
1641
+ if {[info exists text_options($name)]} {
1642
+ $w.txt configure $name $value
1643
+ } elseif {[info exists options($w,$name)]} {
1644
+ set options($w,$name) $value
1645
+ } else {
1646
+ return -code error "Illegal option given to the tokenentry configure command ($name)"
1647
+ }
1648
+ }
1649
+
1650
+ # Update the GUI widgets
1651
+ # $w.txt configure -fg $options($w,-foreground) -bg $options($w,-background) \
1652
+ -relief $options($w,-relief) -state $options($w,-state)
1653
+ if {$options($w,-height) ne ""} {
1654
+ $w.txt configure -height $options($w,-height)
1655
+ }
1656
+ if {$options($w,-width) ne ""} {
1657
+ $w.txt configure -width $options($w,-width)
1658
+ }
1659
+
1660
+ if {[string is boolean $options($w,-wrap)]} {
1661
+ if {$options($w,-wrap)} {
1662
+ $w.txt configure -wrap word
1663
+ } else {
1664
+ $w.txt configure -wrap none
1665
+ }
1666
+ } else {
1667
+ set options($w,-wrap) $orig_options($w,-wrap)
1668
+ return -code error "Value for -wrap option is not a boolean value ($options($w,-wrap))"
1669
+ }
1670
+
1671
+ # If the textbox is empty, configure it for the watermark
1672
+ if {$options($w,-watermark) ne ""} {
1673
+ set state($w) "empty"
1674
+ }
1675
+ handle_state $w 0
1676
+
1677
+ if {($orig_options($w,-dropdownheight) ne $options($w,-dropdownheight)) || \
1678
+ ($orig_options($w,-dropdownmaxheight) ne $options($w,-dropdownmaxheight))} {
1679
+ handle_scrollbar $w
1680
+ }
1681
+
1682
+ # Update the tokens, if necessary
1683
+ if {($orig_options($w,-tokenbg) ne $options($w,-tokenbg)) || ($orig_options($w,-tokenfg) ne $options($w,-tokenfg)) || \
1684
+ ($orig_options($w,-tokenselectbg) ne $options($w,-tokenselectbg)) || ($orig_options($w,-tokenselectfg) ne $options($w,-tokenselectfg)) || \
1685
+ ($orig_options($w,-tokenshape) ne $options($w,-tokenshape))} {
1686
+ set token_num [llength [$w.txt window names]]
1687
+ for {set i 0} {$i < $token_num} {incr i} {
1688
+ tokenconfigure $w $i -bg $options($w,-tokenbg) -fg $options($w,-tokenfg) \
1689
+ -selectbg $options($w,-tokenselectbg) -selectfg $options($w,-tokenselectfg) \
1690
+ -shape $options($w,-tokenshape)
1691
+ }
1692
+ }
1693
+
1694
+ }
1695
+
1696
+ }
1697
+
1698
+ ###########################################################################
1699
+ # Gets configuration option value(s).
1700
+ proc cget {w args} {
1701
+
1702
+ variable options
1703
+ variable text_options
1704
+
1705
+ if {[llength $args] != 1} {
1706
+ return -code error "Incorrect number of parameters given to the tokenentry cget command"
1707
+ }
1708
+
1709
+ if {[info exists text_options([lindex $args 0])]} {
1710
+ return [$w.txt cget [lindex $args 0]]
1711
+ } elseif {[info exists options($w,[lindex $args 0])]} {
1712
+ return $options($w,[lindex $args 0])
1713
+ } else {
1714
+ return -code error "Illegal option given to the tokenentry cget command ([lindex $args 0])"
1715
+ }
1716
+
1717
+ }
1718
+
1719
+ ###########################################################################
1720
+ # Configures the token located at the given index.
1721
+ proc tokenconfigure {w args} {
1722
+
1723
+ variable token_shapes
1724
+
1725
+ if {[expr [llength $args] % 2] == 0} {
1726
+ return -code error "Incorrect number of parameters given to the tokenconfigure command"
1727
+ }
1728
+
1729
+ set index [index_to_position $w [lindex $args 0]]
1730
+
1731
+ # Retrieve the current token pathname
1732
+ set token [$w.txt window cget $index -window]
1733
+
1734
+ # Figure out if the current token is selected or not
1735
+ set selected [is_selected $token]
1736
+
1737
+ set redraw 0
1738
+ set resize 0
1739
+
1740
+ foreach {option value} [lrange $args 1 end] {
1741
+ switch $option {
1742
+ -bg -
1743
+ -background {
1744
+ if {$selected} {
1745
+ $token.ll configure -fg $value
1746
+ } else {
1747
+ $token.ll configure -bg $value
1748
+ $token.l1 configure -bg $value
1749
+ $token.l2 configure -bg $value
1750
+ $token.l2.mid configure -bg $value
1751
+ $token.lr configure -bg $value
1752
+ set redraw 1
1753
+ }
1754
+ }
1755
+ -fg -
1756
+ -foreground {
1757
+ if {$selected} {
1758
+ $token.lr configure -fg $value
1759
+ } else {
1760
+ $token.l1 configure -fg $value
1761
+ set redraw 1
1762
+ }
1763
+ }
1764
+ -bordercolor {
1765
+ if {$selected} {
1766
+ $token.l2.mid configure -fg $value
1767
+ } else {
1768
+ $token.l2.top configure -bg $value
1769
+ $token.l2.bot configure -bg $value
1770
+ set redraw 1
1771
+ }
1772
+ }
1773
+ -shape {
1774
+ if {([llength $value] < 0) || ([llength $value] > 2)} {
1775
+ return -code error "ERROR: Token -shape list must be contain either 1 or 2 values"
1776
+ }
1777
+ foreach val $value {
1778
+ switch $value {
1779
+ pill -
1780
+ tag -
1781
+ square -
1782
+ eased -
1783
+ ticket {}
1784
+ default {
1785
+ return -code error "ERROR: Token -shape is an unsupported value (pill, tag, square, eased, ticket)"
1786
+ }
1787
+ }
1788
+ set token_shapes($token) $value
1789
+ set redraw 1
1790
+ }
1791
+ }
1792
+ -selectbg -
1793
+ -selectbackground {
1794
+ if {$selected} {
1795
+ $token.ll configure -bg $value
1796
+ $token.l1 configure -bg $value
1797
+ $token.l2 configure -bg $value
1798
+ $token.l2.mid configure -bg $value
1799
+ $token.lr configure -bg $value
1800
+ set redraw 1
1801
+ } else {
1802
+ $token.ll configure -fg $value
1803
+ }
1804
+ }
1805
+ -selectfg -
1806
+ -selectforeground {
1807
+ if {$selected} {
1808
+ $token.l1 configure -fg $value
1809
+ set redraw 1
1810
+ } else {
1811
+ $token.lr configure -fg $value
1812
+ }
1813
+ }
1814
+ -selectbordercolor {
1815
+ if {$selected} {
1816
+ $token.l2.top configure -bg $value
1817
+ $token.l2.bot configure -bg $value
1818
+ set redraw 1
1819
+ } else {
1820
+ $token.l2.mid configure -fg $value
1821
+ }
1822
+ }
1823
+ -text {
1824
+ $token.l1 configure -text $value
1825
+ set redraw 1
1826
+ set resize 1
1827
+ }
1828
+ default {
1829
+ return -code error "Illegal option to the tokenconfigure option ($option)"
1830
+ }
1831
+ }
1832
+ }
1833
+
1834
+ # If we need to redraw the token, do it now
1835
+ if {$redraw} {
1836
+ redraw_token $w $token $resize
1837
+ }
1838
+
1839
+ }
1840
+
1841
+ ###########################################################################
1842
+ # Gets the configuration information located at the given index.
1843
+ proc tokencget {w args} {
1844
+
1845
+ variable token_shapes
1846
+
1847
+ if {[llength $args] != 2} {
1848
+ return -code error "Incorrect number of options given to the tokencget command"
1849
+ }
1850
+
1851
+ # Get the token index
1852
+ set index [index_to_position $w [lindex $args 0]]
1853
+
1854
+ # Get the token
1855
+ set token [$w.txt window cget $index -window]
1856
+
1857
+ # Figure out if the token is currently selected
1858
+ set selected [is_selected $token]
1859
+
1860
+ # Do an option lookup
1861
+ switch [lindex $args 1] {
1862
+ -bg -
1863
+ -background {
1864
+ if {$selected} {
1865
+ return [$token.ll cget -fg]
1866
+ } else {
1867
+ return [$token.l1 cget -bg]
1868
+ }
1869
+ }
1870
+ -fg -
1871
+ -foreground {
1872
+ if {$selected} {
1873
+ return [$token.lr cget -fg]
1874
+ } else {
1875
+ return [$token.l1 cget -fg]
1876
+ }
1877
+ }
1878
+ -bordercolor {
1879
+ if {$selected} {
1880
+ return [$token.l2.mid cget -fg]
1881
+ } else {
1882
+ return [$token.l2.top cget -bg]
1883
+ }
1884
+ }
1885
+ -shape {
1886
+ return $token_shapes($token)
1887
+ }
1888
+ -selectbg -
1889
+ -selectbackground {
1890
+ if {$selected} {
1891
+ return [$token.l1 cget -bg]
1892
+ } else {
1893
+ return [$token.ll cget -fg]
1894
+ }
1895
+ }
1896
+ -selectfg -
1897
+ -selectforeground {
1898
+ if {$selected} {
1899
+ return [$token.l1 cget -fg]
1900
+ } else {
1901
+ return [$token.lr cget -fg]
1902
+ }
1903
+ }
1904
+ -selectbordercolor {
1905
+ if {$selected} {
1906
+ return [$token.l2.top cget -bg]
1907
+ } else {
1908
+ return [$token.l2.mid cget -fg]
1909
+ }
1910
+ }
1911
+ -text {
1912
+ return [$token.l1 cget -text]
1913
+ }
1914
+ default {
1915
+ return -code error "Illegal option to the tokencget option ([lindex $args 1])"
1916
+ }
1917
+
1918
+ }
1919
+
1920
+ }
1921
+
1922
+ ###########################################################################
1923
+ # Returns the numerical index of the specified index.
1924
+ proc tokenindex {w args} {
1925
+
1926
+ variable active_token
1927
+
1928
+ if {[llength $args] != 1} {
1929
+ return -code error "Illegal options to the tokenindex command"
1930
+ } else {
1931
+ set index [lindex $args 0]
1932
+ if {$index eq "active"} {
1933
+ if {$active_token($w) eq ""} {
1934
+ return -1
1935
+ } else {
1936
+ return $active_token($w)
1937
+ }
1938
+ } else {
1939
+ return [lindex [$w.txt window names] $index]
1940
+ }
1941
+ }
1942
+
1943
+ }
1944
+
1945
+ ###########################################################################
1946
+ # Handles the tokenselection command.
1947
+ proc tokenselection {w args} {
1948
+
1949
+ variable active_token
1950
+ variable options
1951
+
1952
+ if {[llength $args] == 0} {
1953
+
1954
+ return -code error "Incorrect number of options to the tokenselection command"
1955
+
1956
+ } else {
1957
+
1958
+ switch [lindex $args 0] {
1959
+ get {
1960
+ }
1961
+ clear {
1962
+ set start_index [index_to_position $w [lindex $args 1]]
1963
+ set end_index [index_to_position $w [lindex $args 2]]
1964
+ set index $start_index
1965
+ foreach token [lrange [$w.txt window names] $start_index $end_index] {
1966
+ $token configure -fg $options($w,-tokenfg) -bg $options($w,-tokenbg)
1967
+ incr start_index
1968
+ }
1969
+ set active_token($w) ""
1970
+ }
1971
+ set {
1972
+ set start_index [index_to_position $w [lindex $args 1]]
1973
+ set end_index [index_to_position $w [lindex $args 2]]
1974
+ set index $start_index
1975
+ foreach token [lrange [$w.txt window names] $start_index $end_index] {
1976
+ $token configure -fg $options($w,-tokenselectfg) -bg $options($w,-tokenselectbg)
1977
+ incr index
1978
+ }
1979
+ set active_token($w) $start_index
1980
+ }
1981
+ default {
1982
+ return -code error "Illegal token selection command ([lindex $args 0])"
1983
+ }
1984
+ }
1985
+
1986
+ }
1987
+
1988
+ }
1989
+
1990
+ ###########################################################################
1991
+ # Handles the token insertion command.
1992
+ proc tokeninsert {w args} {
1993
+
1994
+ variable options
1995
+ variable dont_tokenize
1996
+
1997
+ if {[llength $args] != 2} {
1998
+ return -code error "Incorrect number of options to the tokeninsert command"
1999
+ }
2000
+
2001
+ set index [index_to_position $w [lindex $args 0]]
2002
+
2003
+ if {$index eq ""} {
2004
+ set index [$w.txt index "1.[lindex $args 0]"]
2005
+ }
2006
+
2007
+ # Make sure that all inserted text is tokenized
2008
+ set dont_tokenize($w) 0
2009
+
2010
+ # Create the tokens (we do this for performance purposes)
2011
+ set tokens [list]
2012
+ foreach value [lreverse [lindex $args 1]] {
2013
+
2014
+ # Handle the current state
2015
+ handle_state $w 1
2016
+
2017
+ # Insert the text into the widget
2018
+ $w.txt insert $index $value
2019
+
2020
+ # Turn the text into a token
2021
+ tokenize $w
2022
+
2023
+ }
2024
+
2025
+ }
2026
+
2027
+ ###########################################################################
2028
+ # Deletes one or more tokens from the text widget.
2029
+ proc tokendelete {w args} {
2030
+
2031
+ if {([llength $args] == 0) || ([llength $args] > 2)} {
2032
+ return -code error "Incorrect number of options to the tokendelete command"
2033
+ }
2034
+
2035
+ # If the user wants to delete a single item, do so
2036
+ if {[llength $args] == 1} {
2037
+ set index [index_to_position $w [lindex $args 0]]
2038
+ if {$index ne ""} {
2039
+ $w.txt delete $index
2040
+ handle_state $w 1
2041
+ }
2042
+
2043
+ # Otherwise, delete a range of items
2044
+ } else {
2045
+ set sindex [index_to_position $w [lindex $args 0]]
2046
+ set eindex [index_to_position $w [lindex $args 1]]
2047
+ if {$sindex ne ""} {
2048
+ if {$eindex eq ""} {
2049
+ $w.txt delete $sindex
2050
+ } else {
2051
+ $w.txt delete $sindex "$eindex + 1 chars"
2052
+ }
2053
+ handle_state $w 1
2054
+ }
2055
+ }
2056
+
2057
+ }
2058
+
2059
+ ###########################################################################
2060
+ # Converts tokenentry bbox command to a text bbox command.
2061
+ proc bbox {w args} {
2062
+
2063
+ if {[llength $args] != 1} {
2064
+ return -code error "tokenentry::bbox called with wrong arguments"
2065
+ }
2066
+
2067
+ return [$w.txt bbox [entry_to_text_index $w [lindex $args 0]]]
2068
+
2069
+ }
2070
+
2071
+ ###########################################################################
2072
+ # Converts tokenentry delete command to a text delete command.
2073
+ proc delete {w args} {
2074
+
2075
+ variable state
2076
+
2077
+ if {[llength $args] == 1} {
2078
+ if {$state($w) ne "empty"} {
2079
+ $w.txt delete [entry_to_text_index $w [lindex $args 0]]
2080
+ handle_state $w 0
2081
+ }
2082
+ } elseif {[llength $args] == 2} {
2083
+ if {$state($w) ne "empty"} {
2084
+ $w.txt delete [entry_to_text_index $w [lindex $args 0]] \
2085
+ [entry_to_text_index $w [lindex $args 1]]
2086
+ handle_state $w 0
2087
+ }
2088
+ } else {
2089
+ return -code error "tokenentry::delete called with wrong arguments"
2090
+ }
2091
+
2092
+ }
2093
+
2094
+ ###########################################################################
2095
+ # Converts tokenentry get command to a text get command.
2096
+ proc get {w args} {
2097
+
2098
+ variable state
2099
+
2100
+ if {[llength $args] != 0} {
2101
+ return -code error "tokenentry::get called with wrong arguments"
2102
+ }
2103
+
2104
+ if {$state($w) eq "empty"} {
2105
+ return ""
2106
+ } else {
2107
+ return [$w.txt get 1.0 1.end]
2108
+ }
2109
+
2110
+ }
2111
+
2112
+ ###########################################################################
2113
+ # Converts tokenentry icursor command to a text insertion cursor call.
2114
+ proc icursor {w args} {
2115
+
2116
+ variable state
2117
+
2118
+ if {[llength $args] != 1} {
2119
+ return -code error "tokenentry::icursor called with wrong arguments"
2120
+ }
2121
+
2122
+ if {$state($w) eq "empty"} {
2123
+ return [$w.txt mark set insert 1.0]
2124
+ } else {
2125
+ return [eval "$w.txt mark set insert [entry_to_text_index $w [lindex $args 0]]"]
2126
+ }
2127
+
2128
+ }
2129
+
2130
+ ###########################################################################
2131
+ # Converts tokenentry index command to a text index command call.
2132
+ proc index {w args} {
2133
+
2134
+ variable state
2135
+
2136
+ if {[llength $args] != 1} {
2137
+ return -code error "tokenentry::index called with wrong arguments"
2138
+ }
2139
+
2140
+ if {$state($w) eq "empty"} {
2141
+ return 0
2142
+ } else {
2143
+ return [lindex [split [$w.txt index [entry_to_text_index $w [lindex $args 0]]] .] 1]
2144
+ }
2145
+
2146
+ }
2147
+
2148
+ ###########################################################################
2149
+ # Overrides the text insert command to handle watermarks.
2150
+ proc insert {w args} {
2151
+
2152
+ if {([llength $args] != 1) && ([llength $args] != 2)} {
2153
+ return -code error "tokenentry::insert called with wrong arguments"
2154
+ }
2155
+
2156
+ # If the user has inserted a non-empty string of data, make sure the state
2157
+ # is handled properly.
2158
+ if {[lindex $args 1] ne ""} {
2159
+ handle_state $w 1
2160
+ } else {
2161
+ handle_state $w 0
2162
+ }
2163
+
2164
+ return [eval "$w.txt insert [entry_to_text_index $w [lindex $args 0]] [lindex $args 1]"]
2165
+
2166
+ }
2167
+
2168
+ ###########################################################################
2169
+ proc scan {w args} {
2170
+
2171
+ # TBD
2172
+
2173
+ return ""
2174
+
2175
+ }
2176
+
2177
+ ###########################################################################
2178
+ proc selection {w args} {
2179
+
2180
+ # TBD
2181
+
2182
+ return ""
2183
+
2184
+ }
2185
+
2186
+ ###########################################################################
2187
+ proc validate {w args} {
2188
+
2189
+ if {[llength $args] != 0} {
2190
+ return -code error "tokenentry::validate called with wrong arguments"
2191
+ }
2192
+
2193
+ # TBD
2194
+
2195
+ return 1
2196
+
2197
+ }
2198
+
2199
+ ###########################################################################
2200
+ proc xview {w args} {
2201
+
2202
+ return ""
2203
+
2204
+ }
2205
+
2206
+ namespace export *
2207
+
2208
+ }