3 # ----------------------------------------------------------------------
4 # Implements a quicken style date entry field with a popup calendar
5 # by combining the datefield and calendar widgets together. This
6 # allows a user to enter the date via the keyboard or by using the
7 # mouse by selecting the calendar icon which brings up a popup calendar.
8 # ----------------------------------------------------------------------
9 # AUTHOR: Mark L. Ulferts E-mail: mulferts@austin.dsccc.com
12 # ----------------------------------------------------------------------
13 # Copyright (c) 1997 DSC Technologies Corporation
14 # ======================================================================
15 # Permission to use, copy, modify, distribute and license this software
16 # and its documentation for any purpose, and without fee or written
17 # agreement with DSC, is hereby granted, provided that the above copyright
18 # notice appears in all copies and that both the copyright notice and
19 # warranty disclaimer below appear in supporting documentation, and that
20 # the names of DSC Technologies Corporation or DSC Communications
21 # Corporation not be used in advertising or publicity pertaining to the
22 # software without specific, written prior permission.
24 # DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
26 # INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
27 # AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE,
28 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL
29 # DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
30 # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
31 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
32 # ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
34 # ======================================================================
39 itk::usual Dateentry {
40 keep -background -borderwidth -currentdatefont -cursor \
41 -datefont -dayfont -foreground -highlightcolor \
42 -highlightthickness -labelfont -textbackground -textfont \
46 # ------------------------------------------------------------------
48 # ------------------------------------------------------------------
49 class iwidgets::Dateentry {
50 inherit iwidgets::Datefield
54 itk_option define -grab grab Grab "global"
55 itk_option define -icon icon Icon {}
58 # The calendar widget isn't created until needed, yet we need
59 # its options to be available upon creation of a dateentry widget.
60 # So, we'll define them in these class now so they can just be
61 # propagated onto the calendar later.
63 itk_option define -days days Days {Su Mo Tu We Th Fr Sa}
64 itk_option define -forwardimage forwardImage Image {}
65 itk_option define -backwardimage backwardImage Image {}
66 itk_option define -weekdaybackground weekdayBackground Background \#d9d9d9
67 itk_option define -weekendbackground weekendBackground Background \#d9d9d9
68 itk_option define -outline outline Outline \#d9d9d9
69 itk_option define -buttonforeground buttonForeground Foreground blue
70 itk_option define -foreground foreground Foreground black
71 itk_option define -selectcolor selectColor Foreground red
72 itk_option define -selectthickness selectThickness SelectThickness 3
73 itk_option define -titlefont titleFont Font \
74 -*-helvetica-bold-r-normal--*-140-*
75 itk_option define -dayfont dayFont Font \
76 -*-helvetica-medium-r-normal--*-120-*
77 itk_option define -datefont dateFont Font \
78 -*-helvetica-medium-r-normal--*-120-*
79 itk_option define -currentdatefont currentDateFont Font \
80 -*-helvetica-bold-r-normal--*-120-*
81 itk_option define -startday startDay Day sunday
82 itk_option define -height height Height 165
83 itk_option define -width width Width 200
84 itk_option define -state state State normal
87 method _getPopupDate {date}
88 method _releaseGrab {}
89 method _releaseGrabCheck {rootx rooty}
91 method _getDefaultIcon {}
93 common _defaultIcon ""
98 # Provide a lowercased access method for the dateentry class.
100 proc ::iwidgets::dateentry {pathName args} {
101 uplevel ::iwidgets::Dateentry $pathName $args
104 # ------------------------------------------------------------------
106 # ------------------------------------------------------------------
107 body iwidgets::Dateentry::constructor {args} {
109 # Create an icon label to act as a button to bring up the
112 itk_component add iconbutton {
113 label $itk_interior.iconbutton -relief raised
115 keep -borderwidth -cursor -foreground
117 grid $itk_component(iconbutton) -row 0 -column 0 -sticky ns
120 # Initialize the widget based on the command line options.
122 eval itk_initialize $args
125 # ------------------------------------------------------------------
127 # ------------------------------------------------------------------
129 # ------------------------------------------------------------------
132 # Specifies the calendar icon image to be used in the date.
133 # Should one not be provided, then a default pixmap will be used
134 # if possible, bitmap otherwise.
135 # ------------------------------------------------------------------
136 configbody iwidgets::Dateentry::icon {
137 if {$itk_option(-icon) == {}} {
138 $itk_component(iconbutton) configure -image [_getDefaultIcon]
140 if {[lsearch [image names] $itk_option(-icon)] == -1} {
141 error "bad icon option \"$itk_option(-icon)\":\
142 should be an existing image"
144 $itk_component(iconbutton) configure -image $itk_option(-icon)
149 # ------------------------------------------------------------------
152 # Specifies the grab level, local or global, to be obtained when
153 # bringing up the popup calendar. The default is global.
154 # ------------------------------------------------------------------
155 configbody iwidgets::Dateentry::grab {
156 switch -- $itk_option(-grab) {
157 "local" - "global" {}
159 error "bad grab option \"$itk_option(-grab)\":\
160 should be local or global"
165 # ------------------------------------------------------------------
168 # Specifies the state of the widget which may be disabled or
169 # normal. A disabled state prevents selection of the date field
170 # or date icon button.
171 # ------------------------------------------------------------------
172 configbody iwidgets::Dateentry::state {
173 switch -- $itk_option(-state) {
175 bind $itk_component(iconbutton) <Button-1> [code $this _popup]
178 bind $itk_component(iconbutton) <Button-1> {}
183 # ------------------------------------------------------------------
185 # ------------------------------------------------------------------
187 # ------------------------------------------------------------------
188 # PROTECTED METHOD: _getDefaultIcon
190 # This method is invoked uto retrieve the name of the default icon
191 # image displayed in the icon button.
192 # ------------------------------------------------------------------
193 body iwidgets::Dateentry::_getDefaultIcon {} {
194 if {[lsearch [image types] pixmap] != -1} {
195 set _defaultIcon [image create pixmap -data {
197 static char *calendar[] = {
198 /* width height num_colors chars_per_pixel */
208 "d##########d###########dd",
209 "d#ccccccccc##ccccccccca#d",
210 "##ccccccccc.#ccccccccc..#",
211 "##cccbbcccca#cccbbbccca.#",
212 "##cccbbcccc.#ccbbbbbcc..#",
213 "##cccbbccc####ccccbbcc..#",
214 "##cccbbcccca#ccccbbbcca.#",
215 "##cccbbcccc.#cccbbbccc..#",
216 "##cccbbcccca#ccbbbcccca.#",
217 "##cccbbbccc.#ccbbbbbcc..#",
218 "##ccccccccc.#ccccccccc..#",
219 "##ccccccccca#ccccccccca.#",
220 "##cc#####c#cd#c#####cc..#",
221 "##cccccccc####cccccccca.#",
222 "##cc#####cc.#cc#####cc..#",
223 "##ccccccccc.#ccccccccc..#",
224 "##ccccccccc.#ccccccccc..#",
225 "##..........#...........#",
226 "###..........#..........#",
227 "#########################"
231 set _defaultIcon [image create bitmap -data {
232 #define calendr2_width 25
233 #define calendr2_height 20
234 static char calendr2_bits[] = {
235 0xfe,0xf7,0x7f,0xfe,0x02,0x18,0xc0,0xfe,0x03,
236 0x18,0x80,0xff,0x63,0x10,0x47,0xff,0x43,0x98,
237 0x8a,0xff,0x63,0x3c,0x4c,0xff,0x43,0x10,0x8a,
238 0xff,0x63,0x18,0x47,0xff,0x23,0x90,0x81,0xff,
239 0xe3,0x98,0x4e,0xff,0x03,0x10,0x80,0xff,0x03,
240 0x10,0x40,0xff,0xf3,0xa5,0x8f,0xff,0x03,0x3c,
241 0x40,0xff,0xf3,0x99,0x8f,0xff,0x03,0x10,0x40,
242 0xff,0x03,0x18,0x80,0xff,0x57,0x55,0x55,0xff,
243 0x57,0xb5,0xaa,0xff,0xff,0xff,0xff,0xff};
248 # Since this image will only need to be created once, we redefine
249 # this method to just return the image name for subsequent calls.
251 body ::iwidgets::Dateentry::_getDefaultIcon {} {
258 # ------------------------------------------------------------------
259 # PROTECTED METHOD: _popup
261 # This method is invoked upon selection of the icon button. It
262 # creates a calendar widget within a toplevel popup, calculates
263 # the position at which to display the calendar, performs a grab
264 # and displays the calendar.
265 # ------------------------------------------------------------------
266 body iwidgets::Dateentry::_popup {} {
268 # First, let's nullify the icon binding so that any another
269 # selections are ignored until were done with this one. Next,
270 # change the relief of the icon.
272 bind $itk_component(iconbutton) <Button-1> {}
273 $itk_component(iconbutton) configure -relief sunken
276 # Create a withdrawn toplevel widget and remove the window
277 # decoration via override redirect.
279 itk_component add -private popup {
280 toplevel $itk_interior.popup
282 $itk_component(popup) configure -borderwidth 2 -background black
283 wm withdraw $itk_component(popup)
284 wm overrideredirect $itk_component(popup) 1
287 # Add a binding to button 1 events in order to detect mouse
288 # clicks off the calendar in which case we'll release the grab.
289 # Also add a binding for Escape to always release.
291 bind $itk_component(popup) <1> [code $this _releaseGrabCheck %X %Y]
292 bind $itk_component(popup) <KeyPress-Escape> [code $this _releaseGrab]
295 # Create the calendar widget and set its cursor properly.
297 itk_component add calendar {
298 iwidgets::Calendar $itk_component(popup).calendar \
299 -command [code $this _getPopupDate %d]
302 keep -days -forwardimage -backwardimage -weekdaybackground \
303 -weekendbackground -outline -buttonforeground -selectcolor \
304 -selectthickness -titlefont -dayfont -datefont \
305 -currentdatefont -startday -width -height
307 grid $itk_component(calendar) -row 0 -column 0
308 $itk_component(calendar) configure -cursor top_left_arrow
311 # The icon button will be used as the basis for the position of the
312 # popup on the screen. We'll always attempt to locate the popup
313 # off the lower right corner of the button. If that would put
314 # the popup off the screen, then we'll put above the upper left.
316 set rootx [winfo rootx $itk_component(iconbutton)]
317 set rooty [winfo rooty $itk_component(iconbutton)]
318 set popupwidth [winfo reqwidth $itk_component(popup)]
319 set popupheight [winfo reqheight $itk_component(popup)]
321 set popupx [expr $rootx + 3 + \
322 [winfo width $itk_component(iconbutton)]]
323 set popupy [expr $rooty + 3 + \
324 [winfo height $itk_component(iconbutton)]]
326 if {([expr $popupx + $popupwidth] > [winfo screenwidth .]) || \
327 ([expr $popupy + $popupheight] > [winfo screenheight .])} {
328 set popupx [expr $rootx - 3 - $popupwidth]
329 set popupy [expr $rooty - 3 - $popupheight]
333 # Get the current date from the datefield widget and both
334 # show and select it on the calendar.
336 $itk_component(calendar) show [get]
337 $itk_component(calendar) select [get]
340 # Display the popup at the calculated position.
342 wm geometry $itk_component(popup) +$popupx+$popupy
343 wm deiconify $itk_component(popup)
344 tkwait visibility $itk_component(popup)
347 # Perform either a local or global grab based on the -grab option.
349 if {$itk_option(-grab) == "local"} {
350 grab $itk_component(popup)
352 grab -global $itk_component(popup)
356 # Make sure the widget is above all others and give it focus.
358 raise $itk_component(popup)
359 focus $itk_component(calendar)
362 # ------------------------------------------------------------------
363 # PROTECTED METHOD: _popupGetDate
365 # This method is the callback for selection of a date on the
366 # calendar. It releases the grab and sets the date in the
368 # ------------------------------------------------------------------
369 body iwidgets::Dateentry::_getPopupDate {date} {
374 # ------------------------------------------------------------------
375 # PROTECTED METHOD: _releaseGrabCheck rootx rooty
377 # This method handles mouse button 1 events. If the selection
378 # occured within the bounds of the calendar, then return normally
379 # and let the calendar handle the event. Otherwise, we'll drop
380 # the calendar and release the grab.
381 # ------------------------------------------------------------------
382 body iwidgets::Dateentry::_releaseGrabCheck {rootx rooty} {
383 set calx [winfo rootx $itk_component(calendar)]
384 set caly [winfo rooty $itk_component(calendar)]
385 set calwidth [winfo reqwidth $itk_component(calendar)]
386 set calheight [winfo reqheight $itk_component(calendar)]
388 if {($rootx < $calx) || ($rootx > [expr $calx + $calwidth]) || \
389 ($rooty < $caly) || ($rooty > [expr $caly + $calheight])} {
395 # ------------------------------------------------------------------
396 # PROTECTED METHOD: _releaseGrab
398 # This method releases the grab, destroys the popup, changes the
399 # relief of the button back to raised and reapplies the binding
400 # to the icon button that engages the popup action.
401 # ------------------------------------------------------------------
402 body iwidgets::Dateentry::_releaseGrab {} {
403 grab release $itk_component(popup)
404 $itk_component(iconbutton) configure -relief raised
405 destroy $itk_component(popup)
406 bind $itk_component(iconbutton) <Button-1> [code $this _popup]