Text widget used in main window (instead of listbox) to support
[ir-tcl-moved-to-github.git] / client.tcl
1 # IR toolkit for tcl/tk
2 # (c) Index Data 1995
3 # See the file LICENSE for details.
4 # Sebastian Hammer, Adam Dickmeiss
5 #
6 # $Log: client.tcl,v $
7 # Revision 1.35  1995-06-12 15:17:31  adam
8 # Text widget used in main window (instead of listbox) to support
9 # better presentation formats.
10 #
11 # Revision 1.34  1995/06/12  07:59:07  adam
12 # More work on geometry handling.
13 #
14 # Revision 1.33  1995/06/09  11:17:35  adam
15 # Start work on geometry management.
16 #
17 # Revision 1.32  1995/06/07  09:16:37  adam
18 # New presentation format.
19 #
20 # Revision 1.31  1995/06/06  16:31:09  adam
21 # Bug fix: target names couldn't contain blanks.
22 # Bug fix: scan.
23 #
24 # Revision 1.30  1995/06/06  11:35:41  adam
25 # Work on scan. Display of old sets.
26 #
27 # Revision 1.29  1995/06/05  14:11:18  adam
28 # Bug fix in present-more.
29 #
30 # Revision 1.28  1995/06/02  14:52:13  adam
31 # Minor changes really.
32 #
33 # Revision 1.27  1995/06/02  14:29:42  adam
34 # Work on scan interface - up/down buttons.
35 #
36 # Revision 1.26  1995/06/01  16:36:46  adam
37 # About buttons. Minor bug fixes.
38 #
39 # Revision 1.25  1995/05/31  13:09:57  adam
40 # Client searches/presents may be interrupted.
41 # New moving book-logo.
42 #
43 # Revision 1.24  1995/05/31  08:36:24  adam
44 # Bug fix in client.tcl: didn't save options on clientrc.tcl.
45 # New method: referenceId. More work on scan.
46 #
47 # Revision 1.23  1995/05/29  10:33:41  adam
48 # README and rename of startup script.
49 #
50 # Revision 1.22  1995/05/26  11:44:09  adam
51 # Bugs fixed. More work on MARC utilities and queries. Test
52 # client is up-to-date again.
53 #
54 # Revision 1.21  1995/05/11  15:34:46  adam
55 # Scan request changed a bit. This version works with RLG.
56 #
57 # Revision 1.20  1995/04/21  16:31:57  adam
58 # New radiobutton: protocol (z39v2/SR).
59 #
60 # Revision 1.19  1995/04/18  16:11:50  adam
61 # First version of graphical Scan. Some work on query-by-form.
62 #
63 # Revision 1.18  1995/04/10  10:50:22  adam
64 # Result-set name defaults to suffix of ir-set name.
65 # Started working on scan. Not finished at this point.
66 #
67 # Revision 1.17  1995/03/31  09:34:57  adam
68 # Search-button disabled when there is no connection.
69 #
70 # Revision 1.16  1995/03/31  08:56:36  adam
71 # New button "Search".
72 #
73 # Revision 1.15  1995/03/28  12:45:22  adam
74 # New ir method failback: called on disconnect/protocol error.
75 # New ir set/get method: protocol: SR / Z3950.
76 # Simple popup and disconnect when failback is invoked.
77 #
78 # Revision 1.14  1995/03/22  16:07:55  adam
79 # Minor changes.
80 #
81 # Revision 1.13  1995/03/21  17:27:26  adam
82 # Short-hand keys in setup.
83 #
84 # Revision 1.12  1995/03/21  13:41:03  adam
85 # Comstack cs_create not used too often. Non-blocking connect.
86 #
87 # Revision 1.11  1995/03/21  10:39:06  adam
88 # Diagnostic error message displayed with tkerror.
89 #
90 # Revision 1.10  1995/03/20  15:24:06  adam
91 # Diagnostic records saved on searchResponse.
92 #
93 # Revision 1.9  1995/03/17  18:26:16  adam
94 # Non-blocking i/o used now. Database names popup as cascade items.
95 #
96 # Revision 1.8  1995/03/17  15:45:00  adam
97 # Improved target/database setup.
98 #
99 # Revision 1.7  1995/03/16  17:54:03  adam
100 # Minor changes really.
101 #
102 # Revision 1.6  1995/03/15  19:10:20  adam
103 # Database setup in protocol-setup (rather target setup).
104 #
105 # Revision 1.5  1995/03/15  13:59:23  adam
106 # Minor changes.
107 #
108 # Revision 1.4  1995/03/14  17:32:29  adam
109 # Presentation of full Marc record in popup window.
110 #
111 # Revision 1.3  1995/03/12  19:31:52  adam
112 # Pattern matching implemented when retrieving MARC records. More
113 # diagnostic functions.
114 #
115 # Revision 1.2  1995/03/10  18:00:15  adam
116 # Actual presentation in line-by-line format. RPN query support.
117 #
118 # Revision 1.1  1995/03/09  16:15:07  adam
119 # First presentRequest attempts. Hot-target list.
120 #
121 #
122 set hotTargets {}
123 set hotInfo {}
124 set busy 0
125
126 set profile(Default) {{} {} {210} {} 16384 8192 tcpip {} 1 {} {} z39v2}
127 set hostid Default
128 set settingsChanged 0
129 set setNo 0
130 set lastSetNo 0
131 set cancelFlag 0
132 set searchEnable 0
133 set fullMarcSeq 0
134 set displayFormat 1
135
136 set queryTypes {Simple}
137 set queryButtons { { {I 0} {I 1} {I 2} } }
138 set queryInfo { { {Title {1=4}} {Author {1=1}} \
139         {Subject {1=21}} {Any {1=1016}} } }
140 wm minsize . 0 0
141
142 proc read-formats {} {
143     global displayFormats
144     set formats [glob -nocomplain formats/*.tcl]
145     foreach f $formats {
146         source $f
147         set l [expr [string length $f] - 5]
148         lappend displayFormats [string range $f 8 $l]
149     }
150 }
151
152 proc set-display-format {f} {
153     global displayFormat
154     global setNo
155
156     set displayFormat $f
157     if {$setNo == 0} {
158         return
159     }
160     add-title-lines 0 10000 1
161 }
162
163 proc destroyG {w} {
164     global windowGeometry
165     set windowGeometry($w) [wm geometry $w]
166     destroy $w
167 }
168
169 proc toplevelG {w} {
170     global windowGeometry
171
172     toplevel $w
173     if {[info exists windowGeometry($w)]} {
174         set g $windowGeometry($w)
175         if {$g != ""} {
176             wm geometry $w $g
177         }
178     }
179 }
180
181 if {[file readable "clientrc.tcl"]} {
182     source "clientrc.tcl"
183 }
184
185 if {[file readable "clientg.tcl"]} {
186     source "clientg.tcl"
187 }
188
189 set queryButtonsFind [lindex $queryButtons 0]
190 set queryInfoFind [lindex $queryInfo 0]
191
192 proc top-down-window {w} {
193     frame $w.top -relief raised -border 1
194     frame $w.bot -relief raised -border 1
195     
196     pack  $w.top -side top -fill both -expand yes
197     pack  $w.bot -fill both
198 }
199
200 proc top-down-ok-cancel {w ok-action g} {
201     frame $w.bot.left -relief sunken -border 1
202     pack $w.bot.left -side left -expand yes -ipadx 2 -ipady 2 -padx 5 -pady 5
203     button $w.bot.left.ok -width 6 -text {Ok} \
204             -command ${ok-action}
205     pack $w.bot.left.ok -expand yes -ipadx 2 -ipady 2 -padx 3 -pady 3
206     button $w.bot.cancel -width 6 -text {Cancel} \
207             -command [list destroy $w]
208     pack $w.bot.cancel -side left -expand yes    
209
210     if {$g} {
211         grab $w
212         tkwait window $w
213     }
214 }
215
216 proc bottom-buttons {w buttonList g} {
217     set i 0
218     set l [llength $buttonList]
219
220     frame $w.bot.$i -relief sunken -border 1
221     pack $w.bot.$i -side left -expand yes -padx 5 -pady 5
222     button $w.bot.$i.ok -text [lindex $buttonList $i] \
223             -command [lindex $buttonList [expr $i+1]]
224     pack $w.bot.$i.ok -expand yes -ipadx 2 -ipady 2 -padx 3 -pady 3 -side left
225
226     incr i 2
227     while {$i < $l} {
228         button $w.bot.$i -text [lindex $buttonList $i] \
229                 -command [lindex $buttonList [expr $i+1]]
230         pack $w.bot.$i -expand yes -ipadx 2 -ipady 2 -padx 3 -pady 3 -side left
231         incr i 2
232     }
233     if {$g} {
234         # Grab ...
235         grab $w
236         tkwait window $w
237     }
238 }
239
240 proc cancel-operation {} {
241     global cancelFlag
242     global busy
243
244     set cancelFlag 1
245     if {$busy} {
246         show-status Cancelled 0 {}
247     }
248 }
249
250 proc show-target {target} {
251     .bot.a.target configure -text "$target"
252 }
253
254 proc show-logo {v1} {
255     global busy
256     if {$busy != 0} {
257         incr v1 -1
258         if {$v1==0} {
259             set v1 9
260         }
261         .bot.logo configure -bitmap @book${v1}
262         after 140 [list show-logo $v1]
263         return
264     }
265     while {1} {
266         .bot.logo configure -bitmap @book1
267         tkwait variable busy
268         if {$busy} {
269             show-logo 1
270             return
271         }
272     }
273 }
274         
275 proc show-status {status b sb} {
276     global busy
277     global searchEnable
278
279     .bot.a.status configure -text "$status"
280     if {$b == 1} {
281         if {$busy == 0} {set busy 1}
282     } else {
283         set busy 0
284     }
285     if {$sb == {}} {
286         return
287     }
288     if {$sb} {
289         .top.service configure -state normal
290         .mid.search configure -state normal
291         .mid.scan configure -state normal
292         .mid.present configure -state normal
293         if {[winfo exists .scan-window]} {
294             .scan-window.bot.2 configure -state normal
295             .scan-window.bot.4 configure -state normal
296         }
297         set searchEnable 1
298     } else {
299         .top.service configure -state disabled
300         .mid.search configure -state disabled
301         .mid.scan configure -state disabled
302         .mid.present configure -state disabled
303
304         if {[winfo exists .scan-window]} {
305             .scan-window.bot.2 configure -state disabled
306             .scan-window.bot.4 configure -state disabled
307         }
308         set searchEnable 0
309     }
310 }
311
312 proc show-message {msg} {
313     .bot.a.message configure -text "$msg"
314 }
315
316 proc insertWithTags {w text args} {
317     set start [$w index insert]
318     $w insert insert $text
319     foreach tag [$w tag names $start] {
320         $w tag remove $tag $start insert
321     }
322     foreach i $args {
323         $w tag add $i $start insert
324     }
325 }
326
327 proc about-target {} {
328     set w .about-target-w
329
330     toplevelG $w
331
332     wm title $w "About target"
333     top-down-window $w
334
335     set i [z39 targetImplementationName]
336     label $w.top.in -text "Implementation name: $i"
337     set i [z39 targetImplementationId]
338     label $w.top.ii -text "Implementation id: $i"
339     set i [z39 targetImplementationVersion]
340     label $w.top.iv -text "Implementation version: $i"
341     set i [z39 options]
342     label $w.top.op -text "Protocol options: $i"
343
344     pack $w.top.in $w.top.ii $w.top.iv $w.top.op -side top -anchor nw
345
346     bottom-buttons $w [list {Close} [list destroyG $w]] 1
347 }
348
349 proc about-origin {} {
350     set w .about-origin-w
351
352     toplevelG $w
353
354     wm title $w "About IrTcl"
355     place-force $w .
356     top-down-window $w
357
358     set i [z39 implementationName]
359     label $w.top.in -text "Implementation name: $i"
360     set i [z39 implementationId]
361     label $w.top.ii -text "Implementation id: $i"
362
363     pack $w.top.in $w.top.ii -side top -anchor nw
364
365     bottom-buttons $w [list {Close} [list destroyG $w]] 1
366 }
367
368 proc popup-marc {sno no b df} {
369     global fullMarcSeq
370     global displayFormats
371     global popupMarcOdf
372
373     if {[z39.$sno type $no] != "DB"} {
374         return
375     }
376     if {$b} {
377         set w .full-marc-$fullMarcSeq
378         incr fullMarcSeq
379     } else {
380         set w .full-marc
381     }
382     if {[winfo exists $w]} {
383         set new 0
384     } else {
385
386         toplevelG $w
387
388         wm minsize $w 0 0
389         
390         frame $w.top -relief raised -border 1
391         frame $w.bot -relief raised -border 1
392
393         pack  $w.top -side top -fill both -expand yes
394         pack  $w.bot -fill both
395
396         text $w.top.record -width 60 -height 5 -wrap word \
397                 -yscrollcommand [list $w.top.s set]
398         scrollbar $w.top.s -command [list $w.top.record yview]
399
400         if {[tk colormodel .] == "color"} {
401             $w.top.record tag configure marc-tag -foreground blue
402             $w.top.record tag configure marc-id -foreground red
403         } else {
404             $w.top.record tag configure marc-tag -foreground black
405             $w.top.record tag configure marc-id -foreground black
406         }
407         $w.top.record tag configure marc-data -foreground black
408         set new 1
409     }
410     $w.top.record delete 0.0 end
411
412     set ffunc [lindex $displayFormats $df]
413     set ffunc "display-$ffunc"
414
415     $ffunc $sno $no  $w.top.record 0
416
417     if {$new} {
418         bind $w.top.record <Return> {destroy .full-marc}
419         
420         pack $w.top.s -side right -fill y
421         pack $w.top.record -expand yes -fill both
422         
423         if {$b} {
424             bottom-buttons $w [list \
425                 {Close} [list destroy $w] \
426                 {Duplicate} [list popup-marc $sno $no 1 $df]] 0
427             menubutton $w.bot.formats -text "Format" -menu $w.bot.formats.m
428             menu $w.bot.formats.m
429             set i 0
430             foreach f $displayFormats {
431                 $w.bot.formats.m add radiobutton -label $f \
432                         -command [list display-$f $sno $no $w.top.record 0]
433                 incr i
434             }
435             pack $w.bot.formats -expand yes -ipadx 2 -ipady 2 \
436                     -padx 3 -pady 3 -side left
437         } else {
438             bottom-buttons $w [list \
439                 {Close} [list destroyG $w] \
440                 {Duplicate} [list popup-marc $sno $no 1 $df]] 0
441             menubutton $w.bot.formats -text "Format" -menu $w.bot.formats.m
442             menu $w.bot.formats.m
443             set i 0
444             foreach f $displayFormats {
445                 $w.bot.formats.m add radiobutton -label $f \
446                         -command [list display-$f $sno $no $w.top.record 0]
447                 incr i
448             }
449             pack $w.bot.formats -expand yes -ipadx 2 -ipady 2 \
450                     -padx 3 -pady 3 -side left
451         }
452     } else {
453         set i 0
454         foreach f $displayFormats {
455             $w.bot.formats.m entryconfigure $i \
456                     -command [list display-f $sno $no $w.top.record 0]
457             incr i
458         }
459     }
460 }
461
462 proc update-target-hotlist {target} {
463     global hotTargets
464
465     set len [llength $hotTargets]
466     if {$len > 0} {
467         .top.target.m delete 6 [expr 6+[llength $hotTargets]]
468     }
469     set indx [lsearch $hotTargets $target]
470     if {$indx >= 0} {
471         set hotTargets [lreplace $hotTargets $indx $indx]
472     }
473     set hotTargets [linsert $hotTargets 0 $target]
474     set-target-hotlist    
475
476
477 proc set-target-hotlist {} {
478     global hotTargets
479     
480     set i 1
481     foreach target $hotTargets {
482         .top.target.m add command -label "$i $target" -command \
483                 [list reopen-target $target {}]
484         incr i
485         if {$i > 8} {
486              break
487         }
488     }
489 }
490
491 proc reopen-target {target base} {
492     close-target
493     open-target $target $base
494     update-target-hotlist $target
495 }
496
497 proc define-target-action {} {
498     global profile
499     
500     set target [.target-define.top.target.entry get]
501     if {$target == ""} {
502         return
503     }
504     update-target-hotlist $target
505     foreach n [array names profile] {
506         if {$n == $target} {
507             protocol-setup $n
508             return
509         }
510     }
511     set seq [lindex $profile(Default) 12]
512     puts "seq=${seq}"
513     set profile($target) $profile(Default)
514     set profile(Default) [lreplace $profile(Default) 12 12 [incr seq]]
515
516     protocol-setup $target
517     destroy .target-define
518 }
519
520 proc fail-response {target} {
521     close-target
522     tkerror "Target connection closed or protocol error"
523 }
524
525 proc connect-response {target} {
526     puts "connect-response"
527     show-target $target
528     init-request
529 }
530
531 proc open-target {target base} {
532     global profile
533     global hostid
534
535     z39 disconnect
536     z39 comstack [lindex $profile($target) 6]
537     z39 idAuthentication [lindex $profile($target) 3]
538     z39 maximumRecordSize [lindex $profile($target) 4]
539     z39 preferredMessageSize [lindex $profile($target) 5]
540     puts -nonewline "maximumRecordSize="
541     puts [z39 maximumRecordSize]
542     puts -nonewline "preferredMessageSize="
543     puts [z39 preferredMessageSize]
544     show-status {Connecting} 0 0
545     if {$base == ""} {
546         z39 databaseNames [lindex [lindex $profile($target) 7] 0]
547     } else {
548         z39 databaseNames $base
549     }
550     z39 failback [list fail-response $target]
551     z39 callback [list connect-response $target]
552     z39 connect [lindex $profile($target) 1]:[lindex $profile($target) 2]
553 #    z39 options search present scan namedResultSets triggerResourceCtrl
554     show-status {Connecting} 1 {}
555     set hostid $target
556     .top.target.m disable 0
557     .top.target.m enable 1
558     .top.target.m enable 2
559 }
560
561 proc close-target {} {
562     global hostid
563     global cancelFlag
564
565     set cancelFlag 0
566     set hostid Default
567     z39 disconnect
568     show-target {}
569     show-status {Not connected} 0 0
570     show-message {}
571     .top.target.m disable 1
572     .top.target.m disable 2
573     .top.target.m enable 0
574 }
575
576 proc load-set-action {} {
577     global setNo
578
579     incr setNo
580     ir-set z39.$setNo z39
581
582     set fname [.load-set.top.filename.entry get]
583     destroy .load-set
584     if {$fname != ""} {
585         show-status {Loading} 1 {}
586         z39.$setNo loadFile $fname
587
588         set no [z39.$setNo numberOfRecordsReturned]
589         add-title-lines $setNo $no 1
590     }
591     set l [format "%-4d %7d" $setNo $no]
592     .top.rset.m add command -label $l \
593             -command [list add-title-lines $setNo 10000 1]
594     show-status {Ready} 0 {}
595 }
596
597 proc load-set {} {
598     set w .load-set
599
600     set oldFocus [focus]
601     toplevel $w
602
603     place-force $w .
604
605     top-down-window $w
606
607     frame $w.top.filename
608     
609     pack $w.top.filename -side top -anchor e -pady 2
610     
611     entry-fields $w.top {filename} \
612             {{Filename:}} \
613             {load-set-action} {destroy .load-set}
614     
615     top-down-ok-cancel $w {load-set-action} 1
616     focus $oldFocus
617 }
618
619 proc init-request {} {
620     global setNo
621     global cancelFlag
622
623     if {$cancelFlag} {
624         close-target
625         return
626     }
627     z39 callback {init-response}
628     show-status {Initializing} 1 {}
629     z39 init
630 }
631
632 proc init-response {} {
633     global cancelFlag
634
635     if {$cancelFlag} {
636         close-target
637         return
638     }
639     show-status {Ready} 0 1
640     if {![z39 initResult]} {
641         set u [z39 userInformationField]
642         close-target
643         tkerror "Connection rejected by target: $u"
644     }
645 }
646
647 proc search-request {} {
648     global setNo
649     global profile
650     global hostid
651     global busy
652     global cancelFlag
653     global searchEnable
654
655     set target $hostid
656
657     if {$searchEnable == 0} {
658         return
659     }
660     set query [index-query]
661     if {$query==""} {
662         return
663     }
664     incr setNo
665     ir-set z39.$setNo z39
666
667     if {[lindex $profile($target) 10] == 1} {
668         z39.$setNo setName $setNo
669         puts "setName=${setNo}"
670     } else {
671         z39.$setNo setName Default
672         puts "setName=Default"
673     }
674     if {[lindex $profile($target) 8] == 1} {
675         z39.$setNo queryType rpn
676     }
677     if {[lindex $profile($target) 9] == 1} {
678         z39.$setNo queryType ccl
679     }
680     z39 callback {search-response}
681     z39.$setNo search $query
682     show-status {Search} 1 0
683 }
684
685 proc scan-request {attr} {
686     set w .scan-window
687
688     global profile
689     global hostid
690     global scanView
691     global scanTerm
692
693     set target $hostid
694     set scanView 0
695     set scanTerm {}
696
697     ir-scan z39.scan z39
698
699     if {![winfo exists $w]} {
700         toplevelG $w
701         
702         wm title $w "Scan"
703         
704         wm minsize $w 0 0
705
706         top-down-window $w
707
708         entry $w.top.entry -relief sunken 
709         pack $w.top.entry -fill x -padx 4 -pady 2
710         bind $w.top.entry <KeyRelease> [list scan-term-h $attr]
711         if {1} {
712             listbox $w.top.list -yscrollcommand [list $w.top.scroll set] \
713                     -font fixed 
714             scrollbar $w.top.scroll -orient vertical -border 1
715             pack $w.top.list -side left -fill both -expand yes
716             pack $w.top.scroll -side right -fill y
717             $w.top.scroll config -command [list $w.top.list yview]
718         } else {
719             listbox $w.top.list -font fixed -geometry 60x14
720             pack $w.top.list -side left -fill both -expand yes
721         }
722         
723         bottom-buttons $w [list {Close} [list destroyG $w] \
724                 {Up} [list scan-up $attr] \
725                 {Down} [list scan-down $attr]] 0
726         bind $w.top.list <Up> [list scan-up $attr]
727         bind $w.top.list <Down> [list scan-down $attr]
728
729 #       bind $w.top <Any-Enter> [list focus $w.top.entry]
730     }
731     z39 callback [list scan-response $attr 0 35]
732     z39.scan numberOfTermsRequested 5
733     z39.scan preferredPositionInResponse 1
734     z39.scan scan "${attr} 0"
735     
736     show-status {Scan} 1 0
737 }
738
739 proc scan-term-h {attr} {
740     global busy
741     global scanTerm
742
743     if {$busy} {
744         return
745     }
746     set w .scan-window
747     set nScanTerm [$w.top.entry get]
748     if {$nScanTerm == $scanTerm} {
749         return
750     }
751     set scanTerm $nScanTerm
752     z39 callback [list scan-response $attr 0 35]
753     z39.scan numberOfTermsRequested 5
754     z39.scan preferredPositionInResponse 1
755     puts "${attr} \{${scanTerm}\}"
756     if {$scanTerm == ""} {
757         z39.scan scan "${attr} 0"
758     } else {
759         z39.scan scan "${attr} \{${scanTerm}\}"
760     }
761     show-status {Scan} 1 0
762 }
763
764 proc scan-response {attr start toget} {
765     global cancelFlag
766     global scanTerm
767     global scanView
768
769     set w .scan-window
770     puts "In scan-response"
771     set m [z39.scan numberOfEntriesReturned]
772     puts $m
773     puts attr=$attr
774     puts start=$start
775     puts toget=$toget
776
777     if {![winfo exists .scan-window]} {
778         show-status {Ready} 0 1
779         set cancelFlag 0
780         return
781     }
782     set nScanTerm [$w.top.entry get]
783     if {$nScanTerm != $scanTerm} {
784         z39 callback [list scan-response $attr 0 35]
785         z39.scan numberOfTermsRequested 5
786         z39.scan preferredPositionInResponse 1
787         set scanTerm $nScanTerm
788         puts "${attr} \{${scanTerm}\}"
789         if {$scanTerm == ""} {
790             z39.scan scan "${attr} 0"
791         } else {
792             z39.scan scan "${attr} \{${scanTerm}\}"
793         }
794         show-status {Scan} 1 0
795         return
796     }
797     set status [z39.scan scanStatus]
798     if {$status == 6} {
799         tkerror "Scan fail"
800         show-status {Ready} 0 1
801         set cancelFlag 0
802         return
803     }
804     if {$toget < 0} {
805         for {set i 0} {$i < $m} {incr i} {
806             set term [lindex [z39.scan scanLine $i] 1]
807             set nostr [format " %-6d" [lindex [z39.scan scanLine $i] 2]]
808             $w.top.list insert $i "$nostr $term"
809         }
810         incr scanView $m
811         $w.top.list yview $scanView
812     } else {
813         $w.top.list delete $start end
814         for {set i 0} {$i < $m} {incr i} {
815             set term [lindex [z39.scan scanLine $i] 1]
816             set nostr [format " %-6d" [lindex [z39.scan scanLine $i] 2]]
817             $w.top.list insert end "$nostr $term"
818         }
819     }
820     if {$cancelFlag} {
821         show-status {Ready} 0 1
822         set cancelFlag 0
823         return
824     }
825     if {$toget > 0 && $m > 1 && $m < $toget} {
826         set ntoget [expr $toget - $m + 1]
827         puts ntoget=$ntoget
828         z39 callback [list scan-response $attr [expr $start + $m - 1] $ntoget]
829         set q $term
830         puts "down continue: $q"
831         if {$ntoget > 10} {
832             z39.scan numberOfTermsRequested 10
833         } else {
834             z39.scan numberOfTermsRequested $ntoget
835         }
836         z39.scan preferredPositionInResponse 1
837         puts "${attr} \{$q\}"
838         z39.scan scan "${attr} \{$q\}"
839         return
840     }
841     if {$toget < 0 && $m > 1 && $m < [expr - $toget]} {
842         set ntoget [expr - $toget - $m]
843         puts ntoget=$ntoget
844         z39 callback [list scan-response $attr 0 -$ntoget]
845         set q [string range [$w.top.list get 0] 8 end]
846         puts "up continue: $q"
847         if {$ntoget > 10} {
848             z39.scan numberOfTermsRequested 10
849             z39.scan preferredPositionInResponse 11
850         } else {
851             z39.scan numberOfTermsRequested $ntoget
852             z39.scan preferredPositionInResponse [incr ntoget]
853         }
854         puts "${attr} \{$q\}"
855         z39.scan scan "${attr} \{$q\}"
856         return
857     }
858     show-status {Ready} 0 1
859 }
860
861 proc scan-down {attr} {
862     global scanView
863
864     set w .scan-window
865     set scanView [expr $scanView + 5]
866     set s [$w.top.list size]
867     if {$scanView > $s} {
868         z39 callback [list scan-response $attr [expr $s - 1] 25]
869         set q [string range [$w.top.list get [expr $s - 1]] 8 end]
870         puts "down: $q"
871         z39.scan numberOfTermsRequested 10
872         z39.scan preferredPositionInResponse 1
873         show-status {Scan} 1 0
874         puts "${attr} \{$q\}"
875         z39.scan scan "${attr} \{$q\}"
876         return
877     }
878     $w.top.list yview $scanView
879 }
880
881 proc scan-up {attr} {
882     global scanView
883
884     set w .scan-window
885     set scanView [expr $scanView - 5]
886     if {$scanView < 0} {
887         z39 callback [list scan-response $attr 0 -25]
888         set q [string range [$w.top.list get 0] 8 end]
889         puts "up: $q"
890         z39.scan numberOfTermsRequested 10
891         z39.scan preferredPositionInResponse 11
892         show-status {Scan} 1 0
893         z39.scan scan "${attr} \{$q\}"
894         return
895     }
896     $w.top.list yview $scanView
897 }
898
899 proc search-response {} {
900     global setNo
901     global setOffset
902     global setMax
903     global cancelFlag
904     global busy
905
906     puts "In search-response"
907     init-title-lines
908     show-status {Ready} 0 1
909     set setMax [z39.$setNo resultCount]
910     show-message "${setMax} hits"
911     set l [format "%-4d %7d" $setNo $setMax]
912     .top.rset.m add command -label $l \
913             -command [list add-title-lines $setNo 10000 1]
914     if {$setMax <= 0} {
915         set status [z39.$setNo responseStatus]
916         if {[lindex $status 0] == "NSD"} {
917             set code [lindex $status 1]
918             set msg [lindex $status 2]
919             set addinfo [lindex $status 3]
920             tkerror "NSD$code: $msg: $addinfo"
921         }
922         return
923     }
924     if {$setMax > 20} {
925         set setMax 20
926     }
927     set setOffset 1
928     if {$cancelFlag} {
929         set cancelFlag 0
930         return
931     }
932     z39 callback {present-response}
933     z39.$setNo present $setOffset 1
934     show-status {Retrieve} 1 0
935 }
936
937 proc present-more {number} {
938     global setNo
939     global setOffset
940     global setMax
941
942     puts "setOffset=$setOffset"
943     puts "present-more"
944     if {$setNo == 0} {
945         puts "setNo=$setNo"
946         return
947     }
948     set max [z39.$setNo resultCount]
949     if {$max <= $setOffset} {
950         puts "max=$max"
951         puts "setOffset=$setOffset"
952         return
953     }
954     if {$number == ""} {
955         set setMax $max
956     } else {
957         incr setMax $number
958         if {$setMax > $max} {
959             set setMax $max
960         }
961     }
962     z39 callback {present-response}
963
964     set toGet [expr $setMax - $setOffset + 1]
965     if {$toGet <= 0} {
966         return
967     }
968     if {$toGet > 3} {
969         set toGet 3
970     } 
971     z39.$setNo present $setOffset $toGet
972     show-status {Retrieve} 1 0
973 }
974
975 proc init-title-lines {} {
976     .data.record delete 0.0 end
977 }
978
979 proc title-press {y setno} {
980     show-full-marc $setno [expr 1 + [.data.list nearest $y]] 0
981 }
982
983 proc add-title-lines {setno no offset} {
984     global displayFormats
985     global displayFormat
986     global lastSetNo
987
988     if {$setno == 0} {
989         set setno $lastSetNo
990     } else {
991         set lastSetNo $setno
992     }
993     if {$offset == 1} {
994         .bot.a.set configure -text $setno
995         .data.record delete 0.0 end
996     }
997     set ffunc [lindex $displayFormats $displayFormat]
998     set ffunc "display-$ffunc"
999     for {set i 0} {$i < $no} {incr i} {
1000         set o [expr $i + $offset]
1001         set type [z39.$setno type $o]
1002         if {$type == ""} {
1003             break
1004         }
1005         set insert0 [.data.record index insert]
1006         $ffunc $setno $o .data.record 1
1007         .data.record tag add r$o $insert0 insert
1008         .data.record tag bind r$o <1> \
1009                 [list popup-marc $setno $o 0 $displayFormat]
1010     }
1011 }
1012
1013 proc present-response {} {
1014     global setNo
1015     global setOffset
1016     global setMax
1017     global cancelFlag
1018
1019     puts "In present-response"
1020     set no [z39.$setNo numberOfRecordsReturned]
1021     puts "Returned $no records, setOffset $setOffset"
1022     add-title-lines $setNo $no $setOffset
1023     set setOffset [expr $setOffset + $no]
1024     set status [z39.$setNo responseStatus]
1025     if {[lindex $status 0] == "NSD"} {
1026         show-status {Ready} 0 1
1027         set code [lindex $status 1]
1028         set msg [lindex $status 2]
1029         set addinfo [lindex $status 3]
1030         tkerror "NSD$code: $msg: $addinfo"
1031         return
1032     }
1033     if {$cancelFlag} {
1034         show-status {Ready} 0 1
1035         set cancelFlag 0
1036         return
1037     }
1038     if {$no > 0 && $setOffset <= $setMax} {
1039         puts "present-request from ${setOffset}"
1040         set toGet [expr $setMax - $setOffset + 1]
1041         if {$toGet > 3} {
1042             set toGet 3
1043         }
1044         z39.$setNo present $setOffset $toGet
1045     } else {
1046         show-status {Finished} 0 1
1047     }
1048 }
1049
1050 proc left-cursor {w} {
1051     set i [$w index insert]
1052     if {$i > 0} {
1053         incr i -1
1054         $w icursor $i
1055     }
1056 }
1057
1058 proc right-cursor {w} {
1059     set i [$w index insert]
1060     incr i
1061     $w icursor $i
1062 }
1063
1064 proc bind-fields {list returnAction escapeAction} {
1065     set max [expr [llength $list]-1]
1066     for {set i 0} {$i < $max} {incr i} {
1067         bind [lindex $list $i] <Return> $returnAction
1068         bind [lindex $list $i] <Escape> $escapeAction
1069         bind [lindex $list $i] <Tab> [list focus [lindex $list [expr $i+1]]]
1070         bind [lindex $list $i] <Left> [list left-cursor [lindex $list $i]]
1071         bind [lindex $list $i] <Right> [list right-cursor [lindex $list $i]]
1072     }
1073     bind [lindex $list $i] <Return> $returnAction
1074     bind [lindex $list $i] <Escape> $escapeAction
1075     bind [lindex $list $i] <Tab>    [list focus [lindex $list 0]]
1076     bind [lindex $list $i] <Left> [list left-cursor [lindex $list $i]]
1077     bind [lindex $list $i] <Right> [list right-cursor [lindex $list $i]]
1078     focus [lindex $list 0]
1079 }
1080
1081 proc entry-fields {parent list tlist returnAction escapeAction} {
1082     set alist {}
1083     set i 0
1084     foreach field $list {
1085         set label ${parent}.${field}.label
1086         set entry ${parent}.${field}.entry
1087         label $label -text [lindex $tlist $i] -anchor e
1088         entry $entry -width 32 -relief sunken
1089         pack $label -side left
1090         pack $entry -side right
1091         lappend alist $entry
1092         incr i
1093     }
1094     bind-fields $alist $returnAction $escapeAction
1095 }
1096
1097 proc define-target-dialog {} {
1098     set w .target-define
1099
1100     toplevel $w
1101     place-force $w .
1102     top-down-window $w
1103     frame $w.top.target
1104     pack $w.top.target \
1105             -side top -anchor e -pady 2 
1106     entry-fields $w.top {target} \
1107             {{Target:}} \
1108             {define-target-action} {destroy .target-define}
1109     top-down-ok-cancel $w {define-target-action} 1
1110 }
1111
1112 proc protocol-setup-action {target} {
1113     global profile
1114     global csRadioType
1115     global protocolRadioType
1116     global settingsChanged
1117     global RPNCheck
1118     global CCLCheck
1119     global ResultSetCheck
1120
1121     set wno [lindex $profile($target) 12]
1122     set w .setup-${wno}
1123     
1124     set b {}
1125     set settingsChanged 1
1126     set len [$w.top.databases.list size]
1127     for {set i 0} {$i < $len} {incr i} {
1128         lappend b [$w.top.databases.list get $i]
1129     }
1130     set profile($target) [list [$w.top.description.entry get] \
1131             [$w.top.host.entry get] \
1132             [$w.top.port.entry get] \
1133             [$w.top.idAuthentication.entry get] \
1134             [$w.top.maximumRecordSize.entry get] \
1135             [$w.top.preferredMessageSize.entry get] \
1136             $csRadioType \
1137             $b \
1138             $RPNCheck \
1139             $CCLCheck \
1140             $ResultSetCheck \
1141             $protocolRadioType \
1142             $wno]
1143
1144     cascade-target-list
1145     puts $profile($target)
1146     destroyG $w
1147 }
1148
1149 proc place-force {window parent} {
1150     set g [wm geometry $parent]
1151
1152     set p1 [string first + $g]
1153     set p2 [string last + $g]
1154
1155     set x [expr 40+[string range $g [expr $p1 +1] [expr $p2 -1]]]
1156     set y [expr 60+[string range $g [expr $p2 +1] end]]
1157     wm geometry $window +${x}+${y}
1158 }
1159
1160 proc add-database-action {target} {
1161     global profile
1162
1163     set wno [lindex $profile($target) 12]
1164     set w .setup-${wno}
1165
1166     $w.top.databases.list insert end \
1167             [.database-select.top.database.entry get]
1168     destroy .database-select
1169 }
1170
1171 proc add-database {target} {
1172     global profile
1173
1174     set w .database-select
1175
1176     set oldFocus [focus]
1177     toplevel $w
1178  
1179     set wno [lindex $profile($target) 12]
1180     place-force $w .setup-${wno}
1181
1182     top-down-window $w
1183
1184     frame $w.top.database
1185
1186     pack $w.top.database -side top -anchor e -pady 2
1187     
1188     entry-fields $w.top {database} \
1189             {{Database to add:}} \
1190             [list add-database-action $target] {destroy .database-select}
1191
1192     top-down-ok-cancel $w [list add-database-action $target] 1
1193     focus $oldFocus
1194 }
1195
1196 proc delete-database {target} {
1197     global profile
1198
1199     set wno [lindex $profile($target) 12]
1200     set w .setup-${wno}
1201
1202     foreach i [lsort -decreasing \
1203             [$w.top.databases.list curselection]] {
1204         $w.top.databases.list delete $i
1205     }
1206 }
1207
1208 proc protocol-setup {target} {
1209     global profile
1210     global csRadioType
1211     global protocolRadioType
1212     global RPNCheck
1213     global CCLCheck
1214     global ResultSetCheck
1215
1216     set wno [lindex $profile($target) 12]
1217     set w .setup-${wno}
1218
1219     toplevelG $w
1220
1221     wm title $w "Setup $target"
1222
1223     top-down-window $w
1224     
1225     if {$target == ""} {
1226         set target Default
1227     }
1228     puts target
1229     puts $profile($target)
1230
1231     frame $w.top.host
1232     frame $w.top.port
1233     frame $w.top.description
1234     frame $w.top.idAuthentication
1235     frame $w.top.maximumRecordSize
1236     frame $w.top.preferredMessageSize
1237     frame $w.top.cs-type -relief ridge -border 2
1238     frame $w.top.protocol -relief ridge -border 2
1239     frame $w.top.query -relief ridge -border 2
1240     frame $w.top.databases -relief ridge -border 2
1241
1242     # Maximum/preferred/idAuth ...
1243     pack $w.top.description $w.top.host $w.top.port \
1244             $w.top.idAuthentication $w.top.maximumRecordSize \
1245             $w.top.preferredMessageSize -side top -anchor e -pady 2
1246     
1247     entry-fields $w.top {description host port idAuthentication \
1248             maximumRecordSize preferredMessageSize} \
1249             {{Description:} {Host:} {Port:} {Id Authentication:} \
1250             {Maximum Record Size:} {Preferred Message Size:}} \
1251             [list protocol-setup-action $target] [list destroyG $w]
1252     
1253     foreach sub {description host port idAuthentication \
1254             maximumRecordSize preferredMessageSize} {
1255         puts $sub
1256         bind $w.top.$sub.entry <Control-a> [list add-database $target]
1257         bind $w.top.$sub.entry <Control-d> [list delete-database $target]
1258     }
1259     $w.top.description.entry insert 0 [lindex $profile($target) 0]
1260     $w.top.host.entry insert 0 [lindex $profile($target) 1]
1261     $w.top.port.entry insert 0 [lindex $profile($target) 2]
1262     $w.top.idAuthentication.entry insert 0 [lindex $profile($target) 3]
1263     $w.top.maximumRecordSize.entry insert 0 [lindex $profile($target) 4]
1264     $w.top.preferredMessageSize.entry insert 0 [lindex $profile($target) 5]
1265     set csRadioType [lindex $profile($target) 6]
1266     set RPNCheck [lindex $profile($target) 8]
1267     set CCLCheck [lindex $profile($target) 9]
1268     set ResultSetCheck [lindex $profile($target) 10]
1269     set protocolRadioType [lindex $profile($target) 11]
1270     if {$protocolRadioType == ""} {
1271         set protocolRadioType z39v2
1272     }
1273
1274     # Databases ....
1275     pack $w.top.databases -side left -pady 6 -padx 6 -expand yes -fill both
1276
1277     label $w.top.databases.label -text "Databases"
1278     button $w.top.databases.add -text "Add" \
1279             -command [list add-database $target]
1280     button $w.top.databases.delete -text "Delete" \
1281             -command [list delete-database $target]
1282     listbox $w.top.databases.list -geometry 20x6 \
1283             -yscrollcommand "$w.top.databases.scroll set"
1284     scrollbar $w.top.databases.scroll -orient vertical -border 1
1285     pack $w.top.databases.label -side top -fill x \
1286             -padx 2 -pady 2
1287     pack $w.top.databases.add $w.top.databases.delete -side top -fill x \
1288             -padx 2 -pady 2
1289     pack $w.top.databases.list -side left -fill both -expand yes \
1290             -padx 2 -pady 2
1291     pack $w.top.databases.scroll -side right -fill y \
1292             -padx 2 -pady 2
1293     $w.top.databases.scroll config -command "$w.top.databases.list yview"
1294
1295     foreach b [lindex $profile($target) 7] {
1296         $w.top.databases.list insert end $b
1297     }
1298
1299     # Transport ...
1300     pack $w.top.cs-type -pady 6 -padx 6 -side top -fill x
1301     
1302     label $w.top.cs-type.label -text "Transport" 
1303     radiobutton $w.top.cs-type.tcpip -text "TCP/IP" -anchor w \
1304             -command {puts tcp/ip} -variable csRadioType -value tcpip
1305     radiobutton $w.top.cs-type.mosi -text "MOSI" -anchor w\
1306             -command {puts mosi} -variable csRadioType -value mosi
1307     
1308     pack $w.top.cs-type.label $w.top.cs-type.tcpip $w.top.cs-type.mosi \
1309             -padx 4 -side top -fill x
1310
1311     # Protocol ...
1312     pack $w.top.protocol -pady 6 -padx 6 -side top -fill x
1313     
1314     label $w.top.protocol.label -text "Protocol" 
1315     radiobutton $w.top.protocol.z39v2 -text "Z39.50" -anchor w \
1316             -command {puts z39v2} -variable protocolRadioType -value z39v2
1317     radiobutton $w.top.protocol.sr -text "SR" -anchor w \
1318             -command {puts sr} -variable protocolRadioType -value sr
1319     
1320     pack $w.top.protocol.label $w.top.protocol.z39v2 $w.top.protocol.sr \
1321             -padx 4 -side top -fill x
1322
1323     # Query ...
1324     pack $w.top.query -pady 6 -padx 6 -side top -fill x
1325
1326     label $w.top.query.label -text "Query support"
1327     checkbutton $w.top.query.c1 -text "RPN query" -anchor w -variable RPNCheck
1328     checkbutton $w.top.query.c2 -text "CCL query" -anchor w -variable CCLCheck
1329     checkbutton $w.top.query.c3 -text "Result sets" -anchor w -variable ResultSetCheck
1330
1331     pack $w.top.query.label -side top 
1332     pack $w.top.query.c1 $w.top.query.c2 $w.top.query.c3 \
1333             -padx 4 -side top -fill x
1334
1335     # Ok-cancel
1336     bottom-buttons $w [list {Ok} [list protocol-setup-action $target] \
1337             {Cancel} [list destroyG $w]] 0   
1338 }
1339
1340 proc database-select-action {} {
1341     set w .database-select.top
1342     set b {}
1343     foreach indx [$w.databases.list curselection] {
1344         lappend b [$w.databases.list get $indx]
1345     }
1346     if {$b != ""} {
1347         z39 databaseNames $b
1348     }
1349     destroy .database-select
1350 }
1351
1352 proc database-select {} {
1353     set w .database-select
1354     global profile
1355     global hostid
1356
1357     toplevel $w
1358
1359     place-force $w .
1360
1361     top-down-window $w
1362
1363     frame $w.top.databases -relief ridge -border 2
1364
1365     pack $w.top.databases -side left -pady 6 -padx 6 -expand yes -fill x
1366
1367     label $w.top.databases.label -text "List"
1368     listbox $w.top.databases.list -geometry 20x6 \
1369             -yscrollcommand "$w.top.databases.scroll set"
1370     scrollbar $w.top.databases.scroll -orient vertical -border 1
1371     pack $w.top.databases.label -side top -fill x \
1372             -padx 2 -pady 2
1373     pack $w.top.databases.list -side left -fill both -expand yes \
1374             -padx 2 -pady 2
1375     pack $w.top.databases.scroll -side right -fill y \
1376             -padx 2 -pady 2
1377     $w.top.databases.scroll config -command "$w.top.databases.list yview"
1378
1379     foreach b [lindex $profile($hostid) 7] {
1380         $w.top.databases.list insert end $b
1381     }
1382     top-down-ok-cancel $w {database-select-action} 1
1383 }
1384
1385 proc cascade-target-list {} {
1386     global profile
1387     
1388     foreach sub [winfo children .top.target.m.clist] {
1389         puts "deleting $sub"
1390         destroy $sub
1391     }
1392     .top.target.m.clist delete 0 last
1393     foreach n [array names profile] {
1394         if {$n != "Default"} {
1395             set nl [string tolower $n]
1396             if {[llength [lindex $profile($n) 7]] > 1} {
1397                 .top.target.m.clist add cascade -label $n \
1398                         -menu .top.target.m.clist.$nl
1399                 menu .top.target.m.clist.$nl
1400                 foreach b [lindex $profile($n) 7] {
1401                     .top.target.m.clist.$nl add command -label $b \
1402                             -command [list reopen-target $n $b]
1403                 }
1404             } else {
1405                 .top.target.m.clist add command -label $n \
1406                         -command [list reopen-target $n {}]
1407             }
1408         }
1409     }
1410     .top.target.m.slist delete 0 last
1411     foreach n [array names profile] {
1412         if {$n != "Default"} {
1413             .top.target.m.slist add command -label $n \
1414                     -command [list protocol-setup $n]
1415         }
1416     }
1417 }
1418
1419 proc cascade-query-list {} {
1420     global queryTypes
1421
1422     set i 0
1423     .top.options.m.slist delete 0 last
1424     foreach n $queryTypes {
1425         .top.options.m.slist add command -label $n \
1426                 -command [list query-setup $i]
1427         incr i
1428     }
1429
1430     set i 0
1431     .top.options.m.clist delete 0 last
1432     foreach n $queryTypes {
1433         .top.options.m.clist add command -label $n \
1434                 -command [list query-select $i]
1435         incr i
1436     }
1437 }
1438
1439 proc save-geometry {} {
1440     global windowGeometry
1441     
1442     set windowGeometry(.) [wm geometry .]
1443
1444     set f [open "clientg.tcl" w]
1445
1446     foreach n [array names windowGeometry] {
1447         puts -nonewline $f "set \{windowGeometry($n)\} \{"
1448         puts -nonewline $f $windowGeometry($n)
1449         puts $f "\}"
1450     }
1451     close $f
1452 }
1453
1454 proc save-settings {} {
1455     global hotTargets 
1456     global profile
1457     global settingsChanged
1458     global queryTypes
1459     global queryButtons
1460     global queryInfo
1461     
1462     set f [open "clientrc.tcl" w]
1463     puts $f "# Setup file"
1464     puts $f "set hotTargets \{ $hotTargets \}"
1465
1466     foreach n [array names profile] {
1467         puts -nonewline $f "set \{profile($n)\} \{"
1468         puts -nonewline $f $profile($n)
1469         puts $f "\}"
1470     }
1471     puts -nonewline $f "set queryTypes \{" 
1472     puts -nonewline $f $queryTypes
1473     puts $f "\}"
1474     
1475     puts -nonewline $f "set queryButtons \{" 
1476     puts -nonewline $f $queryButtons
1477     puts $f "\}"
1478     
1479     puts -nonewline $f "set queryInfo \{"
1480     puts -nonewline $f $queryInfo
1481     puts $f "\}"
1482     close $f
1483     set settingsChanged 0
1484 }
1485
1486 proc alert {ask} {
1487     set w .alert
1488
1489     global alertAnswer
1490
1491     toplevel $w
1492     place-force $w .
1493     top-down-window $w
1494
1495     message $w.top.message -text $ask
1496
1497     pack $w.top.message -side left -pady 6 -padx 20 -expand yes -fill x
1498   
1499     set alertAnswer 0
1500     top-down-ok-cancel $w {alert-action} 1
1501     return $alertAnswer
1502 }
1503
1504 proc alert-action {} {
1505     global alertAnswer
1506     set alertAnswer 1
1507     destroy .alert
1508 }
1509
1510 proc exit-action {} {
1511     global settingsChanged
1512
1513     save-geometry
1514     if {$settingsChanged} {
1515         set a [alert "you havent saved your settings. Do you wish to save?"]
1516         if {$a} {
1517             save-settings
1518         }
1519     }
1520     exit 0
1521 }
1522
1523 proc listbuttonaction {w name h user i} {
1524     $w configure -text [lindex $name 0]
1525     $h [lindex $name 1] $user $i
1526 }
1527     
1528 proc listbuttonx {button no names handle user} {
1529     if {[winfo exists $button]} {
1530         $button configure -text [lindex [lindex $names $no] 0]
1531         ${button}.m delete 0 last
1532     } else {
1533         menubutton $button -text [lindex [lindex $names $no] 0] \
1534                 -width 10 -menu ${button}.m -relief raised -border 1
1535         menu ${button}.m
1536     }
1537     set i 0
1538     foreach name $names {
1539         ${button}.m add command -label [lindex $name 0] \
1540                 -command [list listbuttonaction ${button} $name \
1541                 $handle $user $i]
1542         incr i
1543     }
1544 }
1545
1546 proc listbutton {button no names} {
1547     menubutton $button -text [lindex $names $no] -width 10 -menu ${button}.m \
1548             -relief raised -border 1
1549     menu ${button}.m
1550     foreach name $names {
1551         ${button}.m add command -label $name \
1552                 -command [list ${button} configure -text $name]
1553     }
1554 }
1555
1556 proc query-add-index-action {queryNo} {
1557     set w .setup-query-$queryNo
1558
1559     global queryInfoTmp
1560     global queryButtonsTmp
1561
1562     lappend queryInfoTmp [list [.query-add-index.top.index.entry get] {}]
1563
1564     destroy .query-add-index
1565     #destroy $w.top.lines
1566     #frame $w.top.lines -relief ridge -border 2
1567     index-lines $w.top.lines 0 $queryButtonsTmp $queryInfoTmp activate-e-index
1568     #pack $w.top.lines -side left -pady 6 -padx 6 -fill y
1569 }
1570
1571 proc query-add-line {queryNo} {
1572     set w .setup-query-$queryNo
1573
1574     global queryInfoTmp
1575     global queryButtonsTmp
1576
1577     lappend queryButtonsTmp {I 0}
1578
1579     #destroy $w.top.lines
1580     #frame $w.top.lines -relief ridge -border 2
1581     index-lines $w.top.lines 0 $queryButtonsTmp $queryInfoTmp activate-e-index
1582     #pack $w.top.lines -side left -pady 6 -padx 6 -fill y
1583 }
1584
1585 proc query-del-line {queryNo} {
1586     set w .setup-query-$queryNo
1587
1588     global queryInfoTmp
1589     global queryButtonsTmp
1590
1591     set l [llength $queryButtonsTmp]
1592     if {$l <= 0} {
1593         return
1594     }
1595     incr l -1
1596     set queryButtonsTmp [lreplace $queryButtonsTmp $l $l]
1597     index-lines $w.top.lines 0 $queryButtonsTmp $queryInfoTmp activate-e-index
1598 }
1599
1600 proc query-add-index {queryNo} {
1601     set w .query-add-index
1602
1603     toplevel $w
1604     place-force $w .setup-query-$queryNo
1605     top-down-window $w
1606     frame $w.top.index
1607     pack $w.top.index \
1608             -side top -anchor e -pady 2 
1609     entry-fields $w.top {index} \
1610             {{Index Name:}} \
1611             [list query-add-index-action $queryNo] {destroy .query-add-index}
1612     top-down-ok-cancel $w [list query-add-index-action $queryNo] 1
1613 }
1614
1615 proc query-setup-action {queryNo} {
1616     global queryButtons
1617     global queryInfo
1618     global queryButtonsTmp
1619     global queryInfoTmp
1620     global queryButtonsFind
1621     global queryInfoFind
1622
1623     set queryInfo [lreplace $queryInfo $queryNo $queryNo \
1624             $queryInfoTmp]
1625     set queryButtons [lreplace $queryButtons $queryNo $queryNo \
1626             $queryButtonsTmp]
1627     set queryInfoFind $queryInfoTmp
1628     set queryButtonsFind $queryButtonsTmp
1629
1630     puts $queryInfo
1631     puts $queryButtons
1632     destroy .setup-query-$queryNo
1633
1634     index-lines .lines 1 $queryButtonsFind $queryInfoFind activate-index
1635 }
1636
1637 proc activate-e-index {value no i} {
1638     global queryButtonsTmp
1639     
1640     puts $queryButtonsTmp
1641     set queryButtonsTmp [lreplace $queryButtonsTmp $no $no [list I $i]]
1642     puts $queryButtonsTmp
1643     puts "value $value"
1644     puts "no $no"
1645     puts "i $i"
1646 }
1647
1648 proc activate-index {value no i} {
1649     global queryButtonsFind
1650
1651     set queryButtonsFind [lreplace $queryButtonsFind $no $no [list I $i]]
1652
1653     puts "queryButtonsFind $queryButtonsFind"
1654     puts "value $value"
1655     puts "no $no"
1656     puts "i $i"
1657 }
1658
1659 proc query-setup {queryNo} {
1660     set w .setup-query-$queryNo
1661     global queryTypes
1662     set queryTypes {Simple}
1663     global queryButtons
1664     global queryInfo
1665     global queryButtonsTmp
1666     global queryInfoTmp
1667
1668     set queryName [lindex $queryTypes $queryNo]
1669     set queryInfoTmp [lindex $queryInfo $queryNo]
1670     set queryButtonsTmp [lindex $queryButtons $queryNo]
1671
1672     #set queryButtons { {I 0 I 1 I 2} }
1673     #set queryInfo { { {Title ti} {Author au} {Subject sh} } }
1674
1675     toplevel $w
1676
1677     wm title $w "Query setup $queryName"
1678     place-force $w .
1679
1680     top-down-window $w
1681
1682     frame $w.top.lines -relief ridge -border 2
1683     frame $w.top.use -relief ridge -border 2
1684     frame $w.top.relation -relief ridge -border 2
1685     frame $w.top.position -relief ridge -border 2
1686     frame $w.top.structure -relief ridge -border 2
1687     frame $w.top.truncation -relief ridge -border 2
1688     frame $w.top.completeness -relief ridge -border 2
1689
1690     # Index Lines
1691
1692     index-lines $w.top.lines 0 $queryButtonsTmp $queryInfoTmp activate-e-index
1693
1694     pack $w.top.lines -side left -pady 6 -padx 6 -fill y
1695
1696     # Use Attributes
1697     pack $w.top.use -side left -pady 6 -padx 6 -fill y
1698
1699     label $w.top.use.label -text "Use"
1700     listbox $w.top.use.list -geometry 20x10 \
1701             -yscrollcommand "$w.top.use.scroll set"
1702     scrollbar $w.top.use.scroll -orient vertical -border 1
1703     pack $w.top.use.label -side top -fill x \
1704             -padx 2 -pady 2
1705     pack $w.top.use.list -side left -fill both -expand yes \
1706             -padx 2 -pady 2
1707     pack $w.top.use.scroll -side right -fill y \
1708             -padx 2 -pady 2
1709     $w.top.use.scroll config -command "$w.top.use.list yview"
1710
1711     foreach u {{Personal name} {Corporate name}} {
1712         $w.top.use.list insert end $u
1713     }
1714     # Relation Attributes
1715     pack $w.top.relation -pady 6 -padx 6 -side top
1716
1717     label $w.top.relation.label -text "Relation" -width 18
1718     
1719     listbutton $w.top.relation.b 0\
1720             {{None} {Less than} {Greater than or equal} \
1721             {Equal} {Greater than or equal} {Greater than} {Not equal} \
1722             {Phonetic} \
1723             {Stem} {Relevance} {AlwaysMatches}}
1724     
1725     pack $w.top.relation.label $w.top.relation.b -fill x 
1726
1727     # Position Attributes
1728     pack $w.top.position -pady 6 -padx 6 -side top
1729
1730     label $w.top.position.label -text "Position" -width 18
1731
1732     listbutton $w.top.position.b 0 {{None} {First in field} {First in subfield}
1733     {Any position in field}}
1734     
1735     pack $w.top.position.label $w.top.position.b -fill x
1736
1737     # Structure Attributes
1738
1739     pack $w.top.structure -pady 6 -padx 6 -side top
1740     
1741     label $w.top.structure.label -text "Structure" -width 18
1742
1743     listbutton $w.top.structure.b 0 {{None} {Phrase} {Word} {Key} {Year}
1744     {Date (norm)} {Word list} {Date (un-norm)} {Name (norm)} {Date (un-norm)}
1745     {Structure} {urx} {free-form} {doc-text} {local-number} {string}
1746     {numeric string}}
1747
1748     pack $w.top.structure.label $w.top.structure.b -fill x
1749
1750     # Truncation Attributes
1751
1752     pack $w.top.truncation -pady 6 -padx 6 -side top
1753     
1754     label $w.top.truncation.label -text "Truncation" -width 18
1755
1756     listbutton $w.top.truncation.b 0 {{Auto} {Right} {Left} {Left and right} \
1757             {No truncation} {Process #} {Re-1} {Re-2}}
1758     pack $w.top.truncation.label $w.top.truncation.b -fill x
1759
1760     # Completeness Attributes
1761
1762     pack $w.top.completeness -pady 6 -padx 6 -side top
1763     
1764     label $w.top.completeness.label -text "Truncation" -width 18
1765
1766     listbutton $w.top.completeness.b 0 {{None} {Incomplete subfield} \
1767             {Complete subfield} {Complete field}}
1768     pack $w.top.completeness.label $w.top.completeness.b -fill x
1769
1770     # Ok-cancel
1771     bottom-buttons $w [list \
1772             {Ok} [list query-setup-action $queryNo] \
1773             {Add index} [list query-add-index $queryNo] \
1774             {Add line} [list query-add-line $queryNo] \
1775             {Delete line} [list query-del-line $queryNo] \
1776             {Cancel} [list destroy $w]] 0
1777 }
1778
1779 proc index-clear {} {
1780     global queryButtonsFind
1781
1782     set i 0
1783     foreach b $queryButtonsFind {
1784         .lines.$i.e delete 0 end
1785         incr i
1786     }
1787 }
1788     
1789 proc index-query {} {
1790     global queryButtonsFind
1791     global queryInfoFind
1792
1793     set i 0
1794     set qs {}
1795
1796     foreach b $queryButtonsFind {
1797         set term [string trim [.lines.$i.e get]]
1798         if {$term != ""} {
1799             set attr [lindex [lindex $queryInfoFind [lindex $b 1]] 1]
1800
1801             set term "\{${term}\}"
1802             foreach a $attr {
1803                 set term "@attr $a ${term}"
1804             }
1805             if {$qs != ""} {
1806                 set qs "@and ${qs} ${term}"
1807             } else {
1808                 set qs $term
1809             }
1810         }
1811         incr i
1812     }
1813     puts "qs=  $qs"
1814     return $qs
1815 }
1816
1817 proc index-lines {w realOp buttonInfo queryInfo handle} {
1818     set i 0
1819     foreach b $buttonInfo {
1820         if {! [winfo exists $w.$i]} {
1821             frame $w.$i -background white -border 1
1822         }
1823         listbuttonx $w.$i.l [lindex $b 1] $queryInfo $handle $i
1824
1825         if {$realOp} {
1826             if {! [winfo exists $w.$i.e]} {
1827                 entry $w.$i.e -width 32 -relief sunken -border 1
1828                 bind $w.$i.e <FocusIn> [list $w.$i configure \
1829                         -background red]
1830                 bind $w.$i.e <FocusOut> [list $w.$i configure \
1831                         -background white]
1832                 pack $w.$i.l -side left
1833                 pack $w.$i.e -side left -fill x -expand yes
1834                 pack $w.$i -side top -fill x -padx 2 -pady 2
1835                 bind $w.$i.e <Left> [list left-cursor $w.$i.e]
1836                 bind $w.$i.e <Right> [list right-cursor $w.$i.e]
1837                 bind $w.$i.e <Return> search-request
1838             }
1839         } else {
1840             pack $w.$i.l -side left
1841             pack $w.$i -side top -fill x -padx 2 -pady 2
1842         }
1843         incr i
1844     }
1845     set j $i
1846     while {[winfo exists $w.$j]} {
1847         destroy $w.$j
1848         incr j
1849     }
1850     if {! $realOp} {
1851         return
1852     }
1853     set j 0
1854     incr i -1
1855     while {$j < $i} {
1856         set k [expr $j+1]
1857         bind $w.$j.e <Tab> "focus $w.$k.e"
1858         set j $k
1859     }
1860     if {$i >= 0} {
1861         bind $w.$i.e <Tab> "focus $w.0.e"
1862         focus $w.0.e
1863     }
1864 }
1865
1866 proc search-fields {w buttondefs} {
1867     set i 0
1868     foreach buttondef $buttondefs {
1869         frame $w.$i -background white
1870         
1871         listbutton $w.$i.l 0 $buttondef
1872         entry $w.$i.e -width 32 -relief sunken
1873         
1874         pack $w.$i.l -side left
1875         pack $w.$i.e -side left -fill x -expand yes
1876
1877         pack $w.$i -side top -fill x -padx 2 -pady 2
1878
1879         bind $w.$i.e <Left> [list left-cursor $w.$i.e]
1880         bind $w.$i.e <Right> [list right-cursor $w.$i.e]
1881
1882         incr i
1883     }
1884     set j 0
1885     incr i -1
1886     while {$j < $i} {
1887         set k [expr $j+1]
1888         bind $w.$j.e <Tab> "focus $w.$k.e \n
1889         $w.$k configure -background red \n
1890         $w.$j configure -background white"
1891         set j $k
1892     }
1893     bind $w.$i.e <Tab> "focus $w.0.e \n
1894         $w.0 configure -background red \n
1895         $w.$i configure -background white"
1896     focus $w.0.e
1897     $w.0 configure -background red
1898 }
1899
1900 if {[info exists windowGeometry(.)]} {
1901     set g $windowGeometry(.)
1902     if {$g != ""} {
1903         wm geometry . $g
1904     }
1905 }    
1906
1907 read-formats
1908
1909 frame .top  -border 1 -relief raised
1910 frame .lines  -border 1 -relief raised
1911 frame .mid  -border 1 -relief raised
1912 frame .data -border 1 -relief raised
1913 frame .bot  -border 1 -relief raised
1914 pack .top .lines .mid -side top -fill x
1915 pack .data -side top -fill both -expand yes
1916 pack .bot -fill x
1917
1918 menubutton .top.file -text "File" -underline 0 -menu .top.file.m
1919 menu .top.file.m
1920 .top.file.m add command -label "Save settings" -command {save-settings}
1921 .top.file.m add separator
1922 .top.file.m add command -label "Exit" -command {exit-action}
1923
1924 menubutton .top.target -text "Target" -underline 0 -menu .top.target.m
1925 menu .top.target.m
1926 .top.target.m add cascade -label "Connect" -menu .top.target.m.clist
1927 .top.target.m add command -label "Disconnect" -command {close-target}
1928 .top.target.m add command -label "About" -command {about-target}
1929 .top.target.m add cascade -label "Setup" -menu .top.target.m.slist
1930 .top.target.m add command -label "Setup new" -command {define-target-dialog}
1931 .top.target.m add separator
1932 set-target-hotlist
1933
1934 .top.target.m disable 1
1935 .top.target.m disable 2
1936
1937 menu .top.target.m.clist
1938 menu .top.target.m.slist
1939 cascade-target-list
1940
1941 menubutton .top.service -text "Service" -underline 0 -menu .top.service.m
1942 menu .top.service.m
1943 .top.service.m add command -label "Database" -command {database-select}
1944 .top.service.m add cascade -label "Query type" -menu .top.service.m.querytype
1945 menu .top.service.m.querytype
1946 .top.service.m.querytype add radiobutton -label "RPN"
1947 .top.service.m.querytype add radiobutton -label "CCL"
1948 .top.service.m add cascade -label "Present" -menu .top.service.m.present
1949 menu .top.service.m.present
1950 .top.service.m.present add command -label "More" \
1951         -command [list present-more 10]
1952 .top.service.m.present add command -label "All" \
1953         -command [list present-more {}]
1954 .top.service configure -state disabled
1955
1956 menubutton .top.rset -text "Set" -menu .top.rset.m
1957 menu .top.rset.m
1958 .top.rset.m add command -label "Load" -command {load-set}
1959 .top.rset.m add separator
1960
1961 menubutton .top.options -text "Options" -underline 0 -menu .top.options.m
1962 menu .top.options.m
1963 .top.options.m add cascade -label "Choose query" -menu .top.options.m.clist
1964 .top.options.m add command -label "Define query" -command {new-query-dialog}
1965 .top.options.m add cascade -label "Edit query" -menu .top.options.m.slist
1966 .top.options.m add cascade -label "Format" -menu .top.options.m.formats
1967
1968 menu .top.options.m.slist
1969 menu .top.options.m.clist
1970 menu .top.options.m.formats
1971 cascade-query-list
1972 set i 0
1973 foreach f $displayFormats {
1974     .top.options.m.formats add radiobutton -label $f \
1975             -command [list set-display-format $i]
1976     incr i
1977 }
1978
1979 menubutton .top.help -text "Help" -menu .top.help.m
1980 menu .top.help.m
1981
1982 .top.help.m add command -label "Help on help" \
1983         -command {tkerror "Help on help not available. Sorry"}
1984 .top.help.m add command -label "About" -command {about-origin}
1985
1986 pack .top.file .top.target .top.service .top.rset .top.options -side left
1987 pack .top.help -side right
1988
1989 index-lines .lines 1 $queryButtonsFind [lindex $queryInfo 0] activate-index
1990
1991 button .mid.search -width 7 -text {Search} -command search-request \
1992         -state disabled
1993 button .mid.scan -width 7 -text {Scan} \
1994         -command [list scan-request "@attr 1=4 @attr 5=1 @attr 4=1"] -state disabled 
1995 button .mid.present -width 7 -text {Present} -command [list present-more 10] \
1996         -state disabled
1997
1998 button .mid.clear -width 7 -text {Clear} -command index-clear
1999 pack .mid.search .mid.scan .mid.present .mid.clear -side left \
2000         -fill y -padx 5 -pady 3
2001
2002 text .data.record -height 2 -width 20 -wrap none \
2003         -yscrollcommand [list .data.scroll set]
2004 scrollbar .data.scroll -command [list .data.record yview]
2005 pack .data.scroll -side right -fill y
2006 pack .data.record -expand yes -fill both
2007
2008 if {[tk colormodel .] == "color"} {
2009     .data.record tag configure marc-tag -foreground blue
2010     .data.record tag configure marc-id -foreground red
2011 } else {
2012     .data.record tag configure marc-tag -foreground black
2013     .data.record tag configure marc-id -foreground black
2014 }
2015 .data.record tag configure marc-data -foreground black
2016
2017 button .bot.logo  -bitmap @book1 -command cancel-operation
2018 frame .bot.a
2019 pack .bot.a -side left -fill x
2020 pack .bot.logo -side right -padx 2 -pady 2
2021
2022 message .bot.a.target -text "" -aspect 1000 -border 1
2023
2024 label .bot.a.status -text "Not connected" -width 15 -relief \
2025         sunken -anchor w -border 1
2026 label .bot.a.set -text "" -width 5 -relief \
2027         sunken -anchor w -border 1
2028 label .bot.a.message -text "" -width 15 -relief \
2029         sunken -anchor w -border 1
2030
2031 pack .bot.a.target -side top -anchor nw -padx 2 -pady 2
2032 pack .bot.a.status .bot.a.set .bot.a.message \
2033         -side left -padx 2 -pady 2
2034
2035 #bind . <Any-Enter> {focus .lines.0.e}
2036
2037 ir z39
2038
2039 .top.options.m.formats invoke $displayFormat
2040     
2041 show-logo 1
2042