[5679] | 1 | # -*- mode: tcl; indent-tabs-mode: nil -*- |
---|
[1] | 2 | # ---------------------------------------------------------------------- |
---|
| 3 | # COMPONENT: editor - pop-up editor for little bits of text |
---|
| 4 | # |
---|
| 5 | # This widget acts as a pop-up editor for small text fields. It |
---|
| 6 | # pops up on top of any text field, accepts edits, and then attempts |
---|
| 7 | # to validate and apply changes back to the underlying widget. |
---|
| 8 | # |
---|
| 9 | # This widget uses a number of callbacks to handle communication |
---|
| 10 | # with the underlying widget: |
---|
| 11 | # |
---|
| 12 | # -activatecommand .... Should return a key/value list with the |
---|
| 13 | # following elements: |
---|
| 14 | # x ...... root x coordinate for editor |
---|
| 15 | # y ...... root y coordinate for editor |
---|
| 16 | # w ...... width of text being edited |
---|
| 17 | # h ...... height of text being edited |
---|
| 18 | # text ... initial text for the editor |
---|
| 19 | # |
---|
| 20 | # -validatecommand .... Invoked with the new value as an argument. |
---|
| 21 | # Should return 1 if the value is okay, and |
---|
| 22 | # 0 otherwise. |
---|
| 23 | # |
---|
| 24 | # -applycommand ....... Invoked with the new value as an argument. |
---|
| 25 | # Should apply the new value to the underlying |
---|
| 26 | # widget. |
---|
| 27 | # |
---|
| 28 | # ====================================================================== |
---|
| 29 | # AUTHOR: Michael McLennan, Purdue University |
---|
[3177] | 30 | # Copyright (c) 2004-2012 HUBzero Foundation, LLC |
---|
[115] | 31 | # |
---|
| 32 | # See the file "license.terms" for information on usage and |
---|
| 33 | # redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
[1] | 34 | # ====================================================================== |
---|
| 35 | package require Itk |
---|
| 36 | |
---|
| 37 | option add *Editor.background white widgetDefault |
---|
| 38 | option add *Editor.outline black widgetDefault |
---|
| 39 | option add *Editor.borderwidth 1 widgetDefault |
---|
| 40 | option add *Editor.relief flat widgetDefault |
---|
| 41 | option add *Editor.selectBorderWidth 0 widgetDefault |
---|
| 42 | |
---|
| 43 | itcl::class Rappture::Editor { |
---|
| 44 | inherit itk::Toplevel |
---|
| 45 | |
---|
| 46 | itk_option define -outline outline Outline "" |
---|
| 47 | itk_option define -activatecommand activateCommand ActivateCommand "" |
---|
| 48 | itk_option define -validatecommand validateCommand ValidateCommand "" |
---|
| 49 | itk_option define -applycommand applyCommand ApplyCommand "" |
---|
| 50 | |
---|
| 51 | constructor {args} { # defined below } |
---|
| 52 | |
---|
| 53 | public method activate {} |
---|
| 54 | public method deactivate {args} |
---|
| 55 | public method value {newval} |
---|
| 56 | |
---|
| 57 | protected method _click {x y} |
---|
| 58 | protected method _resize {} |
---|
| 59 | protected variable _loc ;# array of editor location parameters |
---|
| 60 | } |
---|
[5679] | 61 | |
---|
[1] | 62 | itk::usual Editor { |
---|
| 63 | keep -cursor -font |
---|
| 64 | } |
---|
| 65 | |
---|
| 66 | # ---------------------------------------------------------------------- |
---|
| 67 | # CONSTRUCTOR |
---|
| 68 | # ---------------------------------------------------------------------- |
---|
| 69 | itcl::body Rappture::Editor::constructor {args} { |
---|
| 70 | wm overrideredirect $itk_component(hull) yes |
---|
| 71 | wm withdraw $itk_component(hull) |
---|
| 72 | |
---|
| 73 | itk_option remove hull.background hull.borderwidth |
---|
| 74 | component hull configure -borderwidth 1 |
---|
| 75 | |
---|
| 76 | itk_component add editor { |
---|
[1850] | 77 | entry $itk_interior.editor -highlightthickness 0 |
---|
[1] | 78 | } { |
---|
[1850] | 79 | usual |
---|
| 80 | keep -relief |
---|
| 81 | ignore -highlightthickness |
---|
| 82 | ignore -highlightcolor |
---|
| 83 | ignore -highlightbackground |
---|
[1] | 84 | } |
---|
| 85 | pack $itk_component(editor) -expand yes -fill both |
---|
| 86 | |
---|
| 87 | bind $itk_component(editor) <KeyPress> \ |
---|
[1850] | 88 | [itcl::code $this _resize] |
---|
[1] | 89 | bind $itk_component(editor) <KeyPress-Return> \ |
---|
[1850] | 90 | [itcl::code $this deactivate] |
---|
[6212] | 91 | bind $itk_component(editor) <KP_Enter> \ |
---|
| 92 | [itcl::code $this deactivate] |
---|
[1] | 93 | bind $itk_component(editor) <KeyPress-Escape> \ |
---|
[1850] | 94 | [itcl::code $this deactivate -abort] |
---|
[1] | 95 | bind $itk_component(editor) <ButtonPress> \ |
---|
[1850] | 96 | [itcl::code $this _click %X %Y] |
---|
[1] | 97 | |
---|
[17] | 98 | itk_component add emenu { |
---|
[1850] | 99 | menu $itk_component(editor).menu -tearoff 0 |
---|
[17] | 100 | } { |
---|
[1850] | 101 | usual |
---|
| 102 | ignore -tearoff |
---|
| 103 | ignore -background -foreground |
---|
[17] | 104 | } |
---|
| 105 | $itk_component(emenu) add command -label "Cut" -accelerator "^X" \ |
---|
[1850] | 106 | -command [list event generate $itk_component(editor) <<Cut>>] |
---|
[17] | 107 | $itk_component(emenu) add command -label "Copy" -accelerator "^C" \ |
---|
[1850] | 108 | -command [list event generate $itk_component(editor) <<Copy>>] |
---|
[17] | 109 | $itk_component(emenu) add command -label "Paste" -accelerator "^V" \ |
---|
[1850] | 110 | -command [list event generate $itk_component(editor) <<Paste>>] |
---|
[17] | 111 | bind $itk_component(editor) <<PopupMenu>> { |
---|
[1850] | 112 | tk_popup %W.menu %X %Y |
---|
[17] | 113 | } |
---|
| 114 | |
---|
[1] | 115 | eval itk_initialize $args |
---|
| 116 | } |
---|
| 117 | |
---|
| 118 | # ---------------------------------------------------------------------- |
---|
| 119 | # USAGE: activate |
---|
| 120 | # |
---|
| 121 | # Clients use this to start the editing process on the underlying |
---|
| 122 | # widget. This pops up the editor with the current text from the |
---|
| 123 | # underlying widget and allows the user to edit the text. The editor |
---|
| 124 | # remains up until it is deactivated. |
---|
| 125 | # ---------------------------------------------------------------------- |
---|
| 126 | itcl::body Rappture::Editor::activate {} { |
---|
| 127 | set e $itk_component(editor) |
---|
[17] | 128 | if {[winfo ismapped $e]} { |
---|
[1850] | 129 | return ;# already mapped -- nothing to do |
---|
[17] | 130 | } |
---|
[1] | 131 | |
---|
| 132 | set info "" |
---|
| 133 | if {[string length $itk_option(-activatecommand)] > 0} { |
---|
[1850] | 134 | set status [catch {uplevel #0 $itk_option(-activatecommand)} info] |
---|
| 135 | if {$status != 0} { |
---|
| 136 | bgerror $info |
---|
| 137 | return |
---|
| 138 | } |
---|
[1] | 139 | } |
---|
| 140 | |
---|
| 141 | # |
---|
| 142 | # Pull out the location information from the values passed back |
---|
| 143 | # from the activation command. We must have at least an x,y |
---|
| 144 | # coordinate. If we get width and height too, then use it. |
---|
| 145 | # If not, figure out the width and height based on the size |
---|
| 146 | # of the string. |
---|
| 147 | # |
---|
| 148 | array set vals $info |
---|
| 149 | if {![info exists vals(x)] || ![info exists vals(y)]} { |
---|
[1850] | 150 | return |
---|
[1] | 151 | } |
---|
| 152 | set _loc(x) $vals(x) |
---|
| 153 | set _loc(y) $vals(y) |
---|
| 154 | set _loc(w) [expr {([info exists vals(w)]) ? $vals(w) : 0}] |
---|
| 155 | set _loc(h) [expr {([info exists vals(h)]) ? $vals(h) : 0}] |
---|
| 156 | |
---|
| 157 | $itk_component(editor) delete 0 end |
---|
| 158 | if {[info exists vals(text)]} { |
---|
[1850] | 159 | $itk_component(editor) insert end $vals(text) |
---|
[1] | 160 | } |
---|
| 161 | $itk_component(editor) select from 0 |
---|
| 162 | $itk_component(editor) select to end |
---|
| 163 | |
---|
| 164 | _resize |
---|
| 165 | wm deiconify $itk_component(hull) |
---|
| 166 | raise $itk_component(hull) |
---|
[2159] | 167 | focus -force $itk_component(editor) |
---|
[1] | 168 | |
---|
| 169 | # try to grab the pointer, and keep trying... |
---|
| 170 | update |
---|
| 171 | while {[catch {grab set -global $itk_component(editor)}]} { |
---|
[1850] | 172 | after 100 |
---|
[1] | 173 | } |
---|
| 174 | } |
---|
| 175 | |
---|
| 176 | # ---------------------------------------------------------------------- |
---|
| 177 | # USAGE: deactivate ?-abort? |
---|
| 178 | # |
---|
| 179 | # This is invoked automatically whenever the user presses Enter or |
---|
| 180 | # Escape in the editor. Clients can also use it explicitly to |
---|
| 181 | # deactivate the editor. |
---|
| 182 | # |
---|
| 183 | # If the -abort flag is specified, then the editor is taken down |
---|
| 184 | # without any validation or application of the result. Otherwise, |
---|
| 185 | # we validate the contents of the editor and apply the change back |
---|
| 186 | # to the widget. |
---|
| 187 | # ---------------------------------------------------------------------- |
---|
| 188 | itcl::body Rappture::Editor::deactivate {args} { |
---|
| 189 | # take down any error cue that might be up |
---|
| 190 | ::Rappture::Tooltip::cue hide |
---|
| 191 | |
---|
| 192 | if {$args == "-abort"} { |
---|
[1850] | 193 | grab release $itk_component(editor) |
---|
| 194 | wm withdraw $itk_component(hull) |
---|
| 195 | return |
---|
[1] | 196 | } |
---|
| 197 | |
---|
| 198 | set str [$itk_component(editor) get] |
---|
| 199 | |
---|
| 200 | # |
---|
| 201 | # If there's a -validatecommand option, then invoke the code |
---|
| 202 | # now to check the new value. |
---|
| 203 | # |
---|
| 204 | if {[string length $itk_option(-validatecommand)] > 0} { |
---|
[1850] | 205 | set cmd "uplevel #0 [list $itk_option(-validatecommand) [list $str]]" |
---|
| 206 | if {[catch $cmd result]} { |
---|
| 207 | bgerror $result |
---|
| 208 | set result 1 |
---|
| 209 | } |
---|
| 210 | if {$result == 0} { |
---|
| 211 | bell |
---|
| 212 | $itk_component(editor) select from 0 |
---|
| 213 | $itk_component(editor) select to end |
---|
| 214 | $itk_component(editor) icursor end |
---|
| 215 | focus $itk_component(editor) |
---|
| 216 | return |
---|
| 217 | } |
---|
[1] | 218 | } |
---|
| 219 | |
---|
| 220 | grab release $itk_component(editor) |
---|
| 221 | wm withdraw $itk_component(hull) |
---|
| 222 | |
---|
| 223 | # |
---|
| 224 | # If there's an -applycommand option, then invoke the code |
---|
| 225 | # now to apply the new value. |
---|
| 226 | # |
---|
| 227 | if {[string length $itk_option(-applycommand)] > 0} { |
---|
[1850] | 228 | set cmd "uplevel #0 [list $itk_option(-applycommand) [list $str]]" |
---|
| 229 | if {[catch $cmd result]} { |
---|
| 230 | bgerror $result |
---|
| 231 | return |
---|
| 232 | } |
---|
[1] | 233 | } |
---|
| 234 | } |
---|
| 235 | |
---|
| 236 | # ---------------------------------------------------------------------- |
---|
| 237 | # USAGE: value <newval> |
---|
| 238 | # |
---|
| 239 | # Clients use this to suggest a new value, particular when they've |
---|
| 240 | # caught an error in the editing process. For example, if the user's |
---|
| 241 | # value is below the minimum allowed value, a client would call this |
---|
| 242 | # method to suggest the minimum value. |
---|
| 243 | # ---------------------------------------------------------------------- |
---|
| 244 | itcl::body Rappture::Editor::value {newval} { |
---|
| 245 | $itk_component(editor) delete 0 end |
---|
| 246 | $itk_component(editor) insert end $newval |
---|
| 247 | } |
---|
| 248 | |
---|
| 249 | # ---------------------------------------------------------------------- |
---|
| 250 | # USAGE: _click <X> <Y> |
---|
| 251 | # |
---|
| 252 | # This is invoked automatically whenever the user clicks somewhere |
---|
| 253 | # inside or outside of the editor. If the <X>,<Y> coordinate is |
---|
| 254 | # outside the editor, then we assume the user is done and wants to |
---|
| 255 | # take the editor down. Otherwise, we do nothing, and let the entry |
---|
| 256 | # bindings take over. |
---|
| 257 | # ---------------------------------------------------------------------- |
---|
| 258 | itcl::body Rappture::Editor::_click {x y} { |
---|
| 259 | if {[winfo containing $x $y] != $itk_component(editor)} { |
---|
[1850] | 260 | deactivate |
---|
[17] | 261 | } else { |
---|
[1850] | 262 | # make sure the editor has keyboard focus! |
---|
| 263 | # it loses focus sometimes during cut/copy/paste operations |
---|
| 264 | focus -force $itk_component(editor) |
---|
[1] | 265 | } |
---|
| 266 | } |
---|
| 267 | |
---|
| 268 | # ---------------------------------------------------------------------- |
---|
| 269 | # USAGE: _resize |
---|
| 270 | # |
---|
| 271 | # Invoked automatically as each key is pressed in the editor. |
---|
| 272 | # Resizes the editor so that it is just big enough to show all |
---|
| 273 | # of the text within it. |
---|
| 274 | # ---------------------------------------------------------------------- |
---|
| 275 | itcl::body Rappture::Editor::_resize {} { |
---|
| 276 | set e $itk_component(editor) |
---|
| 277 | set str [$e get] |
---|
| 278 | set fnt [$e cget -font] |
---|
| 279 | |
---|
| 280 | set w [expr {[font measure $fnt $str]+20}] |
---|
| 281 | set w [expr {($w < $_loc(w)) ? $_loc(w) : $w}] |
---|
[5468] | 282 | if {$w+$_loc(x) >= [winfo screenwidth $e]} { |
---|
| 283 | set w [expr {[winfo screenwidth $e]-$_loc(x)}] |
---|
[1] | 284 | } |
---|
| 285 | |
---|
| 286 | set h [expr {[font metrics $fnt -linespace]+4}] |
---|
| 287 | set h [expr {($h < $_loc(h)) ? $_loc(h) : $h}] |
---|
[5468] | 288 | if {$h+$_loc(y) >= [winfo screenheight $e]} { |
---|
| 289 | set h [expr {[winfo screenheight $e]-$_loc(y)}] |
---|
[5679] | 290 | } |
---|
[1258] | 291 | # Temporary fix to prevent Opps. Don't deal with negative dimensions. |
---|
| 292 | if { $w <= 0 || $h <= 0 } { |
---|
[1850] | 293 | wm geometry $itk_component(hull) "+$_loc(x)+$_loc(y)" |
---|
[1258] | 294 | } else { |
---|
[1850] | 295 | wm geometry $itk_component(hull) "${w}x${h}+$_loc(x)+$_loc(y)" |
---|
[1] | 296 | } |
---|
| 297 | } |
---|
| 298 | |
---|
| 299 | # ---------------------------------------------------------------------- |
---|
| 300 | # CONFIGURATION OPTION: -outline |
---|
| 301 | # ---------------------------------------------------------------------- |
---|
| 302 | itcl::configbody Rappture::Editor::outline { |
---|
| 303 | component hull configure -background $itk_option(-outline) |
---|
| 304 | } |
---|