New ir method failback: called on disconnect/protocol error.
[ir-tcl-moved-to-github.git] / client.tcl
1 #
2 # $Log: client.tcl,v $
3 # Revision 1.15  1995-03-28 12:45:22  adam
4 # New ir method failback: called on disconnect/protocol error.
5 # New ir set/get method: protocol: SR / Z3950.
6 # Simple popup and disconnect when failback is invoked.
7 #
8 # Revision 1.14  1995/03/22  16:07:55  adam
9 # Minor changes.
10 #
11 # Revision 1.13  1995/03/21  17:27:26  adam
12 # Short-hand keys in setup.
13 #
14 # Revision 1.12  1995/03/21  13:41:03  adam
15 # Comstack cs_create not used too often. Non-blocking connect.
16 #
17 # Revision 1.11  1995/03/21  10:39:06  adam
18 # Diagnostic error message displayed with tkerror.
19 #
20 # Revision 1.10  1995/03/20  15:24:06  adam
21 # Diagnostic records saved on searchResponse.
22 #
23 # Revision 1.9  1995/03/17  18:26:16  adam
24 # Non-blocking i/o used now. Database names popup as cascade items.
25 #
26 # Revision 1.8  1995/03/17  15:45:00  adam
27 # Improved target/database setup.
28 #
29 # Revision 1.7  1995/03/16  17:54:03  adam
30 # Minor changes really.
31 #
32 # Revision 1.6  1995/03/15  19:10:20  adam
33 # Database setup in protocol-setup (rather target setup).
34 #
35 # Revision 1.5  1995/03/15  13:59:23  adam
36 # Minor changes.
37 #
38 # Revision 1.4  1995/03/14  17:32:29  adam
39 # Presentation of full Marc record in popup window.
40 #
41 # Revision 1.3  1995/03/12  19:31:52  adam
42 # Pattern matching implemented when retrieving MARC records. More
43 # diagnostic functions.
44 #
45 # Revision 1.2  1995/03/10  18:00:15  adam
46 # Actual presentation in line-by-line format. RPN query support.
47 #
48 # Revision 1.1  1995/03/09  16:15:07  adam
49 # First presentRequest attempts. Hot-target list.
50 #
51 #
52 set hotTargets {}
53 set hotInfo {}
54 set busy 0
55
56 set profile(Default) {{} {} {210} {} 16384 8192 tcpip {}}
57 set hostid Default
58 set settingsChanged 0
59 set setNo 0
60
61 wm minsize . 300 200
62
63 if {[file readable "~/.tk-c"]} {
64     source "~/.tk-c"
65 }
66
67 proc top-down-window {w} {
68     frame $w.top -relief raised -border 1
69     frame $w.bot -relief raised -border 1
70     
71     pack  $w.top $w.bot -side top -fill both -expand yes
72 }
73
74 proc top-down-ok-cancel {w ok-action g} {
75     frame $w.bot.left -relief sunken -border 1
76     pack $w.bot.left -side left -expand yes -padx 5 -pady 5
77     button $w.bot.left.ok -width 6 -text {Ok} \
78             -command ${ok-action}
79     pack $w.bot.left.ok -expand yes -padx 3 -pady 3
80     button $w.bot.cancel -width 6 -text {Cancel} \
81             -command "destroy $w"
82     pack $w.bot.cancel -side left -expand yes    
83
84     if {$g} {
85         # Grab ...
86         grab $w
87         tkwait window $w
88     }
89 }
90
91 proc show-target {target} {
92     .bot.target configure -text "$target"
93 }
94
95 proc show-busy {v1 v2} {
96     global busy
97     if {$busy != 0} {
98         .bot.status configure -fg $v1
99         after 200 [list show-busy $v2 $v1]
100     }
101 }
102         
103 proc show-status {status b} {
104     global busy
105     global statusbg
106     .bot.status configure -text "$status"
107     .bot.status configure -fg black
108     if {$b != 0} {
109         if {$busy == 0} {
110             set busy $b   
111             show-busy red blue
112         }
113         #        . config -cursor {watch black white}
114     } else {
115         #        . config -cursor {top_left_arrow black white}
116         puts "Normal"
117     }
118     set busy $b
119 }
120
121 proc show-message {msg} {
122     .bot.message configure -text "$msg"
123 }
124
125 proc insertWithTags {w text args} {
126     set start [$w index insert]
127     $w insert insert $text
128     foreach tag [$w tag names $start] {
129         $w tag remove $tag $start insert
130     }
131     foreach i $args {
132         $w tag add $i $start insert
133     }
134 }
135
136 proc show-full-marc {no} {
137     global setNo
138
139     set w .full-marc
140
141     if {[winfo exists $w]} {
142         $w.top.record delete 0.0 end
143         set new 0
144     } else {
145
146         toplevel $w
147
148         wm minsize $w 200 200
149         
150         frame $w.top -relief raised -border 1
151         frame $w.bot -relief raised -border 1
152
153         pack  $w.top -side top -fill both -expand yes
154         pack  $w.bot -fill both
155
156         text $w.top.record -width 60 -height 12 -wrap word \
157                 -yscrollcommand [list $w.top.s set]
158         scrollbar $w.top.s -command [list $w.top.record yview]
159
160         set new 1
161     }
162     incr no
163     
164     set r [z39.$setNo recordMarc $no line * * *]
165
166     $w.top.record tag configure marc-tag -foreground blue
167     $w.top.record tag configure marc-data -foreground black
168     $w.top.record tag configure marc-id -foreground red
169
170     foreach line $r {
171         set tag [lindex $line 0]
172         set indicator [lindex $line 1]
173         set fields [lindex $line 2]
174
175         if {$indicator != ""} {
176             insertWithTags $w.top.record "$tag $indicator" marc-tag
177         } else {
178             insertWithTags $w.top.record "$tag    " marc-tag
179         }
180         foreach field $fields {
181             set id [lindex $field 0]
182             set data [lindex $field 1]
183             if {$id != ""} {
184                 insertWithTags $w.top.record " $id " marc-id
185             }
186             set start [$w.top.record index insert]
187             insertWithTags $w.top.record $data {}
188         }
189         $w.top.record insert end "\n"
190     }
191     if {$new} {
192         bind $w <Return> {destroy .full-marc}
193         
194         pack $w.top.s -side right -fill y
195         pack $w.top.record -expand yes -fill both
196         
197         frame $w.bot.left -relief sunken -border 1
198         pack $w.bot.left -side left -expand yes -padx 5 -pady 5
199         button $w.bot.left.close -width 6 -text {Close} \
200                 -command {destroy .full-marc}
201         pack $w.bot.left.close -expand yes -padx 3 -pady 3
202         button $w.bot.edit -width 6 -text {Edit} \
203                 -command {destroy .full-marc}
204         pack $w.bot.edit -side left -expand yes
205     }
206 }
207
208 proc update-target-hotlist {target} {
209     global hotTargets
210
211     set len [llength $hotTargets]
212     if {$len > 0} {
213         .top.target.m delete 5 [expr 5+[llength $hotTargets]]
214     }
215     set indx [lsearch $hotTargets $target]
216     if {$indx >= 0} {
217         set hotTargets [lreplace $hotTargets $indx $indx]
218     }
219     set hotTargets [linsert $hotTargets 0 $target]
220     set-target-hotlist    
221
222
223 proc set-target-hotlist {} {
224     global hotTargets
225     
226     set i 1
227     foreach target $hotTargets {
228         .top.target.m add command -label "$i $target" -command \
229                 "menu-open-target $target {}"
230         incr i
231         if {$i > 8} {
232              break
233         }
234     }
235 }
236
237 proc menu-open-target {target base} {
238     open-target $target $base
239     update-target-hotlist $target
240 }
241
242 proc define-target-action {} {
243     global profile
244
245     set target [.target-define.top.target.entry get]
246     if {$target == ""} {
247         return
248     }
249     update-target-hotlist $target
250     foreach n [array names profile] {
251         if {$n == $target} {
252             protocol-setup $n
253             return
254         }
255     }
256     set profile($target) $profile(Default)
257     protocol-setup $target
258     destroy .target-define
259 }
260
261 proc fail-response {target} {
262     close-target
263     tkerror "Target connection closed or protocol error"
264 }
265
266 proc connect-response {target} {
267     puts "connect-response"
268     show-target $target
269     init-request
270 }
271
272 proc open-target {target base} {
273     global profile
274     global hostid
275
276     z39 disconnect
277     z39 comstack [lindex $profile($target) 6]
278     # z39 idAuthentication [lindex $profile($target) 3]
279     z39 maximumRecordSize [lindex $profile($target) 4]
280     z39 preferredMessageSize [lindex $profile($target) 5]
281     puts -nonewline "maximumRecordSize="
282     puts [z39 maximumRecordSize]
283     puts -nonewline "preferredMessageSize="
284     puts [z39 preferredMessageSize]
285     if {$base == ""} {
286         z39 databaseNames [lindex [lindex $profile($target) 7] 0]
287     } else {
288         z39 databaseNames $base
289     }
290     z39 failback [list fail-response $target]
291     z39 callback [list connect-response $target]
292     z39 connect [lindex $profile($target) 1]:[lindex $profile($target) 2]
293     show-status {Connecting} 1
294     set hostid $target
295     .top.target.m disable 0
296     .top.target.m enable 1
297 }
298
299 proc close-target {} {
300     global hostid
301
302     set hostid Default
303     z39 disconnect
304     show-target {None}
305     show-status {Not connected} 0
306     show-message {}
307     .top.target.m disable 1
308     .top.target.m enable 0
309 }
310
311 proc load-set-action {} {
312     global setNo
313
314     incr setNo
315     ir-set z39.$setNo
316
317     set fname [.load-set.top.filename.entry get]
318     destroy .load-set
319     if {$fname != ""} {
320         init-title-lines
321
322         show-status {Loading} 1
323         z39.$setNo loadFile $fname
324
325         set no [z39.$setNo numberOfRecordsReturned]
326         add-title-lines $setNo $no 1
327     }
328     show-status {Ready} 0
329 }
330
331 proc load-set {} {
332     set w .load-set
333
334     set oldFocus [focus]
335     toplevel $w
336
337     place-force $w .
338
339     top-down-window $w
340
341     frame $w.top.filename
342     
343     pack $w.top.filename -side top -anchor e -pady 2
344     
345     entry-fields $w.top {filename} \
346             {{Filename:}} \
347             {load-set-action} {destroy .load-set}
348     
349     top-down-ok-cancel $w {load-set-action} 1
350     focus $oldFocus
351 }
352
353 proc init-request {} {
354     global setNo
355     
356     z39 callback {init-response}
357     z39 init
358     show-status {Initializing} 1
359     set setNo 0
360 }
361
362 proc init-response {} {
363     show-status {Ready} 0
364     bind .mid.searchentry <Return> search-request
365     focus .mid.searchentry
366 }
367
368 proc search-request {} {
369     global setNo
370     global profile
371     global hostid
372
373     set target $hostid
374
375     incr setNo
376     ir-set z39.$setNo
377
378     if {[lindex $profile($target) 10]} {
379         z39.$setNo setName $setNo
380     }
381     if {[lindex $profile($target) 8]} {
382         z39 query rpn
383     }
384     if {[lindex $profile($target) 9]} {
385         z39 query ccl
386     }
387     z39 callback {search-response}
388     z39.$setNo search [.mid.searchentry get]
389     show-status {Search} 1
390 }
391
392 proc search-response {} {
393     global setNo
394     global setOffset
395     global setMax
396
397     init-title-lines
398     show-status {Ready} 0
399     show-message "[z39.$setNo resultCount] hits"
400     set setMax [z39.$setNo resultCount]
401     puts $setMax
402     if {$setMax == 0} {
403         set status [z39.$setNo responseStatus]
404         if {[lindex $status 0] == "NSD"} {
405             set code [lindex $status 1]
406             set msg [lindex $status 2]
407             set addinfo [lindex $status 3]
408             tkerror "NSD$code: $msg: $addinfo"
409         }
410         return
411     }
412     if {$setMax > 10} {
413         set setMax 10
414     }
415     z39 callback {present-response}
416     set setOffset 1
417     z39.$setNo present 1 $setMax
418     show-status {Retrieve} 1
419 }
420
421 proc init-title-lines {} {
422     .data.list delete 0 end
423 }
424
425 proc add-title-lines {setno no offset} {
426     for {set i 0} {$i < $no} {incr i} {
427         set o [expr $i + $offset]
428         set title [lindex [z39.$setno recordMarc $o field 245 * a] 0]
429         set year  [lindex [z39.$setno recordMarc $o field 260 * c] 0]
430         set nostr [format "%3d" $o]
431         .data.list insert end "$nostr $title - $year"
432     }
433 }
434
435 proc present-response {} {
436     global setNo
437     global setOffset
438     global setMax
439
440     puts "In present-response"
441     set no [z39.$setNo numberOfRecordsReturned]
442     puts "Returned $no records, setOffset $setOffset"
443     add-title-lines $setNo $no $setOffset
444     set setOffset [expr $setOffset + $no]
445     set status [z39.$setNo responseStatus]
446     if {[lindex $status 0] == "NSD"} {
447         show-status {Ready} 0
448         set code [lindex $status 1]
449         set msg [lindex $status 2]
450         set addinfo [lindex $status 3]
451         tkerror "NSD$code: $msg: $addinfo"
452         return
453     }
454     if {$no > 0 && $setOffset <= $setMax} {
455         z39.$setNo present $setOffset [expr $setMax - $setOffset + 1]
456     } else {
457         show-status {Finished} 0
458     }
459 }
460
461 proc left-cursor {w} {
462     set i [$w index insert]
463     if {$i > 0} {
464         incr i -1
465         $w icursor $i
466     }
467 }
468
469 proc right-cursor {w} {
470     set i [$w index insert]
471     incr i
472     $w icursor $i
473 }
474
475 proc bind-fields {list returnAction escapeAction} {
476     set max [expr [llength $list]-1]
477     for {set i 0} {$i < $max} {incr i} {
478         bind [lindex $list $i] <Return> $returnAction
479         bind [lindex $list $i] <Escape> $escapeAction
480         bind [lindex $list $i] <Tab> [list focus [lindex $list [expr $i+1]]]
481         bind [lindex $list $i] <Left> [list left-cursor [lindex $list $i]]
482         bind [lindex $list $i] <Right> [list right-cursor [lindex $list $i]]
483     }
484     bind [lindex $list $i] <Return> $returnAction
485     bind [lindex $list $i] <Escape> $escapeAction
486     bind [lindex $list $i] <Tab>    [list focus [lindex $list 0]]
487     bind [lindex $list $i] <Left> [list left-cursor [lindex $list $i]]
488     bind [lindex $list $i] <Right> [list right-cursor [lindex $list $i]]
489     focus [lindex $list 0]
490 }
491
492 proc entry-fields {parent list tlist returnAction escapeAction} {
493     set alist {}
494     set i 0
495     foreach field $list {
496         set label ${parent}.${field}.label
497         set entry ${parent}.${field}.entry
498         label $label -text [lindex $tlist $i] -anchor e
499         entry $entry -width 32 -relief sunken
500         pack $label -side left
501         pack $entry -side right
502         lappend alist $entry
503         incr i
504     }
505     bind-fields $alist $returnAction $escapeAction
506 }
507
508 proc define-target-dialog {} {
509     set w .target-define
510
511     toplevel $w
512
513     place-force $w .
514
515     top-down-window $w
516
517     frame $w.top.target
518
519     pack $w.top.target \
520             -side top -anchor e -pady 2 
521
522     entry-fields $w.top {target} \
523             {{Target:}} \
524             {define-target-action} {destroy .target-define}
525     
526     top-down-ok-cancel $w {define-target-action} 1
527 }
528
529 proc protocol-setup-action {target} {
530     global profile
531     global csRadioType
532     global settingsChanged
533     global RPNCheck
534     global CCLCheck
535     global ResultSetCheck
536
537     set w .setup-${target}.top
538
539     #set w .protocol-setup.top
540     
541     set b {}
542     set settingsChanged 1
543     set len [$w.databases.list size]
544     for {set i 0} {$i < $len} {incr i} {
545         lappend b [$w.databases.list get $i]
546     }
547     set profile($target) [list [$w.description.entry get] \
548             [$w.host.entry get] \
549             [$w.port.entry get] \
550             [$w.idAuthentication.entry get] \
551             [$w.maximumRecordSize.entry get] \
552             [$w.preferredMessageSize.entry get] \
553             $csRadioType \
554             $b \
555             $RPNCheck \
556             $CCLCheck \
557             $ResultSetCheck ]
558
559     cascade-target-list
560     puts $profile($target)
561     destroy .setup-${target}
562 }
563
564
565 proc place-force {window parent} {
566     set g [wm geometry $parent]
567
568     set p1 [string first + $g]
569     set p2 [string last + $g]
570
571     set x [expr 40+[string range $g [expr $p1 +1] [expr $p2 -1]]]
572     set y [expr 60+[string range $g [expr $p2 +1] end]]
573     wm geometry $window +${x}+${y}
574 }
575
576
577 proc add-database-action {target} {
578     set w .setup-${target}
579     
580     ${w}.top.databases.list insert end \
581             [.database-select.top.database.entry get]
582     destroy .database-select
583 }
584
585 proc add-database {target} {
586     set w .database-select
587
588     set oldFocus [focus]
589     toplevel $w
590
591     place-force $w .setup-${target}
592
593     top-down-window $w
594
595     frame $w.top.database
596
597     pack $w.top.database -side top -anchor e -pady 2
598     
599     entry-fields $w.top {database} \
600             {{Database to add:}} \
601             [list add-database-action $target] {destroy .database-select}
602
603     top-down-ok-cancel $w [list add-database-action $target] 1
604     focus $oldFocus
605 }
606
607 proc delete-database {target} {
608     set w .setup-${target}
609     
610     foreach i [lsort -decreasing \
611             [$w.top.databases.list curselection]] {
612         $w.top.databases.list delete $i
613     }
614 }
615
616 proc protocol-setup {target} {
617     set w .setup-$target
618
619     global profile
620     global csRadioType
621     global RPNCheck
622     global CCLCheck
623     global ResultSetCheck
624
625     toplevel $w
626
627     wm title $w "Setup $target"
628     place-force $w .
629
630     top-down-window $w
631     
632     if {$target == ""} {
633         set target Default
634     }
635     puts target
636     puts $profile($target)
637
638     frame $w.top.host
639     frame $w.top.port
640     frame $w.top.description
641     frame $w.top.idAuthentication
642     frame $w.top.maximumRecordSize
643     frame $w.top.preferredMessageSize
644     frame $w.top.cs-type -relief ridge -border 2
645     frame $w.top.query -relief ridge -border 2
646     frame $w.top.databases -relief ridge -border 2
647
648     # Maximum/preferred/idAuth ...
649     pack $w.top.description $w.top.host $w.top.port \
650             $w.top.idAuthentication $w.top.maximumRecordSize \
651             $w.top.preferredMessageSize -side top -anchor e -pady 2
652     
653     entry-fields $w.top {description host port idAuthentication \
654             maximumRecordSize preferredMessageSize} \
655             {{Description:} {Host:} {Port:} {Id Authentification:} \
656             {Maximum Record Size:} {Preferred Message Size:}} \
657             [list protocol-setup-action $target] [list destroy $w]
658     
659     foreach sub {description host port idAuthentication \
660             maximumRecordSize preferredMessageSize} {
661         puts $sub
662         bind $w.top.$sub.entry <Control-a> "add-database $target"
663         bind $w.top.$sub.entry <Control-d> "delete-database $target"
664     }
665     $w.top.description.entry insert 0 [lindex $profile($target) 0]
666     $w.top.host.entry insert 0 [lindex $profile($target) 1]
667     $w.top.port.entry insert 0 [lindex $profile($target) 2]
668     $w.top.idAuthentication.entry insert 0 [lindex $profile($target) 3]
669     $w.top.maximumRecordSize.entry insert 0 [lindex $profile($target) 4]
670     $w.top.preferredMessageSize.entry insert 0 [lindex $profile($target) 5]
671     set csRadioType [lindex $profile($target) 6]
672     set RPNCheck [lindex $profile($target) 8]
673     set CCLCheck [lindex $profile($target) 9]
674     set ResultSetCheck [lindex $profile($target) 10]
675
676     # Databases ....
677     pack $w.top.databases -side left -pady 6 -padx 6 -expand yes -fill x
678
679     label $w.top.databases.label -text "Databases"
680     button $w.top.databases.add -text "Add" \
681             -command "add-database $target"
682     button $w.top.databases.delete -text "Delete" \
683             -command "delete-database $target"
684     listbox $w.top.databases.list -geometry 20x6 \
685             -yscrollcommand "$w.top.databases.scroll set"
686     scrollbar $w.top.databases.scroll -orient vertical -border 1
687     pack $w.top.databases.label -side top -fill x \
688             -padx 2 -pady 2
689     pack $w.top.databases.add $w.top.databases.delete -side top -fill x \
690             -padx 2 -pady 2
691     pack $w.top.databases.list -side left -fill both -expand yes \
692             -padx 2 -pady 2
693     pack $w.top.databases.scroll -side right -fill y \
694             -padx 2 -pady 2
695     $w.top.databases.scroll config -command "$w.top.databases.list yview"
696
697     foreach b [lindex $profile($target) 7] {
698         $w.top.databases.list insert end $b
699     }
700
701     # Transport ...
702     pack $w.top.cs-type -pady 6 -padx 6 -side top
703     
704     label $w.top.cs-type.label -text "Transport" 
705     radiobutton $w.top.cs-type.tcpip -text "TCP/IP" \
706             -command {puts tcp/ip} -variable csRadioType -value tcpip
707     radiobutton $w.top.cs-type.mosi -text "MOSI" \
708             -command {puts mosi} -variable csRadioType -value mosi
709     
710     pack $w.top.cs-type.label $w.top.cs-type.tcpip $w.top.cs-type.mosi \
711             -padx 4 -side top -fill x
712
713     # Query ...
714     pack $w.top.query -pady 6 -padx 6 -side top
715
716     label $w.top.query.label -text "Query support" -anchor e
717     checkbutton $w.top.query.c1 -text "RPN query" -variable RPNCheck
718     checkbutton $w.top.query.c2 -text "CCL query" -variable CCLCheck
719     checkbutton $w.top.query.c3 -text "Result sets" -variable ResultSetCheck
720
721     pack $w.top.query.label -side top 
722     pack $w.top.query.c1 $w.top.query.c2 $w.top.query.c3 \
723             -padx 4 -side top -fill x
724
725     # Ok-cancel
726     top-down-ok-cancel $w [list protocol-setup-action $target] 0
727 }
728
729 proc database-select-action {} {
730     set w .database-select.top
731     set b {}
732     foreach indx [$w.databases.list curselection] {
733         lappend b [$w.databases.list get $indx]
734     }
735     if {$b != ""} {
736         z39 databaseNames $b
737     }
738     destroy .database-select
739 }
740
741 proc database-select {} {
742     set w .database-select
743     global profile
744     global hostid
745
746     toplevel $w
747
748     place-force $w .
749
750     top-down-window $w
751
752     frame $w.top.databases -relief ridge -border 2
753
754     pack $w.top.databases -side left -pady 6 -padx 6 -expand yes -fill x
755
756     label $w.top.databases.label -text "List"
757     listbox $w.top.databases.list -geometry 20x6 \
758             -yscrollcommand "$w.top.databases.scroll set"
759     scrollbar $w.top.databases.scroll -orient vertical -border 1
760     pack $w.top.databases.label -side top -fill x \
761             -padx 2 -pady 2
762     pack $w.top.databases.list -side left -fill both -expand yes \
763             -padx 2 -pady 2
764     pack $w.top.databases.scroll -side right -fill y \
765             -padx 2 -pady 2
766     $w.top.databases.scroll config -command "$w.top.databases.list yview"
767
768     foreach b [lindex $profile($hostid) 7] {
769         $w.top.databases.list insert end $b
770     }
771     top-down-ok-cancel $w {database-select-action} 1
772 }
773
774 proc cascade-target-list {} {
775     global profile
776     
777     foreach sub [winfo children .top.target.m.clist] {
778         puts "deleting $sub"
779         destroy $sub
780     }
781     .top.target.m.clist delete 0 last
782     foreach n [array names profile] {
783         if {$n != "Default"} {
784             set nl [string tolower $n]
785             if {[llength [lindex $profile($n) 7]] > 1} {
786                 .top.target.m.clist add cascade -label $n \
787                         -menu .top.target.m.clist.$nl
788                 menu .top.target.m.clist.$nl
789                 foreach b [lindex $profile($n) 7] {
790                     .top.target.m.clist.$nl add command -label $b \
791                             -command "menu-open-target $n $b"
792                 }
793             } else {
794                 .top.target.m.clist add command -label $n \
795                         -command "menu-open-target $n {}"
796             }
797         }
798     }
799     .top.target.m.slist delete 0 last
800     foreach n [array names profile] {
801         if {$n != "Default"} {
802             .top.target.m.slist add command -label $n \
803                     -command "protocol-setup $n"
804         }
805     }
806 }
807
808 proc save-settings {} {
809     global hotTargets 
810     global profile
811     global settingsChanged
812
813     set f [open "~/.tk-c" w]
814     puts $f "# Setup file"
815     puts $f "set hotTargets \{ $hotTargets \}"
816
817     foreach n [array names profile] {
818         puts -nonewline $f "set profile($n) \{"
819         puts -nonewline $f $profile($n)
820         puts $f "\}"
821     }
822     close $f
823     set settingsChanged 0
824 }
825
826 proc alert {ask} {
827     set w .alert
828
829     global alertAnswer
830
831     toplevel $w
832     place-force $w .
833     top-down-window $w
834
835     message $w.top.message -text $ask
836
837     pack $w.top.message -side left -pady 6 -padx 20 -expand yes -fill x
838   
839     set alertAnswer 0
840     top-down-ok-cancel $w {alert-action} 1
841     return $alertAnswer
842 }
843
844 proc alert-action {} {
845     global alertAnswer
846     set alertAnswer 1
847     destroy .alert
848 }
849
850 proc exit-action {} {
851     global settingsChanged
852
853     if {$settingsChanged} {
854         set a [alert "you havent saved your settings. Do you wish to save?"]
855         if {$a} {
856             save-settings
857         }
858     }
859     destroy .
860 }
861
862 frame .top  -border 1 -relief raised
863 frame .mid  -border 1 -relief raised
864 frame .data -border 1 -relief raised
865 frame .bot  -border 1 -relief raised
866 pack .top .mid -side top -fill x
867 pack .data      -side top -fill both -expand yes
868 pack .bot      -fill x
869
870 menubutton .top.file -text "File" -underline 0 -menu .top.file.m
871 menu .top.file.m
872 .top.file.m add command -label "Save settings" -command {save-settings}
873 .top.file.m add command -label "Load Set" -command {load-set}
874 .top.file.m add separator
875 .top.file.m add command -label "Exit" -command {exit-action}
876
877 menubutton .top.target -text "Target" -underline 0 -menu .top.target.m
878 menu .top.target.m
879 .top.target.m add cascade -label "Connect" -menu .top.target.m.clist
880 .top.target.m add command -label "Disconnect" -command {close-target}
881 #.top.target.m add command -label "Initialize" -command {init-request}
882 .top.target.m add cascade -label "Setup" -menu .top.target.m.slist
883 .top.target.m add command -label "Setup new" -command {define-target-dialog}
884 .top.target.m add separator
885 set-target-hotlist
886
887 .top.target.m disable 1
888
889 menu .top.target.m.clist
890 menu .top.target.m.slist
891 cascade-target-list
892
893 menubutton .top.database -text "Database" -underline 0 -menu .top.database.m
894 menu .top.database.m
895 .top.database.m add command -label "Select ..." -command {database-select}
896 .top.database.m add command -label "Add ..." -command {puts "Add"}
897
898 menubutton .top.help -text "Help" -menu .top.help.m
899 menu .top.help.m
900
901 .top.help.m add command -label "Help on help" -command {puts "Help on help"}
902 .top.help.m add command -label "About" -command {puts "About"}
903
904 pack .top.file .top.target .top.database -side left
905 pack .top.help -side right
906
907 label .mid.searchlabel -text {Search:}
908 entry .mid.searchentry -width 32 -relief sunken
909 pack .mid.searchlabel  -side left
910 pack .mid.searchentry -side left -fill x -expand yes
911
912 focus .mid.searchentry
913 bind .mid.searchentry <Left> {left-cursor .mid.searchentry}
914 bind .mid.searchentry <Right> {right-cursor .mid.searchentry}
915
916 listbox .data.list -yscrollcommand {.data.scroll set}
917 scrollbar .data.scroll -orient vertical -border 1
918 pack .data.list -side left -fill both -expand yes
919 pack .data.scroll -side right -fill y
920 .data.scroll config -command {.data.list yview}
921
922 message .bot.target -text "None" -aspect 1000 -relief sunken -border 1
923 label .bot.status -text "Not connected" -width 12 -relief \
924         sunken -anchor w -border 1
925 label .bot.set -textvariable setNo -width 5 -relief \
926         sunken -anchor w -border 1
927 label .bot.message -text "" -width 14 -relief \
928         sunken -anchor w -border 1
929 pack .bot.target .bot.status .bot.set .bot.message -anchor nw \
930         -side left -padx 2 -pady 2
931
932 bind .data.list <Double-Button-1> {set indx [.data.list nearest %y]
933 show-full-marc $indx}
934
935 ir z39