summaryrefslogtreecommitdiff
path: root/itcl/iwidgets3.0.0/generic/dateentry.itk
blob: 5cf648c03b16d82c64c68669748c416f4486ecd8 (plain)
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
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
#
# Dateentry
# ----------------------------------------------------------------------
# Implements a quicken style date entry field with a popup calendar
# by combining the datefield and calendar widgets together.  This
# allows a user to enter the date via the keyboard or by using the
# mouse by selecting the calendar icon which brings up a popup calendar.
# ----------------------------------------------------------------------
#   AUTHOR:  Mark L. Ulferts          E-mail: mulferts@austin.dsccc.com
#
#   @(#) $Id$
# ----------------------------------------------------------------------
#            Copyright (c) 1997 DSC Technologies Corporation
# ======================================================================
# Permission to use, copy, modify, distribute and license this software 
# and its documentation for any purpose, and without fee or written 
# agreement with DSC, is hereby granted, provided that the above copyright 
# notice appears in all copies and that both the copyright notice and 
# warranty disclaimer below appear in supporting documentation, and that 
# the names of DSC Technologies Corporation or DSC Communications 
# Corporation not be used in advertising or publicity pertaining to the 
# software without specific, written prior permission.
# 
# DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
# INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
# AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, 
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL 
# DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
# SOFTWARE.
# ======================================================================

#
# Usual options.
#
itk::usual Dateentry {
    keep -background -borderwidth -currentdatefont -cursor \
	-datefont -dayfont -foreground -highlightcolor \
	-highlightthickness -labelfont -textbackground -textfont \
	-titlefont
}

# ------------------------------------------------------------------
#                              DATEENTRY
# ------------------------------------------------------------------
class iwidgets::Dateentry {
    inherit iwidgets::Datefield
    
    constructor {args} {}

    itk_option define -grab grab Grab "global"
    itk_option define -icon icon Icon {}
    
    #
    # The calendar widget isn't created until needed, yet we need
    # its options to be available upon creation of a dateentry widget.
    # So, we'll define them in these class now so they can just be
    # propagated onto the calendar later.
    #
    itk_option define -days days Days {Su Mo Tu We Th Fr Sa}
    itk_option define -forwardimage forwardImage Image {}
    itk_option define -backwardimage backwardImage Image {}
    itk_option define -weekdaybackground weekdayBackground Background \#d9d9d9
    itk_option define -weekendbackground weekendBackground Background \#d9d9d9
    itk_option define -outline outline Outline \#d9d9d9
    itk_option define -buttonforeground buttonForeground Foreground blue
    itk_option define -foreground foreground Foreground black
    itk_option define -selectcolor selectColor Foreground red
    itk_option define -selectthickness selectThickness SelectThickness 3
    itk_option define -titlefont titleFont Font \
	-*-helvetica-bold-r-normal--*-140-*
    itk_option define -dayfont dayFont Font \
	-*-helvetica-medium-r-normal--*-120-*
    itk_option define -datefont dateFont Font \
	-*-helvetica-medium-r-normal--*-120-*
    itk_option define -currentdatefont currentDateFont Font \
	-*-helvetica-bold-r-normal--*-120-*
    itk_option define -startday startDay Day sunday
    itk_option define -height height Height 165
    itk_option define -width width Width 200
    itk_option define -state state State normal

    protected {
	method _getPopupDate {date}
	method _releaseGrab {}
	method _releaseGrabCheck {rootx rooty}
	method _popup {}
	method _getDefaultIcon {}

        common _defaultIcon ""
    }
}

#
# Provide a lowercased access method for the dateentry class.
# 
proc ::iwidgets::dateentry {pathName args} {
    uplevel ::iwidgets::Dateentry $pathName $args
}

# ------------------------------------------------------------------
#                        CONSTRUCTOR
# ------------------------------------------------------------------
body iwidgets::Dateentry::constructor {args} {
    #
    # Create an icon label to act as a button to bring up the 
    # calendar popup.
    #
    itk_component add iconbutton {
	label $itk_interior.iconbutton -relief raised
    } {
	keep -borderwidth -cursor -foreground 
    }
    grid $itk_component(iconbutton) -row 0 -column 0 -sticky ns
    
    #
    # Initialize the widget based on the command line options.
    #
    eval itk_initialize $args
}

# ------------------------------------------------------------------
#                             OPTIONS
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# OPTION: -icon
#
# Specifies the calendar icon image to be used in the date.
# Should one not be provided, then a default pixmap will be used
# if possible, bitmap otherwise.
# ------------------------------------------------------------------
configbody iwidgets::Dateentry::icon {
    if {$itk_option(-icon) == {}} {
	$itk_component(iconbutton) configure -image [_getDefaultIcon]
    } else {
	if {[lsearch [image names] $itk_option(-icon)] == -1} {
	    error "bad icon option \"$itk_option(-icon)\":\
                   should be an existing image"
	} else {
	    $itk_component(iconbutton) configure -image $itk_option(-icon)
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -grab
#
# Specifies the grab level, local or global, to be obtained when 
# bringing up the popup calendar.  The default is global.
# ------------------------------------------------------------------
configbody iwidgets::Dateentry::grab {
    switch -- $itk_option(-grab) {
	"local" - "global" {}
	default {
	    error "bad grab option \"$itk_option(-grab)\":\
                   should be local or global"
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -state
#
# Specifies the state of the widget which may be disabled or
# normal.  A disabled state prevents selection of the date field
# or date icon button.
# ------------------------------------------------------------------
configbody iwidgets::Dateentry::state {
    switch -- $itk_option(-state) {
	normal {
	    bind $itk_component(iconbutton) <Button-1> [code $this _popup]
	}
	disabled {
	    bind $itk_component(iconbutton) <Button-1> {}
	}
    }
}

# ------------------------------------------------------------------
#                            METHODS
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# PROTECTED METHOD: _getDefaultIcon
#
# This method is invoked uto retrieve the name of the default icon
# image displayed in the icon button.
# ------------------------------------------------------------------
body iwidgets::Dateentry::_getDefaultIcon {} {
    if {[lsearch [image types] pixmap] != -1} {
      set _defaultIcon [image create pixmap -data {
	  /* XPM */
	  static char *calendar[] = {
	  /* width height num_colors chars_per_pixel */
	  "    25    20        6            1",
	  /* colors */
	  ". c #808080",
	  "# c #040404",
	  "a c #848484",
	  "b c #fc0404",
	  "c c #fcfcfc",
	  "d c #c0c0c0",
	  /* pixels */
	  "d##########d###########dd",
	  "d#ccccccccc##ccccccccca#d",
	  "##ccccccccc.#ccccccccc..#",
	  "##cccbbcccca#cccbbbccca.#",
	  "##cccbbcccc.#ccbbbbbcc..#",
	  "##cccbbccc####ccccbbcc..#",
	  "##cccbbcccca#ccccbbbcca.#",
	  "##cccbbcccc.#cccbbbccc..#",
	  "##cccbbcccca#ccbbbcccca.#",
	  "##cccbbbccc.#ccbbbbbcc..#",
	  "##ccccccccc.#ccccccccc..#",
	  "##ccccccccca#ccccccccca.#",
	  "##cc#####c#cd#c#####cc..#",
	  "##cccccccc####cccccccca.#",
	  "##cc#####cc.#cc#####cc..#",
	  "##ccccccccc.#ccccccccc..#",
	  "##ccccccccc.#ccccccccc..#",
	  "##..........#...........#",
	  "###..........#..........#",
	  "#########################"
	 };
	}]
    } else {
	set _defaultIcon [image create bitmap -data {
	    #define calendr2_width 25
	    #define calendr2_height 20
	    static char calendr2_bits[] = {
		0xfe,0xf7,0x7f,0xfe,0x02,0x18,0xc0,0xfe,0x03,
		0x18,0x80,0xff,0x63,0x10,0x47,0xff,0x43,0x98,
		0x8a,0xff,0x63,0x3c,0x4c,0xff,0x43,0x10,0x8a,
		0xff,0x63,0x18,0x47,0xff,0x23,0x90,0x81,0xff,
		0xe3,0x98,0x4e,0xff,0x03,0x10,0x80,0xff,0x03,
		0x10,0x40,0xff,0xf3,0xa5,0x8f,0xff,0x03,0x3c,
		0x40,0xff,0xf3,0x99,0x8f,0xff,0x03,0x10,0x40,
		0xff,0x03,0x18,0x80,0xff,0x57,0x55,0x55,0xff,
		0x57,0xb5,0xaa,0xff,0xff,0xff,0xff,0xff};
        }]
    }

    #
    # Since this image will only need to be created once, we redefine
    # this method to just return the image name for subsequent calls.
    #
    body ::iwidgets::Dateentry::_getDefaultIcon {} {
	return $_defaultIcon
    }

    return $_defaultIcon
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _popup
#
# This method is invoked upon selection of the icon button.  It 
# creates a calendar widget within a toplevel popup, calculates 
# the position at which to display the calendar, performs a grab
# and displays the calendar.
# ------------------------------------------------------------------
body iwidgets::Dateentry::_popup {} {
    #
    # First, let's nullify the icon binding so that any another 
    # selections are ignored until were done with this one.  Next,
    # change the relief of the icon.
    #
    bind $itk_component(iconbutton) <Button-1> {}
    $itk_component(iconbutton) configure -relief sunken

    #
    # Create a withdrawn toplevel widget and remove the window 
    # decoration via override redirect.
    #
    itk_component add -private popup {
	toplevel $itk_interior.popup 
    } 
    $itk_component(popup) configure -borderwidth 2 -background black
    wm withdraw $itk_component(popup)
    wm overrideredirect $itk_component(popup) 1

    #
    # Add a binding to button 1 events in order to detect mouse
    # clicks off the calendar in which case we'll release the grab.
    # Also add a binding for Escape to always release.
    #
    bind $itk_component(popup) <1> [code $this _releaseGrabCheck %X %Y]
    bind $itk_component(popup) <KeyPress-Escape> [code $this _releaseGrab]

    #
    # Create the calendar widget and set its cursor properly.
    #
    itk_component add calendar {
	iwidgets::Calendar $itk_component(popup).calendar \
	    -command [code $this _getPopupDate %d]
    } {
	usual
	keep -days -forwardimage -backwardimage -weekdaybackground \
	    -weekendbackground -outline -buttonforeground -selectcolor \
	    -selectthickness -titlefont -dayfont -datefont \
	    -currentdatefont -startday -width -height
    }
    grid $itk_component(calendar) -row 0 -column 0
    $itk_component(calendar) configure -cursor top_left_arrow

    #
    # The icon button will be used as the basis for the position of the
    # popup on the screen.  We'll always attempt to locate the popup
    # off the lower right corner of the button.  If that would put
    # the popup off the screen, then we'll put above the upper left.
    #
    set rootx [winfo rootx $itk_component(iconbutton)]
    set rooty [winfo rooty $itk_component(iconbutton)]
    set popupwidth [winfo reqwidth $itk_component(popup)]
    set popupheight [winfo reqheight $itk_component(popup)]

    set popupx [expr $rootx + 3 + \
		    [winfo width $itk_component(iconbutton)]]
    set popupy [expr $rooty + 3 + \
		    [winfo height $itk_component(iconbutton)]]

    if {([expr $popupx + $popupwidth] > [winfo screenwidth .]) || \
	    ([expr $popupy + $popupheight] > [winfo screenheight .])} {
	set popupx [expr $rootx - 3 - $popupwidth]
	set popupy [expr $rooty - 3 - $popupheight]
    }
    
    #
    # Get the current date from the datefield widget and both
    # show and select it on the calendar.
    #
    $itk_component(calendar) show [get]
    $itk_component(calendar) select [get]

    #
    # Display the popup at the calculated position.
    #
    wm geometry $itk_component(popup) +$popupx+$popupy
    wm deiconify $itk_component(popup)
    tkwait visibility $itk_component(popup)

    #
    # Perform either a local or global grab based on the -grab option.
    #
    if {$itk_option(-grab) == "local"} {
	grab $itk_component(popup)
    } else {
	grab -global $itk_component(popup)
    }

    #
    # Make sure the widget is above all others and give it focus.
    #
    raise $itk_component(popup)
    focus $itk_component(calendar)
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _popupGetDate
#
# This method is the callback for selection of a date on the 
# calendar.  It releases the grab and sets the date in the
# datefield widget.
# ------------------------------------------------------------------
body iwidgets::Dateentry::_getPopupDate {date} {
    _releaseGrab 
    show $date
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _releaseGrabCheck rootx rooty
#
# This method handles mouse button 1 events.  If the selection
# occured within the bounds of the calendar, then return normally
# and let the calendar handle the event.  Otherwise, we'll drop
# the calendar and release the grab.
# ------------------------------------------------------------------
body iwidgets::Dateentry::_releaseGrabCheck {rootx rooty} {
    set calx [winfo rootx $itk_component(calendar)]
    set caly [winfo rooty $itk_component(calendar)]
    set calwidth [winfo reqwidth $itk_component(calendar)]
    set calheight [winfo reqheight $itk_component(calendar)]

    if {($rootx < $calx) || ($rootx > [expr $calx + $calwidth]) || \
	    ($rooty < $caly) || ($rooty > [expr $caly + $calheight])} {
	_releaseGrab
	return -code break
    }
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _releaseGrab
#
# This method releases the grab, destroys the popup, changes the 
# relief of the button back to raised and reapplies the binding
# to the icon button that engages the popup action.
# ------------------------------------------------------------------
body iwidgets::Dateentry::_releaseGrab {} {
    grab release $itk_component(popup)
    $itk_component(iconbutton) configure -relief raised
    destroy $itk_component(popup) 
    bind $itk_component(iconbutton) <Button-1> [code $this _popup]
}