source: trunk/gui/scripts/textentry.tcl @ 45

Last change on this file since 45 was 45, checked in by mmc, 19 years ago

Fixed copy/paste with desktop to work properly. It was
relying on the clipboard when the selection and the
cutbuffer were not available. But that causes strange
behavior with the applet panel. Now that the clipboard
stuff is removed, it works better.

Also, changed the wrapping on text entries to "char"
instead of "word", which should be more intuitive for
users entering long lines.

File size: 14.6 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: textentry - general-purpose text entry widget
3#
4#  This widget is a cross between the Tk entry and text widgets.  For
5#  one-line messages, it acts like an entry widget.  For larger
6#  messages, it morphs into a text widget.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2005
10#  Purdue Research Foundation, West Lafayette, IN
11# ======================================================================
12package require Itk
13
14option add *TextEntry.size auto widgetDefault
15option add *TextEntry.width 0 widgetDefault
16option add *TextEntry.height 0 widgetDefault
17option add *TextEntry.editable yes widgetDefault
18option add *TextEntry.textBackground white widgetDefault
19
20option add *TextEntry.hintForeground gray50 widgetDefault
21option add *TextEntry.hintFont \
22    -*-helvetica-medium-r-normal-*-*-100-* widgetDefault
23
24
25itcl::class Rappture::TextEntry {
26    inherit itk::Widget
27
28    itk_option define -editable editable Editable ""
29    itk_option define -width width Width 0
30    itk_option define -height height Height 0
31
32    constructor {owner path args} { # defined below }
33
34    public method value {args}
35
36    public method label {}
37    public method tooltip {}
38    public method size {} { return $_size }
39
40    protected method _layout {}
41    protected method _newValue {}
42
43    private variable _dispatcher "" ;# dispatcher for !events
44    private variable _owner ""    ;# thing managing this control
45    private variable _path ""     ;# path in XML to this number
46
47    private variable _mode ""       ;# entry or text mode
48    private variable _size ""       ;# size hint from XML
49}
50                                                                               
51itk::usual TextEntry {
52}
53
54# ----------------------------------------------------------------------
55# CONSTRUCTOR
56# ----------------------------------------------------------------------
57itcl::body Rappture::TextEntry::constructor {owner path args} {
58    if {[catch {$owner isa Rappture::ControlOwner} valid] != 0 || !$valid} {
59        error "bad object \"$owner\": should be Rappture::ControlOwner"
60    }
61    set _owner $owner
62    set _path $path
63
64    Rappture::dispatcher _dispatcher
65    $_dispatcher register !layout
66    $_dispatcher dispatch $this !layout "[itcl::code $this _layout]; list"
67
68    set _size [$_owner xml get $path.size]
69
70    set hints [$_owner xml get $path.about.hints]
71    if {[string length $hints] > 0} {
72        itk_component add hints {
73            ::label $itk_interior.hints -anchor w -text $hints
74        } {
75            usual
76            rename -foreground -hintforeground hintForeground Foreground
77            rename -font -hintfont hintFont Font
78        }
79        pack $itk_component(hints) -side bottom -fill x
80    }
81
82    eval itk_initialize $args
83
84    set str [$_owner xml get $path.default]
85    if {"" != $str} { value $str }
86}
87
88# ----------------------------------------------------------------------
89# USAGE: value ?-check? ?<newval>?
90#
91# Clients use this to query/set the value for this widget.  With
92# no args, it returns the current value for the widget.  If the
93# <newval> is specified, it sets the value of the widget and
94# sends a <<Value>> event.  If the -check flag is included, the
95# new value is not actually applied, but just checked for correctness.
96# ----------------------------------------------------------------------
97itcl::body Rappture::TextEntry::value {args} {
98    set onlycheck 0
99    set i [lsearch -exact $args -check]
100    if {$i >= 0} {
101        set onlycheck 1
102        set args [lreplace $args $i $i]
103    }
104
105    if {[llength $args] == 1} {
106        if {$onlycheck} {
107            # someday we may add validation...
108            return
109        }
110        set newval [lindex $args 0]
111        if {$_mode == "entry"} {
112            $itk_component(entry) configure -state normal
113            $itk_component(emenu) entryconfigure "Cut" -state normal
114            $itk_component(emenu) entryconfigure "Copy" -state normal
115            $itk_component(emenu) entryconfigure "Paste" -state normal
116            $itk_component(entry) delete 0 end
117            $itk_component(entry) insert 0 $newval
118            if {!$itk_option(-editable)} {
119                $itk_component(entry) configure -state disabled
120                $itk_component(emenu) entryconfigure "Cut" -state disabled
121                $itk_component(emenu) entryconfigure "Copy" -state disabled
122                $itk_component(emenu) entryconfigure "Paste" -state disabled
123            }
124        } elseif {$_mode == "text"} {
125            $itk_component(text) configure -state normal
126            $itk_component(tmenu) entryconfigure "Cut" -state normal
127            $itk_component(tmenu) entryconfigure "Copy" -state normal
128            $itk_component(tmenu) entryconfigure "Paste" -state normal
129            $itk_component(text) delete 1.0 end
130            $itk_component(text) insert end $newval
131            if {!$itk_option(-editable)} {
132                $itk_component(text) configure -state disabled
133                $itk_component(tmenu) entryconfigure "Cut" -state disabled
134                $itk_component(tmenu) entryconfigure "Copy" -state disabled
135                $itk_component(tmenu) entryconfigure "Paste" -state disabled
136            }
137        }
138        $_dispatcher event -idle !layout
139        event generate $itk_component(hull) <<Value>>
140        return $newval
141
142    } elseif {[llength $args] != 0} {
143        error "wrong # args: should be \"value ?-check? ?newval?\""
144    }
145
146    #
147    # Query the value and return.
148    #
149    if {$_mode == "entry"} {
150        return [$itk_component(entry) get]
151    } elseif {$_mode == "text"} {
152        return [$itk_component(text) get 1.0 end-1char]
153    }
154    return ""
155}
156
157# ----------------------------------------------------------------------
158# USAGE: label
159#
160# Clients use this to query the label associated with this widget.
161# Reaches into the XML and pulls out the appropriate label string.
162# ----------------------------------------------------------------------
163itcl::body Rappture::TextEntry::label {} {
164    set label [$_owner xml get $_path.about.label]
165    if {"" == $label} {
166        set label "String"
167    }
168    return $label
169}
170
171# ----------------------------------------------------------------------
172# USAGE: tooltip
173#
174# Clients use this to query the tooltip associated with this widget.
175# Reaches into the XML and pulls out the appropriate description
176# string.  Returns the string that should be used with the
177# Rappture::Tooltip facility.
178# ----------------------------------------------------------------------
179itcl::body Rappture::TextEntry::tooltip {} {
180    set str [$_owner xml get $_path.about.description]
181    return [string trim $str]
182}
183
184# ----------------------------------------------------------------------
185# USAGE: _layout
186#
187# Used internally to change the layout of this widget depending
188# on the .size hint and its contents.  Switches between an entry
189# and a text widget.
190# ----------------------------------------------------------------------
191itcl::body Rappture::TextEntry::_layout {} {
192    set size $_size
193    if {$size == "" || $size == "auto"} {
194        #
195        # If the size is "auto", then look at the current value
196        # and count its lines/characters.
197        #
198        set val ""
199        if {$_mode == "entry"} {
200            set val [$itk_component(entry) get]
201        } elseif {$_mode == "text"} {
202            set val [$itk_component(text) get 1.0 end-1char]
203        }
204
205        set chars 0
206        set lines 0
207        foreach line [split $val \n] {
208            incr lines
209            if {[string length $line] > $chars} {
210                set chars [string length $line]
211            }
212        }
213        incr chars
214
215        if {$lines > 1} {
216            set size "${chars}x${lines}"
217        } else {
218            set size $chars
219        }
220    }
221
222    if {[regexp {^[0-9]+$} $size]} {
223        #
224        # If the size is WW, then flip to entry mode, with
225        # a requested size of WW characters.
226        #
227        if {$_mode != "entry"} {
228            set val ""
229            if {$_mode == "text"} {
230                set val [$itk_component(text) get 1.0 end-1char]
231                destroy $itk_component(text)
232                destroy $itk_component(scrollbars)
233            }
234
235            itk_component add entry {
236                entry $itk_interior.entry
237            } {
238                usual
239                rename -background -textbackground textBackground Background
240                rename -foreground -textforeground textForeground Foreground
241            }
242            pack $itk_component(entry) -expand yes -fill both
243            $itk_component(entry) configure \
244                -background $itk_option(-textbackground) \
245                -foreground $itk_option(-textforeground)
246
247            bind $itk_component(entry) <KeyPress> [itcl::code $this _newValue]
248
249            itk_component add emenu {
250                menu $itk_component(entry).menu -tearoff 0
251            }
252            $itk_component(emenu) add command -label "Cut" -accelerator "^X" \
253                -command [list event generate $itk_component(entry) <<Cut>>]
254            $itk_component(emenu) add command -label "Copy" -accelerator "^C" \
255                -command [list event generate $itk_component(entry) <<Copy>>]
256            $itk_component(emenu) add command -label "Paste" -accelerator "^V" \
257                -command [list event generate $itk_component(entry) <<Paste>>]
258            bind $itk_component(entry) <<PopupMenu>> {
259                tk_popup %W.menu %X %Y
260            }
261
262            $itk_component(entry) insert end $val
263            if {!$itk_option(-editable)} {
264                $itk_component(entry) configure -state disabled
265            }
266            set _mode "entry"
267        }
268        $itk_component(entry) configure -width $size
269
270    } elseif {[regexp {^([0-9]+)x([0-9]+)$} $size match w h]} {
271        #
272        # If the size is WWxHH, then flip to text mode, with
273        # a requested size of HH lines by WW characters.
274        #
275        if {$_mode != "text"} {
276            set val ""
277            if {$_mode == "entry"} {
278                set val [$itk_component(entry) get]
279                destroy $itk_component(entry)
280            }
281
282            itk_component add scrollbars {
283                Rappture::Scroller $itk_interior.scrl \
284                     -xscrollmode auto -yscrollmode auto
285            }
286            pack $itk_component(scrollbars) -expand yes -fill both
287
288            itk_component add text {
289                text $itk_component(scrollbars).text \
290                    -width 1 -height 1 -wrap char
291            } {
292                usual
293                rename -background -textbackground textBackground Background
294                rename -foreground -textforeground textForeground Foreground
295            }
296            $itk_component(text) configure \
297                -background $itk_option(-textbackground) \
298                -foreground $itk_option(-textforeground)
299            $itk_component(scrollbars) contents $itk_component(text)
300
301            bind $itk_component(text) <KeyPress> [itcl::code $this _newValue]
302
303            itk_component add tmenu {
304                menu $itk_component(text).menu -tearoff 0
305            }
306            $itk_component(tmenu) add command -label "Cut" -accelerator "^X" \
307                -command [list event generate $itk_component(text) <<Cut>>]
308            $itk_component(tmenu) add command -label "Copy" -accelerator "^C" \
309                -command [list event generate $itk_component(text) <<Copy>>]
310            $itk_component(tmenu) add command -label "Paste" -accelerator "^V" \
311                -command [list event generate $itk_component(text) <<Paste>>]
312            bind $itk_component(text) <<PopupMenu>> {
313                tk_popup %W.menu %X %Y
314            }
315
316            $itk_component(text) insert end $val
317            if {!$itk_option(-editable)} {
318                $itk_component(text) configure -state disabled
319                $itk_component(menu) entryconfigure "Cut" -state disabled
320                $itk_component(menu) entryconfigure "Copy" -state disabled
321                $itk_component(menu) entryconfigure "Paste" -state disabled
322            }
323            set _mode "text"
324        }
325        $itk_component(text) configure -width $w -height $h
326    }
327
328    #
329    # Fix the overall widget size according to -width / -height
330    #
331    if {$itk_option(-width) == 0 && $itk_option(-height) == 0} {
332        pack propagate $itk_component(hull) yes
333    } else {
334        pack propagate $itk_component(hull) no
335        component hull configure \
336            -width $itk_option(-width) -height $itk_option(-width)
337    }
338}
339
340# ----------------------------------------------------------------------
341# USAGE: _newValue
342#
343# Invoked automatically whenever the value in the entry changes.
344# Sends a <<Value>> event to notify clients of the change.
345# ----------------------------------------------------------------------
346itcl::body Rappture::TextEntry::_newValue {} {
347    event generate $itk_component(hull) <<Value>>
348}
349
350# ----------------------------------------------------------------------
351# CONFIGURATION OPTION: -editable
352# ----------------------------------------------------------------------
353itcl::configbody Rappture::TextEntry::editable {
354    if {![string is boolean -strict $itk_option(-editable)]} {
355        error "bad value \"$itk_option(-editable)\": should be boolean"
356    }
357
358    if {$itk_option(-editable)} {
359        set state normal
360    } else {
361        set state disabled
362    }
363    if {$_mode == "entry"} {
364        $itk_component(editor) configure -state $state
365        $itk_component(emenu) entryconfigure "Cut" -state $state
366        $itk_component(emenu) entryconfigure "Copy" -state $state
367        $itk_component(emenu) entryconfigure "Paste" -state $state
368    } elseif {$_mode == "text"} {
369        $itk_component(text) configure -state $state
370        $itk_component(tmenu) entryconfigure "Cut" -state $state
371        $itk_component(tmenu) entryconfigure "Copy" -state $state
372        $itk_component(tmenu) entryconfigure "Paste" -state $state
373    }
374}
375
376# ----------------------------------------------------------------------
377# CONFIGURATION OPTION: -width
378# ----------------------------------------------------------------------
379itcl::configbody Rappture::TextEntry::width {
380    # check size to see if it has the proper form
381    winfo pixels $itk_component(hull) $itk_option(-width)
382    $_dispatcher event -idle !layout
383}
384
385# ----------------------------------------------------------------------
386# CONFIGURATION OPTION: -height
387# ----------------------------------------------------------------------
388itcl::configbody Rappture::TextEntry::height {
389    # check size to see if it has the proper form
390    winfo pixels $itk_component(hull) $itk_option(-height)
391    $_dispatcher event -idle !layout
392}
Note: See TracBrowser for help on using the repository browser.