File: support.tcl

package info (click to toggle)
ical 2.2-6
  • links: PTS
  • area: main
  • in suites: potato
  • size: 1,720 kB
  • ctags: 1,685
  • sloc: cpp: 12,651; tcl: 6,755; sh: 695; makefile: 579; perl: 60; ansic: 27
file content (434 lines) | stat: -rw-r--r-- 12,094 bytes parent folder | download | duplicates (2)
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
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# Copyright (c) 1993 by Sanjay Ghemawat
##############################################################################
# Various Support Routines

# Initialization
set ical_state(views)	{}
set ical_state(view)	{}
set ical_state(clip)	{}
set ical_state(search)	{}

proc support_init {} {
    global month_name ical
    catch {unset month_name}

    set month_name(1)	January
    set month_name(2)	February
    set month_name(3)	March
    set month_name(4)	April
    set month_name(5)	May
    set month_name(6)	June
    set month_name(7)	July
    set month_name(8)	August
    set month_name(9)	September
    set month_name(10)	October
    set month_name(11)	November
    set month_name(12)	December

    global weekday_name
    catch {unset weekday_name}

    set weekday_name(1)	Sunday
    set weekday_name(2)	Monday
    set weekday_name(3)	Tuesday
    set weekday_name(4)	Wednesday
    set weekday_name(5)	Thursday
    set weekday_name(6)	Friday
    set weekday_name(7)	Saturday

    # Parse arguments
    ical_parse_args
    if [string compare [info commands tk] ""] {
	support_init_tk
    }

    # Hooks
    create-hook ical-startup
    create-hook ical-exit

    create-hook item-create
    create-hook todo-item-done
    create-hook alarm-fire
    create-hook	item-select
    create-hook item-unselect

    create-hook dayview-startup
    create-hook dayview-close
    create-hook dayview-set-date

    # Load various customization files
    ical_load_file $ical(libparent)/site.tcl
    ical_load_file $ical(library)/site.tcl
    ical_load_file ~/.tk/ical/user.tcl

    run-hook ical-startup
}

proc support_init_tk {} {
    global ical

    # Generate display type identifier
    set width  [winfo screenwidth .]
    set height [winfo screenheight .]
    set visual [winfo screenvisual .]
    set ical(displaytype) [set visual]_[set width]_[set height]

    # Parse tk arguments before calculating preferences
    ical_parse_tk_args

    # Handle preferences before user code gets loaded.
    pref_init

    # Special handling for background colors
    if [info exists ical(bg)] {tk_setPalette $ical(bg)}

    # Remove default window
    wm withdraw .
}

proc ical_load_file {name} {
    # See if the file exists.  We wrap the "file exists ..." command
    # inside a "catch" to handle errors generated by tilde expansion.
    set e 0
    catch {set e [file exists $name]}
    if $e {
	if [catch {uplevel #0 [list source $name]} msg] {
	    error_notify "" "Error loading \"$name\"\n\n$msg"
	}
    }
}

proc ical_filenames {} {
    set list ""
    lappend list [cal main]
    cal forincludes x {
	lappend list $x
    }
    return $list
}

# Return title for calendar file name
proc ical_title {calendar} {
    # First look in calendar itself
    if ![catch {set title [cal option -calendar $calendar Title]}] {
	return $title
    }

    # No title in calendar:  Generate title from file name.
    if ![string compare $calendar [cal main]] {
	return "Main Calendar"
    } else {
	return [file tail $calendar]
    }
}

# effects - If listName contains specified string, remove it from the list
#	    and return 1.  Else return 0.

proc lremove {listName string} {
    upvar $listName list

    set i [lsearch -exact $list $string]
    if {$i >= 0} {
	set list [lreplace $list $i $i]
	return 1
    } else {
	return 0
    }
}

# requires - item occurs on date.  leader is either "" or a top-level window.
# effects -  If item does not repeat, return "unnecessary".
#
#	     If item repeats, but user interaction says it is ok to
#	     modify all items, return "ok".
#
#	     If item repeats, but user interaction says it is ok to
#	     modify this instance, split item up so that modifications
#	     will only happen to the instance on date, and return "instance".
#
#	     If item repeats, and user interaction cancels the current
#	     operation, return "cancel".

set ical_state(last) {}
set ical_state(modifylast) 0

proc repeat_check {leader item date} {
    if [catch {set cal [$item calendar]}] {return "cancel"}

    global ical_state
    set last $ical_state(last)
    set lastmod $ical_state(modifylast)
    set ical_state(last) $item
    set ical_state(modifylast) 0

    # Nothing to check if item does not repeat
    if ![$item repeats] {return "unnecessary"}

    # See if user already said it was okay to modify this item
    if {![string compare $item $last] && $lastmod} {
	set ical_state(modifylast) 1
	return "unnecessary"
    }

    set result [yes_no_cancel $leader\
		"This item repeats.  Should all occurrences be changed?"\
		"Yes" "Just this one" "Cancel"]

    if {$result == "cancel"} {return "cancel"}
    if {$result == "yes"} {
	set ical_state(modifylast) 1
	return "ok"
    }

    # Split item into two items.  One item only occurs on this
    # date.  The other item covers the rest of the occurrences.

    set copy [$item clone]
    $copy deleteon $date
    $item date $date

    cal add $copy $cal

    return "instance"
}

# effects - Print usage message and exist
proc ical_usage {} {
    puts stderr {Usage: ical [options]
          -calendar <file>          ; Calendar file
          -date <date>              ; Start ical on specified date
          -list                     ; List imminent items
          -show [+<days>]           ; Like "-list" but covers specified range
          -print (1|2|4|8|10|month) ; Generate postscript for specified range
    If on X display --
          -iconic                   ; Start iconified
          -iconposition <x,y>       ; Initial icon position
          -popup                    ; Just display imminent items
          -fg <color>               ; Foreground color
          -bg <color>               ; Background color
          -geometry <geometry>      ; Initial window geometry}
    exit 1
}

# effects - Parse some standard arguments
proc ical_parse_args {} {
    global argv ical
    set mono 0

    set oldargv $argv
    set argv {}
    while {[llength $oldargv] > 0} {
	set arg [lindex $oldargv 0]
	set oldargv [lrange $oldargv 1 end]

	if {![string compare $arg "-calendar"] && ([llength $oldargv] >= 1)} {
	    set ical(calendar) [lindex $oldargv 0]
	    set oldargv [lrange $oldargv 1 end]
	    continue
	}

	if {![string compare $arg "-date"] && ([llength $oldargv] >= 1)} {
	    set spec [lindex $oldargv 0]
	    if [catch {set date [date_parse $spec]} msg] {
		puts stderr "ical: $msg"
		ical_usage
	    }
	    set ical(startdate) $date
	    set oldargv [lrange $oldargv 1 end]
	    continue
	}

	lappend argv $arg
    }
}

# effects - Parse some standard Tk arguments
proc ical_parse_tk_args {} {
    global argv ical

    set oldargv $argv
    set argv {}
    while {[llength $oldargv] > 0} {
	set arg [lindex $oldargv 0]
	set oldargv [lrange $oldargv 1 end]

	if {![string compare $arg "-iconic"]} {
	    set ical(iconic) 1
	    continue
	}

	if {![string compare $arg "-iconposition"] && ([llength $oldargv] >= 1)} {
	    if ![scan [lindex $oldargv 0] "%d,%d" x y] {ical_usage}
	    set ical(iconposition) [list $x $y]
	    set oldargv [lrange $oldargv 1 end]
	    continue
	}

	if {![string compare $arg "-bg"] && ([llength $oldargv] >= 1)} {
	    set ical(bg) [lindex $oldargv 0]
	    set oldargv [lrange $oldargv 1 end]
	    continue
	}

	if {![string compare $arg "-fg"] && ([llength $oldargv] >= 1)} {
	    lappend ical(prefs) "option add *Foreground [lindex $oldargv 0]"
	    set oldargv [lrange $oldargv 1 end]
	    continue
	}

	lappend argv $arg
    }
}

proc date_parse {spec} {
    if {[date extract $spec date j1 j2] && ("x$j1$j2" == "x")} {return $date}
    error "could not parse \"$spec\" as a date"
}

proc tk_support_init {} {
    # Ical specific keys
    global keymap

    # Used during item editing
    set keymap(item) {
	<Control-Key-c>		ical_copy
	<Control-Key-x>		ical_cut_or_hide
	<Control-Key-w>		ical_delete_selection
	<Control-Key-y>		ical_insert_selection
    }

    # Used for invoking commands
    set keymap(command) {
	<Control-Key-v>		ical_paste
	<Key-Left>		ical_last_day
	<Key-Right>		ical_next_day
	<Key-Down>		ical_next_week
	<Key-Up>		ical_last_week
	<Control-Key-s>		ical_search_forward
	<Control-Key-r>		ical_search_backward
	<Key-Escape>		ical_unselect
	<Control-Key-g>		ical_unselect
	<Meta-Key-n>		ical_cycle_through_items
	<Key-Next>		ical_cycle_through_items
	<Key-Tab>		ical_cycle_through_items
    }

    ical_init_bindings

    # Catch calendar reconfigurations
    trigger on reconfig {ical_reconfig_options}
    trigger on keybind {ical_recompute_bindings}
}

proc ical_init_bindings {} {
    # Record current focus window so we can find current view easily
    bind Dayview <FocusIn> {ical_focus_on %W}

    # Do not allow normal tab bindings
    bind Dayview <Tab> {break}

    # Event tag for disabling X coordinate scanning
    bind YScan <2>		{%W scan mark 0 %y; break}
    bind YScan <B2-Motion>	{%W scan dragto 0 %y; break}

    # Configure menu whenever it gets the focus or is mapped
    bind Menu <FocusIn> {ical_config_menu %W}
    bind Menu <Map>	{ical_config_menu %W}
}

proc ical_recompute_bindings {} {
    global keymap

    ical_make_bindings		IcalItem		$keymap(item)
    ical_make_bindings		IcalCommand		$keymap(command)
    itemwindow_make_bindings	IcalItemEditBindings

    # Read any user defined key bindings from calendar
    catch {ical_make_bindings IcalUser [cal option Keybindings]}

    # Append "break" to all non-empty bindings so that after any
    # of these bindings fire, no other binding fires.
    foreach tag {IcalUser IcalItem IcalCommand IcalItemEditBindings} {
	ical_add_bind_breaks $tag
    }
}

# requires	"bindings" is a list of pairs of form {<key> <command>}
# effects	Sets $tag to have the specified set of bindings.
proc ical_make_bindings {tag bindings} {
    foreach s [bind $tag] {bind $tag $s ""}
    foreach {s c} $bindings {bind $tag $s $c}
}

# For each binding associated with "$tag", add a "break" command to
# the end of the binding command.
proc ical_add_bind_breaks {tag} {
    foreach seq [bind $tag] {
	bind $tag $seq "[bind $tag $seq]; break"
    }
}

proc ical_reconfig_options {} {
    global dv_state
    set dv_state(state:overflow)	[cal option AllowOverflow]
    set dv_state(state:ampm)		[cal option AmPm]
    set dv_state(state:mondayfirst)	[cal option MondayFirst]
}

#### Menu-item enabling code ####

# An ical action X can be setup to be disabled or enabled based on the
# value of an entry in the global variable "ical_enable" by setting
# "ical_action_enabler($X)" to the name of entry.
#
# Currently the following entries in "ical_enable" are supported:
#	ical_enable(always)	always set to "normal" 
#	ical_enable(writable)	"normal" iff main calendar is writable
#	ical_enable(item)	"normal" iff an item is selected
#	ical_enable(appt)	"normal" iff an appointment is selected
#	ical_enable(witem)	"normal" iff a writable item is selected
#	ical_enable(wappt)	"normal" iff a writable appointment is selected

# The following proc is called whenever a menu is activated.  It enables
# or disables the menu entries according to the current state of the
# application.
proc ical_config_menu {w} {
    global ical_enable ical_action_enabler

    # Set-up the variables controlling enable/disable
    set ical_enable(always) normal
    set ical_enable(item) disabled
    set ical_enable(appt) disabled
    set ical_enable(witem) disabled
    set ical_enable(wappt) disabled
    set ical_enable(writable) normal

    if ![catch {set item [ical_find_selection]}] {
	set ical_enable(item) normal
	catch {
	    if ![cal readonly [$item calendar]] {
		set ical_enable(witem) normal
	    }
	}
	if [$item is appt] {
	    set ical_enable(appt) normal
	    catch {
		if ![cal readonly [$item calendar]] {
		    set ical_enable(wappt) normal
		}
	    }
	}
    }
    if [cal readonly] {set ical_enable(writable) disabled}

    # Now process menu entries
    set last [$w index last]
    for {set i 0} {$i <= $last} {incr i} {
	catch {
	    set cmd [lindex [$w entrycget $i -command] 0]
	    $w entryconfigure $i\
		-state $ical_enable($ical_action_enabler($cmd))
	}
    }
}