File: dateentry.tcl

package info (click to toggle)
r-cran-tcltk2 1.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,744 kB
  • sloc: tcl: 59,824; ansic: 792; python: 324; sed: 53; sh: 17; makefile: 2
file content (383 lines) | stat: -rw-r--r-- 11,346 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# -*- tcl -*-
#
# dateentry.tcl -
#
#       dateentry widget
#
# This widget provides an entry with a visual calendar for
# choosing a date. It is mostly a gathering compoments.
#
# The basics for the entry were taken from the "MenuEntry widget"
# of the widget package in the tklib.
# The visual calendar is taken from http://wiki.tcl.tk/1816.
#
# So many thanks to Richard Suchenwirth for visual calendar
# and to Jeff Hobbs for the widget package in tklib.
#
# See the example at the bottom.
#

# Creation and Options - widget::dateentry $path ...
#  -command        -default {}
#  -dateformat     -default "%m/%d/%Y"
#  -font           -default {Helvetica 9}
#  -background     -default white
#  -textvariable   -default {}  -configuremethod C-textvariable
#
# Following are passed to widget::calendar component:
#  -firstday
#  -highlightcolor
#  -language
#
# Methods
#  $widget post   - display calendar dropdown
#  $widget unpost - remove calendar dropdown
#  All other methods to entry
#
# Bindings
#  NONE
#

###

package require widget
package require widget::calendar

namespace eval ::widget {
    # http://www.famfamfam.com/lab/icons/mini/
    # ?Mini? is a set of 144 GIF icons available for free use for any purpose.
    variable dateentry_gifdata {
	R0lGODlhEAAQAMQAANnq+K7T5HiUsMHb+v/vlOXs9IyzzHWs1/T5/1ZtjUlVa+z1/+3
	x9uTx/6a2ysng+FFhe0NLXIDG/fD4/ykxQz5FVf/41vr8/6TI3MvM0XHG/vbHQPn8//
	b8/4PL/f///yH5BAAAAAAALAAAAAAQABAAAAWV4Cdam2h+5AkExCYYsCC0iSAGTisAP
	JC7kNvicPBIjkeiIyHCMDzQaFRTYH4wBY6W0+kgvpNC8GNgXLhd8CQ8Lp8f3od8sSgo
	RIasHPGY0AcNdiIHBV0PfHQNgAURIgKFfBMPCw2KAIyOkH0LA509FY4TXn6UDT0MoB8
	JDwwFDK+wrxkUjgm2EBAKChERFRUUYyfCwyEAOw==
    }
    # http://www.famfamfam.com/lab/icons/silk/
    # ?Silk? is a smooth, free icon set,
    variable dateentry_gifdata {
	R0lGODlhEAAQAPZ8AP99O/9/PWmrYmytZW6uaHOxbP+EQv+LR/+QTf+UUv+VVP+WVP+
	YV/+ZWP+aWv+dXP+eXf+fX/+nVP+rWv+gYP+hYf+iYv+jZP+kZP+kZf+wYf+zaP+4bf
	+5cf+7df+9eUJ3u1KEw1SGxFWGxlaHx12KxVyKxl+MxlmKyFuKyV+NyF6Oy1+Py2OSz
	mSTzmiW0WqX0W6Z02+b1HKe1nSg13Wh13qj2nqk2X2l3H6o3ZHBjJvHlqXNoa/Sq4Cp
	3YOr3IKq34mu2Yyw24mw3pG03Za434Ss4Ieu4Yiv4oyx44+14Yyy5I+05ZC15pO355S
	355W445294Zq75p++5pa66Zi66Zq865u9652+656/7KG/55/A7aTB5KTB56vG5abD6a
	HB7qLB76rG6a7J6rLL6rfO6rrQ67zQ68PdwNfp1dji8Nvk8d7n8t7n8+Lq9Obt9urw9
	+vx9+3y+O7z+e/z+fD0+vH2+vL2+vT3+/n8+f7+/v7//v///wAAAAAAAAAAACH5BAEA
	AH0ALAAAAAAQABAAAAfMgH2Cg4SFg2FbWFZUTk1LSEY+ODaCYHiXmJmXNIJZeBkXFBA
	NCwgHBgF4MoJXeBgfHh0cGxoTEgB4MIJVnxcWFREPDgwKCXgugk94X3zNzs1ecSyCTH
	difD0FaT0DPXxcbCiCSXZjzQJpO3kFfFFqI4JHdWTnaTp8AnxFaiKCQHRl+KARwKMHA
	W9E1KgQlIOOGT569uyB2EyIGhOCbsw500XLFClQlAz5EUTNCUE15MB546bNGjUwY5YQ
	NCPGixYrUpAIwbMnCENACQUCADs=
    }
}

proc ::widget::createdateentryLayout {} {
    variable dateentry
    if {[info exists dateentry]} { return }
    set dateentry 1
    variable dateentry_pngdata
    variable dateentry_gifdata
    set img ::widget::img_dateentry
    image create photo $img -format GIF -data $dateentry_gifdata
    namespace eval ::ttk [list set dateimg $img] ; # namespace resolved
    namespace eval ::ttk {
	# Create -padding for space on left and right of icon
	set pad [expr {[image width $dateimg] + 6}]
	style theme settings "default" {
	    style layout dateentry {
		Entry.field -children {
		    dateentry.icon -side left
		    Entry.padding -children {
			Entry.textarea
		    }
		}
	    }
	    # center icon in padded cell
	    style element create dateentry.icon image $dateimg \
		-sticky "" -padding [list $pad 0 0 0]
	}
	if 0 {
	    # Some mappings would be required per-theme to adapt to theme
	    # changes
	    foreach theme [style theme names] {
		style theme settings $theme {
		    # Could have disabled, pressed, ... state images
		    #style map dateentry -image [list disabled $img]
		}
	    }
	}
    }
}

snit::widgetadaptor widget::dateentry {
    delegate option * to hull
    delegate method * to hull

    option -command      -default {}
    option -dateformat   -default "%m/%d/%Y"    -configuremethod C-passtocalendar
    option -font         -default {Helvetica 9} -configuremethod C-passtocalendar
    option -textvariable -default {}            -configuremethod C-textvariable
    option -language     -default en            -configuremethod C-passtocalendar

    delegate option -highlightcolor to calendar
    delegate option -shadecolor     to calendar
    delegate option -firstday       to calendar
    delegate option -showpast       to calendar

    component dropbox
    component calendar

    variable formattedDate	;# Chosen date, formatted, linked to calendar, shown in entry
    variable rawDate		;# Same, as seconds.
    variable startOnMonday 1	;# !! Unused

    constructor args {
	::widget::createdateentryLayout

	installhull using ttk::entry -style dateentry

	bindtags $win [linsert [bindtags $win] 1 TDateEntry]

	$self MakeCalendar
	$self configurelist $args

	# Initialize entry to current date, midnight
	set rawDate       [expr {([clock seconds] / 86400) * 86400}]
	set formattedDate [clock format $rawDate -format $options(-dateformat)]

	$self UpdateEntry
    }

    destructor {
	# Drop link to outer textvariable
	$self configure -textvariable {}
    }
    
    method C-passtocalendar {option value} {
	set options($option) $value
	$calendar configure $option $value
    }

    method C-textvariable {option value} {
	if {$options(-textvariable) ne {}} {
	    trace remove variable $options(-textvariable) write [mymethod DateSet]
	}
	set options($option) $value
	if {$options(-textvariable) ne {}} {
	    trace add variable $options(-textvariable) write [mymethod DateSet]
	}	
    }
    
    method MakeCalendar {args} {
	set dropbox $win.__drop
	destroy $dropbox
	toplevel $dropbox -takefocus 0
	wm withdraw $dropbox

	if {[tk windowingsystem] ne "aqua"} {
	    wm overrideredirect $dropbox 1
	    wm transient $dropbox [winfo toplevel $win]
	    wm group     $dropbox [winfo parent $win]
	} else {
	    tk::unsupported::MacWindowStyle style $dropbox \
		help {noActivates hideOnSuspend}
	}
	wm resizable $dropbox 0 0

	# Unpost on Escape or whenever user clicks outside the dropdown
	bind $dropbox <Escape> [list $win unpost]
	bind $dropbox <ButtonPress> [subst -nocommands {
	    if {[string first "$dropbox" [winfo containing %X %Y]] != 0} {
		$win unpost
            }
	}]
	bindtags $dropbox [linsert [bindtags $dropbox] 1 TDateEntryPopdown]

	set calendar $dropbox.calendar
	widget::calendar $calendar \
	    -textvariable   [myvar formattedDate] \
	    -dateformat     $options(-dateformat) \
	    -font           $options(-font) \
	    -language       $options(-language)\
	    -borderwidth    1 \
	    -relief         solid \
            -enablecmdonkey 0 \
	    -command        [mymethod DateChosen]

	bind $calendar <Map> [list focus -force $calendar]
	pack $calendar -expand 1 -fill both

	return $dropbox
    }

    method set {date} {
	# Run the incoming value through scan to ensure that it has the proper format.
	set rawDate       [clock scan   $date     -format $options(-dateformat)]
	set formattedDate [clock format $rawDate  -format $options(-dateformat)]
	$self UpdateEntry
	return
    }
    
    method post { args } {
	# TODO TCL 8.5+: `"disabled" in [$self state]`
	if {[lsearch -exact [$self state] "disabled"] >= 0} {
	    return
	}
	
	# XXX should we reset date on each display?
	if {![winfo exists $dropbox]} { $self MakeCalendar }

	foreach {x y} [$self PostPosition] { break }
	wm geometry $dropbox "+$x+$y"
	wm deiconify $dropbox
	raise $dropbox

	if {[tk windowingsystem] ne "aqua"} {
	    tkwait visibility $dropbox
	}

	focus -force $calendar
	return
    }

    method unpost {args} {
	if {![winfo exists $dropbox]} return
	wm withdraw  $dropbox
	grab release $dropbox ; # just in case
	return
    }

    method PostPosition {} {
	# PostPosition --
	#	Returns the x and y coordinates where the menu
	#	should be posted, based on the dateentry and menu size
	#	and -direction option.
	#
	# TODO: adjust menu width to be at least as wide as the button
	#	for -direction above, below.
	#
	set x [winfo rootx $win]
	set y [winfo rooty $win]
	set dir "below" ; #[$win cget -direction]

	set bw [winfo width $win]
	set bh [winfo height $win]
	set mw [winfo reqwidth $dropbox]
	set mh [winfo reqheight $dropbox]
	set sw [expr {[winfo screenwidth  $dropbox] - $bw - $mw}]
	set sh [expr {[winfo screenheight $dropbox] - $bh - $mh}]

	switch -- $dir {
	    above { if {$y >= $mh} { incr y -$mh } { incr y  $bh } }
	    below { if {$y <= $sh} { incr y  $bh } { incr y -$mh } }
	    left  { if {$x >= $mw} { incr x -$mw } { incr x  $bw } }
	    right { if {$x <= $sw} { incr x  $bw } { incr x -$mw } }
	}

	return [list $x $y]
    }

    #
    #  DateChosen --
    #
    #  Called from the calendar when a date was selected.
    #
    #  Formats the date, calls the callback -command if specified and
    #  then updates the entry.
    #
    ##
    method DateChosen { args } {
	$self UpdateEntry

	# synch raw date - Ensures that chosen format is held to
	set rawDate [clock scan $formattedDate -format $options(-dateformat)]
	
	# Export to linked variable
	upvar 0  $options(-textvariable) date
	set date $formattedDate

	# Export via callback
	$self CallCommand

        $self unpost
	return
    }

    # Handle changes to the contents of the linked -textvariable
    method DateSet {n1 n2 op} {
	upvar #0 $options(-textvariable) date
	# ignore non-changes
	if {$date eq $formattedDate} return
	# pass into the system
	$self set $date
	return
    }
    
    method CallCommand {} {
	if {![llength $options(-command)]} return
	uplevel \#0 $options(-command) [list $formattedDate] $rawDate
    }
    
    method UpdateEntry {} {
	$hull configure -state normal
	$hull delete 0 end
	$hull insert end $formattedDate
	$hull configure -state readonly
	return
    }    
}

# Bindings for menu portion.
#
# This is a variant of the ttk menubutton.tcl bindings.
# See menubutton.tcl for detailed behavior info.
#

bind TDateEntry <Enter>     { %W state active }
bind TDateEntry <Leave>     { %W state !active }
bind TDateEntry <<Invoke>>  { %W post }
bind TDateEntry <Control-space> { %W post }
bind TDateEntry <Escape>        { %W unpost }

bind TDateEntry <ButtonPress-1> { %W state pressed ; %W post }
bind TDateEntry <ButtonRelease-1> { %W state !pressed }

# These are to get around issues on aqua (see ttk::combobox bindings)
bind TDateEntryPopdown <Map> { ttk::globalGrab %W }
bind TDateEntryPopdown <Unmap> { ttk::releaseGrab %W }

package provide widget::dateentry 0.98

##############
# TEST CODE ##
##############

# PhG: this does not work in R
#if { [info script] eq $argv0 } {
#    set auto_path [linsert $auto_path 0 [file dirname [info script]]]
#    package require widget::dateentry
#    destroy {*}[winfo children .]
#    proc getDate { args } {
#	puts [info level 0]
#	puts "DATE $::DATE"
#	update idle
#    }
#
#    # Samples
#    # package require widget::dateentry
#    set ::DATE ""
#    set start [widget::dateentry .s -textvariable ::DATE \
#		   -dateformat "%d.%m.%Y %H:%M" \
#		   -command [list getDate .s]]
#    set end [widget::dateentry .e \
#		 -command [list getDate .e] \
#		 -highlightcolor dimgrey \
#		 -font {Courier 10} \
#		 -firstday sunday]
#    grid [label .sl -text "Start:"] $start  -padx 4 -pady 4
#    grid [label .el -text "End:"  ] $end    -padx 4 -pady 4
#
#    puts [$end get]
#}