583f5d7056cf751caf5023de257ef3e467228fb7
[ir-tcl-moved-to-github.git] / client2 / client.tcl
1 wm title . "IrTcl Client"
2 #wm iconname . "IrTcl Client"
3
4
5 # Procedure irmenu
6 proc irmenu {w} {
7         menu $w -tearoff off
8 }
9
10 proc debug-window {} {
11         set w .debug-window
12     toplevel $w
13
14     wm title $w "Debug Window" 
15     
16     frame $w.top -relief raised -border 1
17     frame $w.bot -relief raised -border 1
18     pack  $w.top -side top -fill both -expand yes
19     pack  $w.bot -fill both
20         scrollbar $w.top.s -command [list $w.top.t yview]
21     text $w.top.t -width 60 -height 10 -wrap word -relief flat -borderwidth 0 \
22         -font fixed -yscroll [list $w.top.s set]
23     pack $w.top.s -side right -fill y
24     pack $w.top.t -expand yes -fill both -expand y
25 }
26 debug-window
27
28     
29 # Procedure configure-enable-e {w n}
30 #  w   is a menu
31 #  n   menu entry number (0 is first entry)
32 # Enables menu entry
33 proc configure-enable-e {w n} {
34         $w entryconfigure $n -state normal
35 }
36
37 # Procedure configure-disable-e {w n}
38 #  w   is a menu
39 #  n   menu entry number (0 is first entry)
40 # Disables menu entry
41 proc configure-disable-e {w n} {
42         $w entryconfigure $n -state disabled
43 }
44 set noFocus [list -takefocus 0]
45
46
47 # Define dummy clock function if it is not there.
48 if {[catch {clock seconds}]} {
49     proc clock {args} {
50         return {}
51     }
52 }
53
54 # Define libdir to the IrTcl configuration directory.
55 # In the line below LIBDIR will be modified during 'make install'.
56 set libdir LIBDIR
57
58 # If the bitmaps sub directory is present with a bitmap we assume 
59 # the client is run from the source directory in which case we
60 # set libdir the current directory.
61 if {[file readable [file join bitmaps book2]]} {
62     set libdir .
63 }
64
65 # Make a final check to see if libdir was set ok.
66 if {! [file readable [file join $libdir bitmaps book2]]} {
67     puts "Cannot locate system files in ${libdir}. You must either run this"
68     puts "program from the source directory root of ir-tcl or you must assure"
69     puts "that it is installed - normally in /usr/local/lib/irtcl"
70     exit 1
71 }
72
73 # Initialize a lot of globals.
74 set hotTargets {}
75 set hotInfo {}
76 set busy 0
77
78 set profile(Default,description) {}
79 set profile(Default,host) {}
80 set profile(Default,port) 210
81 set profile(Default,authentication) {}
82 set profile(Default,maximumRecordSize) 50000
83 set profile(Default,preferredMessageSize) 30000
84 set profile(Default,comstack) tcpip
85 set profile(Default,namedResultSets) 1
86 set profile(Default,queryRPN) 1
87 set profile(Default,queryCCL) 0
88 set profile(Default,protocol) Z39
89 set profile(Default,windowNumber) 1
90 set profile(Default,largeSetLowerBound) 2
91 set profile(Default,smallSetUpperBound) 0
92 set profile(Default,mediumSetPresentNumber) 0
93 set profile(Default,presentChunk) 4
94 set profile(Default,timeDefine) {}
95 set profile(Default,timeLastInit) {}
96 set profile(Default,timeLastExplain) {}
97 set profile(Default,targetInfoName) {}
98 set profile(Default,recentNews) {}
99 set profile(Default,maxResultSets) {}
100 set profile(Default,maxResultSize) {}
101 set profile(Default,maxTerms) {}
102 set profile(Default,multipleDatabases) 0
103 set profile(Default,welcomeMessage) {}
104
105 set hostid Default
106 set currentDb Default
107 set settingsChanged 0
108 set setNo 0
109 set setNoLast 0
110 set cancelFlag 0
111 set scanEnable 0
112 set displayFormat 1
113 set popupMarcdf 0
114 set textWrap word
115 set recordSyntax None
116 set elementSetNames None
117 set delayRequest {}
118 set debugMode 0
119 set queryAutoOld 0
120
121 set queryTypes {Simple}
122 set queryButtons { { {I 0} {I 1} {I 2} } }
123 set queryInfo { { {Title {1=4 4=1}} {Author {1=1}} \
124         {Subject {1=21}} {Any {1=1016}} } }
125 set queryAuto 1
126 wm minsize . 0 0
127
128 set setOffset 0
129 set setMax 0
130
131 set syntaxList {None sep USMARC UNIMARC UKMARC DANMARC FINMARC NORMARC PICAMARC sep SUTRS sep GRS1}
132
133
134 set font(bb,normal) {Helvetica 24}
135 set font(bb,bold) {Helvetica 24 bold}
136 set font(b,normal) {Helvetica 24}
137 set font(b,bold) {Helvetica 18 bold}
138 set font(n,normal) {Helvetica 12}
139 set font(n,bold) {Helvetica 12 bold}
140 set font(s,bold) {Helvetica 10 bold}
141 set font(ss,bold) {Helvetica 8 bold}
142
143 # Procedure tkerror {err}
144 #   err   error message
145 # Override the Tk error handler function.
146 if {1} {
147     proc tkerror err {
148                 global font
149                 set w .tkerrorw
150                 
151                 if {[winfo exists $w]} {
152                     destroy $w
153                 }
154                 toplevel $w
155                 wm title $w "Error"
156                 
157                 place-force $w .
158                 top-down-window $w
159                 
160                 label $w.top.b -bitmap error
161                 message $w.top.t -aspect 300 -text "Error: $err" -font $font(b,bold)
162                 pack $w.top.b $w.top.t -side left -padx 10 -pady 10
163                 
164                 bottom-buttons $w [list {Close} [list destroy $w]] 1
165     }
166 }
167
168 # Read tag set file (if present)
169 if {[file readable [file join $libdir tagsets.tcl]]} {
170         source [file join $libdir tagsets.tcl]
171 }
172
173 # Read the global target configuration file.
174 if {[file readable [file join $libdir irtdb.tcl]]} {
175 #    source "${libdir}/irtdb.tcl"
176     source [file join $libdir irtdb.tcl]
177 }
178 # Read the local target configuration file.
179 if {[file readable "irtdb.tcl"]} {
180     source "irtdb.tcl"
181 }
182
183 # Read the user configuration file.
184 if {[file readable [file join $libdir .clientrc.tcl]]} {
185 #    source "${libdir}/.clientrc.tcl"
186     source [file join $libdir .clientrc.tcl]
187 }
188
189 source "bib-1.tcl"
190
191 set queryAutoOld $queryAuto
192
193 # Convert old format to new format...
194 foreach target [array names profile] {
195     set timedef [clock seconds]
196     if {[string first , $target] == -1} {
197         if {![info exists profile($target,port)]} {
198             foreach n [array names profile Default,*] {
199                 set profile($target,[string range $n 8 end]) $profile($n)
200             }
201             set profile($target,description) [lindex $profile($target) 0]
202             set profile($target,host) [lindex $profile($target) 1]
203             set profile($target,port) [lindex $profile($target) 2]
204             set profile($target,authentication) [lindex $profile($target) 3]
205             set profile($target,maximumRecordSize) \
206                 [lindex $profile($target) 4]
207             set profile($target,preferredMessageSize) \
208                 [lindex $profile($target) 5]
209             set profile($target,comstack) [lindex $profile($target) 6]
210             set profile($target,databases) [lindex $profile($target) 7]
211             set profile($target,timeDefine) $timedef
212             set profile($target,windowNumber) 1
213         }
214         unset profile($target)
215     }
216 }
217
218 # Assign unique ID's to each target's window number
219 set wno 1
220 foreach n [array names profile *,windowNumber] {
221     set profile($n) $wno
222     incr wno
223 }
224 set profile(Default,windowNumber) $wno
225
226 # These globals describe the current query type. They are set to the
227 # first query type.
228 set queryButtonsFind [lindex $queryButtons 0]
229 set queryInfoFind [lindex $queryInfo 0]
230
231 # Procedure read-formats
232 # Read all Tcl source files in the subdirectory 'formats'.
233 # The name of each source will correspond to a display format.
234 proc read-formats {} {
235     global displayFormats
236     global libdir
237
238     set oldDir [pwd]
239     cd [file join $libdir formats]
240     set formats [glob {*.[tT][cC][lL]}]
241     foreach f $formats {
242         if {[file readable $f]} {
243             source $f
244             set l [string length $f]
245             set f [string tolower [string range $f 0 [expr $l - 5]]]
246             lappend displayFormats $f
247         }
248     }
249     cd $oldDir
250 }
251
252 # Procedure set-wrap {m}
253 #  m    boolean wrap mode
254 # Handler to enable/disable text wrap in the main record window
255 proc set-wrap {m} {
256     global textWrap
257
258     set textWrap $m
259     .data.record configure -wrap $m
260 }
261
262 # Procedure dputs {m}
263 #  m    string to be printed
264 # puts utility for debugging.
265 proc dputs {m} {
266     global debugMode
267     if {$debugMode} {
268         puts $m
269     }
270 }
271
272 # Procedure apduDump {}
273 # Logs BER dump of last APDU in window if debugMode is true.
274 proc apduDump {} {
275     global debugMode
276
277     set w .apdu
278
279     if {$debugMode == 0} return
280     set x [z39 apduInfo]
281
282     set offset [lindex $x 1]
283     set length [lindex $x 0]
284
285     if {![winfo exists $w]} {
286         catch {destroy $w}
287         toplevelG $w
288
289         wm title $w "APDU information"       
290         wm minsize $w 0 0
291         
292         top-down-window $w
293         
294         text $w.top.t -font fixed -width 60 -height 12 -wrap word \
295                         -relief flat -borderwidth 0 \
296                         -yscrollcommand [list $w.top.s set] -background grey85
297         scrollbar $w.top.s -command [list $w.top.t yview]
298         pack $w.top.s -side right -fill y
299         pack $w.top.t -expand yes -fill both
300
301         bottom-buttons $w [list {Close} [list destroy $w]] 0
302     }
303     $w.top.t insert end "Length: ${length}\n"
304     if {$offset != -1} {
305         $w.top.t insert end "Offset: ${offset}\n"
306     }
307     $w.top.t insert end [lindex $x 2]
308     $w.top.t insert end "---------------------------------\n"
309
310 }
311
312 # Procedure set-display-format {f}
313 #  f    display format
314 # Reformats main record window to use display format given by f
315 proc set-display-format {f} {
316     global displayFormat setNo busy
317
318     set displayFormat $f
319     if {$setNo == 0} {
320         return
321     }
322     if {!$busy} {
323         .bot.a.status configure -text "Reformatting"
324     }
325     update idletasks
326     add-title-lines -1 10000 1
327 }
328
329 # Procedure initBindings
330 # Disables various default bindings for Text and Listbox widgets.
331 proc initBindings {} {
332     global TextBinding
333
334     foreach e [bind Text] {
335         set TextBinding($e) [bind Text $e]
336         bind Text $e {}
337     }
338     set w Listbox
339     bind $w <B1-Motion> {}
340     bind $w <Shift-B1-Motion> {}
341
342     set w Entry
343 }
344
345 # Procedure TextEditable 
346 # Apply "standard" events to a text widget. It should be editable now.
347 proc TextEditable {w} {
348     global TextBinding
349
350     foreach e [array names TextBinding] {
351         bind $w $e $TextBinding($e)
352     }
353 }
354
355 # Procedure destroyGW {w}
356 #   w     top level widget
357 # Saves geometry of widget w in windowGeometry array. This
358 # Procedure is used to save current geometry of a window before
359 # it is destroyed.
360 # See also topLevelG.
361 proc destroyGW {w} {
362     global windowGeometry
363     catch {set windowGeometry($w) [wm geometry $w]}
364 }    
365
366 # Procedure topLevelG
367 #   w     top level widget
368 # Makes a new top level widget named w; sets geometry of window if it 
369 # exists in windowGeometry array. The destroyGW procedure is set 
370 # to be called when the Destroy event occurs.
371 proc toplevelG {w} {
372     global windowGeometry
373
374     toplevel $w
375     if {[info exists windowGeometry($w)]} {
376         set g $windowGeometry($w)
377         if {$g != ""} {
378             wm geometry $w $g
379         }
380     }
381     bind $w <Destroy> [list destroyGW $w]
382 }
383
384 # Procedure top-down-window {w}
385 #  w    window (possibly top level)
386 # Makes two frames inside w called top and bot.
387 proc top-down-window {w} {
388     frame $w.top -relief raised -border 1
389     frame $w.bot -relief raised -border 1
390     pack  $w.top -side top -fill both -expand yes
391     pack  $w.bot -fill both
392 }
393
394 # Procedure top-down-ok-cancel {w ok-action g}
395 #  w          top level widget with $w.bot-frame
396 #  ok-action  ok script
397 #  g          grab flag
398 # Makes two buttons in the bot frame called Ok and Cancel. The
399 # ok-action is executed if Ok is pressed. If Cancel is activated
400 # The window is destroyed. If g is true a grab is performed on the
401 # window and the procedure waits until the window is destroyed.
402 proc top-down-ok-cancel {w ok-action g} {
403     frame $w.bot.left -relief sunken -border 1
404     pack $w.bot.left -side left -expand yes -ipadx 2 -ipady 2 -padx 1 -pady 1
405     button $w.bot.left.ok -width 4 -text {Ok} -command ${ok-action}
406     pack $w.bot.left.ok -expand yes -ipadx 1 -ipady 1 -padx 2 -pady 2
407     button $w.bot.cancel -width 5 -text {Cancel} -command [list destroy $w]
408     pack $w.bot.cancel -side left -expand yes    
409
410     if {$g} {
411         grab $w
412         tkwait window $w
413     }
414 }
415
416 # Procedure bottom-buttons {w buttonList g}
417 #  w          top level widget with $w.bot-frame
418 #  buttonList button specifications
419 #  g          grab flag
420 # Makes a list of buttons in the $w.bot frame. The buttonList is a list 
421 # of button specifications. Each button specification consists of two
422 # items; the first item is button label name; the second item is a script
423 # of be executed when that button is executed. A grab is performed if g 
424 # is true and it waits for the window to be destroyed.
425 proc bottom-buttons {w buttonList g} {
426     set i 0
427     set l [llength $buttonList]
428
429     frame $w.bot.$i -relief sunken -border 1
430     pack $w.bot.$i -side left -expand yes -padx 2 -pady 2
431     button $w.bot.$i.ok -text [lindex $buttonList $i] \
432                 -command [lindex $buttonList [expr $i + 1]]
433     pack $w.bot.$i.ok -expand yes -padx 2 -pady 2 -side left
434
435     incr i 2
436     while {$i < $l} {
437         button $w.bot.$i -text [lindex $buttonList $i] \
438                 -command [lindex $buttonList [expr $i + 1]]
439         pack $w.bot.$i -expand yes -padx 2 -pady 2 -side left
440         incr i 2
441     }
442     if {$g} {
443         # Grab ...
444         grab $w
445         tkwait window $w
446     }
447 }
448
449 # Procedure cancel-operation
450 # This handler is invoked when the user wishes to cancel an operation.
451 # If the system is currently busy a "Cancel" will be displayed in the
452 # status area and the cancelFlag is set to true indicating that future
453 # responses from the target should be ignored. The system is no longer
454 # busy when this procedure exists.
455 proc cancel-operation {} {
456     global cancelFlag busy delayRequest
457
458     if {$busy} {
459         set cancelFlag 1
460         set delayRequest {}
461         show-status Cancel 0 1
462     }
463 }
464
465 # Procedure show-target {target base}
466 #  target     name of target
467 #  base       name of database
468 # Displays target name and database name in the target status area.
469 proc show-target {target base} {
470     if {![string length $target]} {
471         .bot.a.target configure -text {}
472         return
473     }
474     if {![string length $base]} {
475                 .bot.a.target configure -text "$target"
476     } else {
477          .bot.a.target configure -text "$target - $base"
478     }
479 }
480
481 # Procedure show-logo {v1}
482 #  v1    integer level
483 # This procedure maintains the book logo in the bottom of the screen.
484 # It is invoked only once during initialization of windows, etc., and
485 # by itself. The global 'busy' variable determines whether the logo is
486 # moving or not.
487 proc show-logo {v1} {
488     global busy libdir
489
490     if {$busy != 0} {
491         incr v1
492         if {$v1==10} {
493             set v1 1
494         }
495         .bot.logo configure -bitmap @[file join $libdir bitmaps book${v1}] 
496         after 140 [list show-logo $v1]
497         return
498     }
499     while {1} {
500         .bot.logo configure -bitmap @[file join $libdir bitmaps book1]
501         tkwait variable busy
502         if {$busy} {
503             show-logo 1
504             return
505         }
506     }
507 }
508
509 # Procedure show-status {status b sb}
510 #  status     status message string
511 #  b          busy indicator
512 #  sb         search busy indicator
513 # Display status information according to 'status' and sets the global
514 # busy flag 'busy' to b if b is non-empty. If sb is non-empty it indicates
515 # whether service buttons should be enabled or disabled.
516 proc show-status {status b sb} {
517     global busy scanEnable setOffset setMax setNo
518
519     .bot.a.status configure -text "$status"
520     if {$b == 1} {
521         if {$busy == 0} {set busy 1}
522     } else {
523         set busy 0
524     }
525     if {$sb == {}} {
526         return
527     }
528     if {$sb} {
529         .top.service configure -state normal
530         .mid.search configure -state normal
531         if {$scanEnable} {
532             .mid.scan configure -state normal
533         } else {
534             configure-disable-e .top.service.m 3
535         }
536         if {$setNo == 0} {
537             configure-disable-e .top.service.m 1
538         } elseif {[z39.$setNo nextResultSetPosition] > 0 && 
539             [z39.$setNo nextResultSetPosition] <= [z39.$setNo resultCount]} {
540             configure-enable-e .top.service.m 1
541             .mid.present configure -state normal
542         } else {
543             configure-disable-e .top.service.m 1
544             .mid.present configure -state disabled
545         }
546         if {[winfo exists .scan-window]} {
547             .scan-window.bot.2 configure -state normal
548             .scan-window.bot.4 configure -state normal
549         }
550     } else {
551         .top.service configure -state disabled
552         .mid.search configure -state disabled
553         .mid.scan configure -state disabled
554         .mid.present configure -state disabled
555
556         if {[winfo exists .scan-window]} {
557             .scan-window.bot.2 configure -state disabled
558             .scan-window.bot.4 configure -state disabled
559         }
560     }
561 }
562
563 # Procedure show-message {msg}
564 #  msg    message string
565 # Sets message the bottom of the screen to msg.
566 proc show-message {msg} {
567     .bot.a.message configure -text "$msg"
568 }
569
570 # Procedure insertWithTags {w text args}
571 #  w      text widget
572 #  text   string to be inserted
573 #  args   list of tags
574 # Inserts text at the insertion point in widget w. The text is tagged 
575 # with the tags in args.
576 proc insertWithTags {w text args} {
577     set start [$w index insert]
578     $w insert insert $text
579     foreach tag [$w tag names $start] {
580         $w tag remove $tag $start insert
581     }
582     foreach i $args {
583         $w tag add $i $start insert
584     }
585 }
586
587 # Procedure popup-license and displays LICENSE information.
588 proc popup-license {} {
589     global libdir
590     set w .popup-license
591     toplevel $w
592
593     wm title $w "License" 
594     wm minsize $w 0 0
595
596     top-down-window $w
597
598     text $w.top.t -width 80 -height 10 -wrap word -relief flat -borderwidth 0 \
599         -font fixed -yscrollcommand [list $w.top.s set]
600     scrollbar $w.top.s -command [list $w.top.t yview]
601     pack $w.top.s -side right -fill y
602     pack $w.top.t -expand yes -fill both
603
604     if {[file readable [file join $libdir LICENSE]]} {
605         set f [open [file join $libdir LICENSE] r]
606         while {[gets $f buf] != -1} {
607             $w.top.t insert end $buf
608             $w.top.t insert end "\n"
609         } 
610         close $f
611     }
612     bottom-buttons $w [list {Close} [list destroy $w]] 1
613 }
614
615 # Procedure about-target
616 # Displays various information about the current target, such
617 # as implementation-name, implementation-id, etc.
618 proc about-target {} {
619     set w .about-target-w
620     global hostid font
621
622     toplevel $w
623
624     wm title $w "About target"
625     place-force $w .
626     top-down-window $w
627
628     frame $w.top.a -relief ridge -border 2
629     frame $w.top.p -relief ridge -border 2
630     pack $w.top.a $w.top.p -side top -fill x
631
632     label $w.top.a.about -text "About"
633     label $w.top.a.irtcl -text $hostid -font $font(bb,bold)
634     pack $w.top.a.about $w.top.a.irtcl -side top
635
636     set i [z39 targetImplementationName]
637     label $w.top.p.in -text "Implementation name: $i"
638     set i [z39 targetImplementationId]
639     label $w.top.p.ii -text "Implementation id: $i"
640     set i [z39 targetImplementationVersion]
641     label $w.top.p.iv -text "Implementation version: $i"
642     set i [z39 options]
643     label $w.top.p.op -text "Protocol options: $i"
644     pack $w.top.p.in $w.top.p.ii $w.top.p.iv $w.top.p.op -side top -anchor nw
645
646     bottom-buttons $w [list {Close} [list destroy $w]] 1
647 }
648
649 # Procedure about-origin-logo {n}
650 #   n    integer level
651 # Displays book logo in the .about-origin-w widget
652 proc about-origin-logo {n} {
653     global libdir
654     set w .about-origin-w
655     if {![winfo exists $w]} {
656         return
657     }
658     incr n
659     if {$n==10} {
660         set n 1
661     }
662     $w.top.a.logo configure -bitmap @[file join $libdir bitmaps book$n]
663     after 140 [list about-origin-logo $n]
664 }
665
666 # Procedure about-origin
667 # Display various information about origin (this client).
668 proc about-origin {} {
669     set w .about-origin-w
670     global libdir font tk_version
671     
672     if {[winfo exists $w]} {
673         destroy $w
674     }
675     toplevel $w
676
677     wm title $w "About IrTcl"
678     place-force $w .
679     top-down-window $w
680
681     frame $w.top.a -relief ridge -border 2
682     frame $w.top.p -relief ridge -border 2
683     pack $w.top.a $w.top.p -side top -fill x
684     
685     label $w.top.a.irtcl -text "IrTcl" -font $font(bb,bold)
686     label $w.top.a.logo -bitmap @[file join $libdir bitmaps book1] 
687     pack $w.top.a.irtcl $w.top.a.logo -side left -expand yes
688
689     set i unknown
690     catch {set i [z39 implementationName]}
691     label $w.top.p.in -text "Implementation name: $i"
692     catch {set i [z39 implementationId]}
693     label $w.top.p.ii -text "Implementation id: $i"
694     catch {set i [z39 implementationVersion]}
695     label $w.top.p.iv -text "Implementation version: $i"
696     set i $tk_version
697     label $w.top.p.tk -text "Tk version: $i"
698     pack $w.top.p.in $w.top.p.ii $w.top.p.iv $w.top.p.tk -side top -anchor nw
699
700     about-origin-logo 1
701     bottom-buttons $w [list {Close} [list destroy $w] \
702                             {License} [list popup-license]] 0
703 }
704
705 # Procedure popup-marc {sno no b df}
706 #  sno     result set number
707 #  no      record position number
708 #  b       popup window number
709 #  df      display format
710 # Displays record in set $sno at position $no in window .full-marc$b.
711 # The global variable $popupMarcdf holds the current format method.
712 proc popup-marc {sno no b df} {
713     global font displayFormats popupMarcdf
714
715     if {[z39.$sno type $no] != "DB"} {
716         return
717     }
718     if {$b == -1} {
719         set b 0
720         while {[winfo exists .full-marc$b]} {
721             incr b
722         }
723     }
724     set df $popupMarcdf
725     set w .full-marc$b
726     if {![winfo exists $w]} {
727         toplevelG $w
728
729         wm minsize $w 0 0
730         
731         frame $w.top -relief raised -border 1
732         frame $w.bot -relief raised -border 1
733         pack  $w.top -side top -fill both -expand yes
734         pack  $w.bot -fill both
735
736         text $w.top.record -width 60 -height 5 -wrap word -relief flat \
737             -borderwidth 0 -font fixed \
738             -yscrollcommand [list $w.top.s set] -background grey85
739         scrollbar $w.top.s -command [list $w.top.record yview] 
740         $w.top.record tag configure marc-tag -foreground blue
741         $w.top.record tag configure marc-id -foreground red
742         $w.top.record tag configure marc-data -foreground black
743         $w.top.record tag configure marc-head -font $font(n,bold) \
744                 -background black -foreground white
745         $w.top.record tag configure marc-pref -font $font(n,normal) -foreground blue
746         $w.top.record tag configure marc-text -font $font(n,normal) -foreground black
747         $w.top.record tag configure marc-it -font $font(n,normal) -foreground black
748
749         pack $w.top.s -side right -fill y
750         pack $w.top.record -expand yes -fill both
751         
752         bottom-buttons $w [list \
753             {Close} [list destroy $w] \
754             {Prev} {} \
755             {Next} {} \
756             {Duplicate} {}] 0
757         menubutton $w.bot.formats -text "Format" -menu $w.bot.formats.m -relief raised
758         irmenu $w.bot.formats.m
759         pack $w.bot.formats -expand yes -ipadx 2 -ipady 2 -padx 3 -pady 3 -side left
760     } else {
761         $w.bot.formats.m delete 0 last
762     }
763     set i 0
764     foreach f $displayFormats {
765         $w.bot.formats.m add radiobutton -label $f -variable popupMarcdf -value $i \
766                 -command [list popup-marc $sno $no $b 0]
767         incr i
768     }
769     $w.top.record delete 0.0 end
770     set recordType [z39.$sno recordType $no]
771     wm title $w "$recordType record #$no"
772
773     $w.bot.2 configure -command [list popup-marc $sno [expr $no-1] $b $df]
774     $w.bot.4 configure -command [list popup-marc $sno [expr $no+1] $b $df]
775     if {$no == 1} {
776         $w.bot.2 configure -state disabled
777     } else {
778         $w.bot.2 configure -state normal
779     }
780     if {[z39.$sno type [expr $no+1]] != "DB"} {
781         $w.bot.4 configure -state disabled
782     } else {
783         $w.bot.4 configure -state normal
784     }
785     $w.bot.6 configure -command [list popup-marc $sno $no -1 0]
786     set ffunc [lindex $displayFormats $df]
787     set ffunc "display-$ffunc"
788
789     $ffunc $sno $no $w.top.record 0
790 }
791
792 # Procedure update-target-hotlist {target base}
793 #  target     current target name
794 #  base       current database name
795 # Updates the global $hotTargets so that $target and $base are
796 # moved to the front, i.e. they become the number 1 target/base.
797 # The target menu is updated by a call to set-target-hotlist.
798 proc update-target-hotlist {target base} {
799     global hotTargets
800
801     set olen [llength $hotTargets]
802     set i 0
803     foreach e $hotTargets {
804         if {$target == [lindex $e 0] && $base == [lindex $e 1]} {
805             set hotTargets [lreplace $hotTargets $i $i]
806             break
807         }
808         incr i    
809     }
810     set hotTargets [linsert $hotTargets 0 [list $target $base]]
811     set-target-hotlist $olen
812
813
814 # Procedure delete-target-hotlist {target}
815 #  target    target to be deleted
816 # Updates the global $hotTargets so that $target is removed.
817 # The target menu is updated by a call to set-target-hotlist.
818 proc delete-target-hotlist {target} {
819     global hotTargets
820
821     set olen [llength $hotTargets]
822     set i 0
823     foreach e $hotTargets {
824         if {$target == [lindex $e 0]} {
825             set hotTargets [lreplace $hotTargets $i $i]
826         }
827         incr i
828     }
829     set-target-hotlist $olen
830 }
831
832 # Procedure set-target-hotlist {olen}
833 #  olen     number of hot target entries to be deleted from menu
834 # Updates the target menu with the targets with the first 8 entries
835 # in the $hotTargets global.
836 proc set-target-hotlist {olen} {
837     global hotTargets
838    
839     if {$olen > 0} {
840        .top.target.m delete 6 [expr 6+$olen]
841     }
842     set i 1
843     foreach e $hotTargets {
844         set target [lindex $e 0]
845         set base [lindex $e 1]
846         if {![string length $base]} {
847             .top.target.m add command -label "$i $target" -command \
848                 [list reopen-target $target {}]
849         } else {
850             .top.target.m add command -label "$i $target - $base" -command \
851                 [list reopen-target $target $base]
852         }
853         incr i
854         if {$i > 8} {
855              break
856         }
857     }
858 }
859
860 # Procedure reopen-target {target base}
861 #  target    target to be opened
862 #  base      base to be used
863 # Closes connection with current target and opens a new connection
864 # with $target and database $base.
865 proc reopen-target {target base} {
866     close-target
867     open-target $target $base
868     update-target-hotlist $target $base
869 }
870
871 # Procedure define-target-action
872 # Prepares the setup of a new target. The name of the target
873 # is read from the dialog .target-define dialog (procedure
874 # define-target-dialog) and the target definition window is displayed by
875 # a call to protocol-setup.
876 proc define-target-action {} {
877     global profile
878     
879     set target [.target-define.top.target.entry get]
880     if {![string length $target]} {
881         return
882     }
883     foreach n [array names profile *,host] {
884         if {![string compare $n ${target},host]} {
885             destroy .target-define
886             protocol-setup $n
887             return
888         }
889     }
890     foreach n [array names profile Default,*] {
891         set profile($target,[string range $n 8 end]) $profile($n)
892
893     }
894     incr profile(Default,windowNumber)
895     
896     protocol-setup $target
897     destroy .target-define
898 }
899
900 # Procedure fail-response {target}
901 #  target   current target
902 # Error handler (IrTcl failback) that takes care of serious protocol
903 # errors, connection lost, etc.
904 proc fail-response {target} {
905     global debugMode
906
907     set c [lindex [z39 failInfo] 0]
908     set m [lindex [z39 failInfo] 1]
909     if {$c == 4 || $c == 5} {
910         set debugMode 1        
911         apduDump
912     }
913     close-target
914 #    tkerror "$m ($c)"
915         bgerror "$m ($c)"
916 }
917
918 # Procedure connect-response {target base}
919 #  target   current target
920 #  base     current database
921 # IrTcl connect response handler.
922 proc connect-response {target base} {
923     dputs "connect-response"
924     init-request $target $base
925 }
926
927 # Procedure open-target {target base}
928 #  target   target to be opened
929 #  base     database to be used
930 # Opens a new connection with $target/$base.
931 proc open-target {target base} {
932     global profile hostid presentChunk currentDb
933
934     z39 disconnect
935     z39 comstack $profile($target,comstack)
936     z39 protocol $profile($target,protocol)
937     eval z39 idAuthentication $profile($target,authentication)
938     z39 maximumRecordSize $profile($target,maximumRecordSize)
939     z39 preferredMessageSize $profile($target,preferredMessageSize)
940     dputs "maximumRecordSize=[z39 maximumRecordSize]"
941     dputs "preferredMessageSize=[z39 preferredMessageSize]"
942     show-status Connecting 1 0
943     set x $profile($target,largeSetLowerBound)
944     if {![string length $x]} {
945         set x 2
946     }
947     z39 largeSetLowerBound $x
948     
949     set x $profile($target,smallSetUpperBound)
950     if {![string length $x]} {
951         set x 0
952     }
953     z39 smallSetUpperBound $x
954     
955     set x $profile($target,mediumSetPresentNumber)
956     if {![string length $x]} {
957         set x 0
958     }
959     z39 mediumSetPresentNumber $x
960
961     set presentChunk $profile($target,presentChunk)
962     if {![string length $presentChunk]} {
963         set presentChunk 4
964     }
965
966     z39 failback [list fail-response $target]
967     z39 callback [list connect-response $target $base]
968     show-target $target $base
969     update idletasks
970     set err [catch {
971         z39 connect $profile($target,host):$profile($target,port)
972     } errorMessage]
973     if {$err} {
974         set hostid Default
975 #        tkerror $errorMessage
976         bgerror $errorMessage
977         show-status "Not connected" 0 {}
978         show-target {} {}
979         return
980     }
981     set hostid $target
982     set currentDb $base
983     configure-disable-e .top.target.m 0
984     configure-enable-e .top.target.m 1
985     configure-enable-e .top.target.m 2
986 }
987
988 # Procedure close-target
989 # Shuts down the connection with current target.
990 proc close-target {} {
991     global hostid cancelFlag setNo setNoLast currentDb
992
993     set cancelFlag 0
994     set setNo 0
995     set setNoLast 0
996     .bot.a.set configure -text ""
997     set hostid Default
998     set currentDb Default
999     z39 disconnect
1000     show-target {} {}
1001     show-status {Not connected} 0 0
1002     .top.options.m.query.slist entryconfigure 2 -state disabled
1003     init-title-lines
1004     show-message {}
1005     configure-disable-e .top.target.m 1
1006     configure-disable-e .top.target.m 2
1007     .top.rset.m delete 1 last
1008     .top.rset.m add separator
1009     configure-enable-e .top.target.m 0
1010 }
1011
1012 # Procedure load-set-action
1013 # Loads records from a file. The filename is read from the entry
1014 # .load-set.filename.entry (see function load-set)
1015 proc load-set-action {} {
1016     global setNoLast
1017
1018     incr setNoLast
1019     ir-set z39.$setNoLast z39
1020
1021     set fname [.load-set.top.filename.entry get]
1022     destroy .load-set
1023     if {$fname != ""} {
1024         show-status Loading 1 {}
1025         update
1026         z39.$setNoLast loadFile $fname
1027
1028         set no [z39.$setNoLast numberOfRecordsReturned]
1029         add-title-lines $setNoLast $no 1
1030     }
1031     set l [format "%-4d %7d" $setNoLast $no]
1032     .top.rset.m add command -label $l \
1033             -command [list add-title-lines $setNoLast 10000 1]
1034     show-status Ready 0 {}
1035 }
1036
1037 # Procedure load-set
1038 # Dialog that asks for a filename with records to be loaded
1039 # into a result set.
1040 proc load-set {} {
1041     set w .load-set
1042     toplevel $w
1043     set oldFocus [focus]
1044     place-force $w .
1045     top-down-window $w
1046
1047     frame $w.top.filename
1048     pack $w.top.filename -side top -anchor e -pady 2
1049     
1050     entry-fields $w.top {filename} \
1051             {{Filename:}} \
1052             {load-set-action} {destroy .load-set}
1053     
1054     top-down-ok-cancel $w {load-set-action} 1
1055     focus $oldFocus
1056 }
1057
1058 # Procedure init-request
1059 # Sends an initialize request to the target. This procedure is called
1060 # when a connect has been established.
1061 proc init-request {target base} {
1062     global cancelFlag
1063
1064     if {$cancelFlag} {
1065         close-target
1066         return
1067     }
1068     z39 callback [list init-response $target $base]
1069     show-status Initializing 1 {}
1070     set err [catch {z39 init} errorMessage]
1071     if {$err} {
1072 #        tkerror $errorMessage
1073         bgerror $errorMessage
1074         show-status Ready 0 {}
1075     }
1076 }
1077
1078 # Procedure init-response
1079 # Handles and incoming init-response. The service buttons
1080 # are enabled. The global $scanEnable indicates whether the target
1081 # supports scan.
1082 proc init-response {target base} {
1083     global cancelFlag profile scanEnable settingsChanged
1084
1085     dputs {init-response}
1086     apduDump
1087     if {$cancelFlag} {
1088         close-target
1089         return
1090     }
1091     if {![z39 initResult]} {
1092         set u [z39 userInformationField]
1093         close-target
1094 #        tkerror "Connection rejected by target: $u"
1095         bgerror "Connection rejected by target: $u"
1096     } else {
1097                 z39 failback [list explain-crash $target $base]
1098         explain-check $target [list ready-response $base] $base
1099     }
1100 }
1101
1102 # Procedure explain-crash
1103 # Handles target that dies during explain.
1104 proc explain-crash {target base} {
1105     global profile settingsChanged
1106     
1107     set profile($target,timeLastInit) [clock seconds]
1108     set settingsChanged 1
1109
1110     show-message {}
1111     open-target $target $base
1112 }
1113
1114 # Procedure explain-check 
1115 # Stub function to check explain. May be overwritten later.
1116 #proc explain-check {target response} 
1117 #    eval $response [list $target]
1118
1119
1120 # Procedure ready-response
1121 # Called after a target has been initialized and, possibly, explained
1122 proc ready-response {base target} {
1123     global profile settingsChanged scanEnable queryAuto
1124     
1125     z39 failback [list fail-response $target]
1126     if {[string length $base]} {
1127         set profile($target,timeLastInit) [clock seconds]
1128         set settingsChanged 1
1129
1130         z39 databaseNames $base
1131         cascade-dblist $target $base
1132         show-target $target $base
1133     }
1134     if {[lsearch [z39 options] scan] >= 0} {
1135         set scanEnable 1
1136     } else {
1137         set scanEnable 0
1138     }
1139     .data.record delete 1.0 end
1140     set desc [string trim $profile($target,description)]
1141     if {[string length $desc]} {
1142         .data.record insert end "$desc\n\n"
1143     } else {
1144         .data.record insert end "$target\n\n"
1145     }
1146     set data [string trim $profile($target,welcomeMessage)]
1147     if {[string length $data]} {
1148                 .data.record insert end "Welcome Message:\n$data\n\n"
1149     }
1150     set data [string trim $profile($target,recentNews)]
1151     if {[string length $data]} {
1152         .data.record insert end "News:\n$data\n"
1153     }
1154     ready-response-actions $target $base
1155     show-message {}
1156     show-status Ready 0 1
1157 }
1158
1159 #proc ready-response-actions {target base}
1160 #This procedure take care of all the actions that should start if connect is succesfull.
1161 proc ready-response-actions {target base} {
1162         global profile queryAuto
1163 #       changeQueryButtons $target $base 
1164         configureOptionsSyntax $target $base
1165         if {[info exists profile($target,AttributeDetails,$base,Bib1Use)] && $queryAuto == 1} {
1166                 changeQueryButtons $target $base 
1167                 change-queryInfo $target $base
1168                 query-select 2
1169                 .top.options.m.query.slist entryconfigure 2 -state normal
1170 #               listbuttonx
1171         } else {
1172                 query-select 0
1173                 .top.options.m.query.slist entryconfigure 2 -state disabled
1174         }
1175 }
1176
1177 # Procedure search-request
1178 #  bflag     flag to indicate if this procedure calls itself
1179 # Performs a search. If $busy is 1, the search-request is performed
1180 # at a later time (when another response arrives). This procedure
1181 # sets many search-related Z39-settings. The global $setNo is set
1182 # to the result set number (z39.$setNo).
1183 proc search-request {bflag} {
1184     global setNo setNoLast profile hostid busy cancelFlag delayRequest recordSyntax elementSetNames
1185
1186     set target $hostid
1187     
1188     if {![string length [z39 connect]]} {
1189         return
1190     }
1191     dputs "search-request"
1192     show-message {}
1193     if {!$bflag && $busy} {
1194         dputs "busy: search-request ignored"
1195         return
1196     }
1197     if {$cancelFlag} {
1198         dputs "cancelFlag"
1199         show-status Searching 1 0
1200         set delayRequest {search-request 1}
1201         return
1202     }
1203     set delayRequest {} 
1204
1205     set query [index-query]
1206     if {![string length $query]} {
1207         return
1208     }
1209     incr setNoLast
1210     set setNo $setNoLast
1211     ir-set z39.$setNo z39
1212     
1213     if {$profile($target,namedResultSets)} {
1214         z39.$setNo setName $setNo
1215         dputs "setName=${setNo}"
1216     } else {
1217         z39.$setNo setName default
1218         dputs "setName=default"
1219     }
1220     if {$profile($target,queryRPN)} {
1221                 z39.$setNo queryType rpn
1222     } elseif {$profile($target,queryCCL)} {
1223                 z39.$setNo queryType ccl
1224     }
1225     dputs Setting
1226     dputs $recordSyntax
1227     if {![string compare $recordSyntax None]} {
1228         z39.$setNo preferredRecordSyntax {}
1229     } else {
1230         z39.$setNo preferredRecordSyntax $recordSyntax
1231     }
1232     if {![string compare $elementSetNames None]} {
1233         z39.$setNo elementSetNames {}
1234         z39.$setNo smallSetElementSetNames {}
1235         z39.$setNo mediumSetElementSetNames {}
1236     } else {
1237         z39.$setNo elementSetNames $elementSetNames
1238         z39.$setNo smallSetElementSetNames $elementSetNames
1239         z39.$setNo mediumSetElementSetNames $elementSetNames
1240     }
1241     z39 callback {search-response}
1242     z39.$setNo search $query
1243     show-status Searching 1 0
1244 }
1245
1246 # Procedure scan-copy {y entry}
1247 #  y       y-position of mouse pointer
1248 #  entry   a search entry in the top
1249 # Copies the term in the list nearest $y to the query entry specified
1250 # by $entry
1251 proc scan-copy {y entry} {
1252     set w .scan-window
1253     set no [$w.top.list nearest $y]
1254     dputs "no=$no"
1255     .lines.$entry.e delete 0 end
1256     .lines.$entry.e insert 0 [string range [$w.top.list get $no] 8 end]
1257 }
1258
1259 # Procedure scan-request
1260 # Performs a scan on term "0" with the current attributes in entry
1261 # specified by the global $curIndexEntry.
1262 proc scan-request {} {
1263     set w .scan-window
1264
1265     global profile hostid scanView scanTerm curIndexEntry queryButtonsFind \
1266                 queryInfoFind cancelFlag delayRequest
1267
1268     dputs "scan-request"
1269     if {$cancelFlag} {
1270         dputs "cancelFlag"
1271         show-status Scanning 1 0
1272         set delayRequest scan-request
1273         return
1274     }
1275     set delayRequest {} 
1276     set target $hostid
1277     set scanView 0
1278     set scanTerm {}
1279     set b [lindex $queryButtonsFind $curIndexEntry]
1280     set attr {}
1281     foreach a [lrange [lindex $queryInfoFind [lindex $b 1]] 1 end] {
1282         set attr "@attr $a $attr"
1283     }
1284     set title [lindex [lindex $queryInfoFind [lindex $b 1]] 0]
1285     ir-scan z39.scan z39
1286
1287     if {![winfo exists $w]} {
1288         toplevelG $w
1289         
1290         wm minsize $w 0 0
1291
1292         top-down-window $w
1293
1294         entry $w.top.entry -relief sunken 
1295         pack $w.top.entry -fill x -padx 4 -pady 2
1296         bind $w.top.entry <KeyRelease> [list scan-term-h $attr]
1297         listbox $w.top.list -yscrollcommand [list $w.top.scroll set] -font fixed 
1298         scrollbar $w.top.scroll -orient vertical -border 1
1299         pack $w.top.list -side left -fill both -expand yes
1300         pack $w.top.scroll -side right -fill y
1301         $w.top.scroll config -command [list $w.top.list yview]
1302         
1303         bottom-buttons $w [list {Close} [list destroy $w] \
1304                 {Up} [list scan-up $attr] \
1305                 {Down} [list scan-down $attr]] 0
1306         bind $w.top.list <Up> [list scan-up $attr]
1307         bind $w.top.list <Down> [list scan-down $attr]
1308         focus $w.top.entry
1309     }
1310     bind $w.top.list <Double-Button-1> [list scan-copy %y $curIndexEntry]
1311     wm title $w "Scan $title"
1312         
1313     z39 callback [list scan-response $attr 0 35]
1314     z39.scan numberOfTermsRequested 5
1315     z39.scan preferredPositionInResponse 1
1316     z39.scan scan "${attr} 0"
1317     
1318     show-status Scanning 1 0
1319 }
1320
1321 # Procedure scan-term-h {attr} 
1322 # attr    attribute specification
1323 # This procedure is called whenever a key is released in the entry in the
1324 # scan window (.scan-window). A scan is then initiated with the new contents
1325 # of the entry as the starting term.
1326 proc scan-term-h {attr} {
1327     global busy scanTerm
1328
1329     if {$busy} {
1330         return
1331     }
1332     set w .scan-window
1333     set nScanTerm [$w.top.entry get]
1334     if {$nScanTerm == $scanTerm} {
1335         return
1336     }
1337     set scanTerm $nScanTerm
1338     z39 callback [list scan-response $attr 0 35]
1339     z39.scan numberOfTermsRequested 5
1340     z39.scan preferredPositionInResponse 1
1341     dputs "${attr} \{${scanTerm}\}"
1342     if {![string length $scanTerm]} {
1343         z39.scan scan "${attr} 0"
1344     } else {
1345         z39.scan scan "${attr} \{${scanTerm}\}"
1346     }
1347     show-status Scanning 1 0
1348 }
1349
1350 # Procedure scan-response {attr start toget}
1351 #  attr   attribute specification
1352 #  start  position of first term in the response
1353 #  toget  number of total terms to get
1354 # This procedure handles all scan-responses. $start specifies the list
1355 # entry number of the first incoming term. The $toget indicates the total
1356 # number of terms to be retrieved from the target. The $toget may be
1357 # negative in which case, scan is performed 'backwards' (- $toget is
1358 # the total number of terms in this case). This procedure usually calls
1359 # itself several times in order to get small scan-term-list chunks.
1360 proc scan-response {attr start toget} {
1361     global cancelFlag delayRequest scanTerm scanView
1362
1363     set w .scan-window
1364     dputs "In scan-response"
1365     apduDump
1366     set m [z39.scan numberOfEntriesReturned]
1367     dputs $m
1368     dputs attr=$attr
1369     dputs start=$start
1370     dputs toget=$toget
1371
1372     if {![winfo exists .scan-window]} {
1373         if {$cancelFlag} {
1374             set cancelFlag 0
1375             dputs "Handling cancel"
1376             if {$delayRequest != ""} {
1377                 eval $delayRequest
1378             }
1379             return
1380         }
1381         show-status Ready 0 1
1382         return
1383     }
1384     set nScanTerm [$w.top.entry get]
1385     if {$nScanTerm != $scanTerm} {
1386         z39 callback [list scan-response $attr 0 35]
1387         z39.scan numberOfTermsRequested 5
1388         z39.scan preferredPositionInResponse 1
1389         set scanTerm $nScanTerm
1390         dputs "${attr} \{${scanTerm}\}"
1391         if {![string length $scanTerm]} {
1392             z39.scan scan "${attr} 0"
1393         } else {
1394             z39.scan scan "${attr} \{${scanTerm}\}"
1395         }
1396         show-status Scanning 1 0
1397         return
1398     }
1399     set status [z39.scan scanStatus]
1400     if {$status == 6} {
1401 #        tkerror "Scan fail"
1402         bgerror "Scan fail"
1403         show-status Ready 0 1
1404         set cancelFlag 0
1405         return
1406     }
1407     if {$toget < 0} {
1408         for {set i 0} {$i < $m} {incr i} {
1409             set term [lindex [z39.scan scanLine $i] 1]
1410             set nostr [format " %-6d" [lindex [z39.scan scanLine $i] 2]]
1411             $w.top.list insert $i "$nostr $term"
1412         }
1413         incr scanView $m
1414         $w.top.list yview $scanView
1415     } else {
1416         $w.top.list delete $start end
1417         for {set i 0} {$i < $m} {incr i} {
1418             set term [lindex [z39.scan scanLine $i] 1]
1419             set nostr [format " %-6d" [lindex [z39.scan scanLine $i] 2]]
1420             $w.top.list insert end "$nostr $term"
1421         }
1422     }
1423     if {$cancelFlag} {
1424         dputs "Handling cancel"
1425         set cancelFlag 0
1426         if {$delayRequest != ""} {
1427             eval $delayRequest
1428         }
1429         return
1430     }
1431     set delayRequest {}
1432     if {$toget > 0 && $m > 1 && $m < $toget} {
1433         set ntoget [expr $toget - $m + 1]
1434         dputs ntoget=$ntoget
1435         z39 callback [list scan-response $attr [expr $start + $m - 1] $ntoget]
1436         set q $term
1437         dputs "down continue: $q"
1438         if {$ntoget > 10} {
1439             z39.scan numberOfTermsRequested 10
1440         } else {
1441             z39.scan numberOfTermsRequested $ntoget
1442         }
1443         z39.scan preferredPositionInResponse 1
1444         dputs "${attr} \{$q\}"
1445         z39.scan scan "${attr} \{$q\}"
1446         return
1447     }
1448     if {$toget < 0 && $m > 1 && $m < [expr - $toget]} {
1449         set ntoget [expr - $toget - $m]
1450         dputs ntoget=$ntoget
1451         z39 callback [list scan-response $attr 0 -$ntoget]
1452         set q [string range [$w.top.list get 0] 8 end]
1453         dputs "up continue: $q"
1454         if {$ntoget > 10} {
1455             z39.scan numberOfTermsRequested 10
1456             z39.scan preferredPositionInResponse 11
1457         } else {
1458             z39.scan numberOfTermsRequested $ntoget
1459             z39.scan preferredPositionInResponse [incr ntoget]
1460         }
1461         dputs "${attr} \{$q\}"
1462         z39.scan scan "${attr} \{$q\}"
1463         return
1464     }
1465     show-status Ready 0 1
1466 }
1467
1468 # Procedure scan-down {attr}
1469 #  attr   attribute specification
1470 # This procedure is called when the user hits the Down button the scan
1471 # window. A new scan is initiated with a positive $toget passed to the
1472 # scan-response handler.
1473 proc scan-down {attr} {
1474     global scanView cancelFlag delayRequest
1475
1476     dputs {scan-down}
1477     if {$cancelFlag} {
1478         dputs "cancelFlag"
1479         show-status {Scanning down} 1 0
1480         set delayRequest [list scan-down $attr]
1481         return
1482     }
1483     set delayRequest {} 
1484
1485     set w .scan-window
1486     set scanView [expr $scanView + 5]
1487     set s [$w.top.list size]
1488     if {$scanView > $s} {
1489         z39 callback [list scan-response $attr [expr $s - 1] 25]
1490         set q [string range [$w.top.list get [expr $s - 1]] 8 end]
1491         dputs "down: $q"
1492         z39.scan numberOfTermsRequested 10
1493         z39.scan preferredPositionInResponse 1
1494         show-status Scanning 1 0
1495         dputs "${attr} \{$q\}"
1496         z39.scan scan "${attr} \{$q\}"
1497         return
1498     }
1499     $w.top.list yview $scanView
1500 }
1501
1502 # Procedure scan-up {attr}
1503 #  attr   attribute specification
1504 # This procedure is called when the user hits the Up button the scan
1505 # window. A new scan is initiated with a negative $toget passed to the
1506 # scan-response handler.
1507 proc scan-up {attr} {
1508     global scanView cancelFlag delayRequest
1509
1510     dputs {scan-up}
1511     if {$cancelFlag} {
1512         dputs "cancelFlag"
1513         show-status Scanning 1 0
1514         set delayRequest [list scan-up $attr]
1515         return
1516     }
1517     set delayRequest {} 
1518
1519     set w .scan-window
1520     set scanView [expr $scanView - 5]
1521     if {$scanView < 0} {
1522         z39 callback [list scan-response $attr 0 -25]
1523         set q [string range [$w.top.list get 0] 8 end]
1524         dputs "up: $q"
1525         z39.scan numberOfTermsRequested 10
1526         z39.scan preferredPositionInResponse 11
1527         show-status Scanning 1 0
1528         z39.scan scan "${attr} \{$q\}"
1529         return
1530     }
1531     $w.top.list yview $scanView
1532 }
1533
1534 # Procedure search-response
1535 # This procedure handles search-responses. If the search is successful
1536 # this procedure will try to retrieve a total of 20 records from the target;
1537 # however not more than $presentChunk records at a time. This procedure
1538 # affects the following globals:
1539 #   $setOffset        current record position offset
1540 #   $setMax           total number of records to be retrieved
1541 proc search-response {} {
1542     global setNo setOffset setMax cancelFlag busy delayRequest presentChunk
1543
1544     apduDump
1545     dputs "In search-response"
1546     if {$cancelFlag} {
1547         dputs "Handling cancel"
1548         set cancelFlag 0
1549         if {$delayRequest != ""} {
1550             eval $delayRequest
1551         }
1552         return
1553     }
1554     set setOffset 0
1555     set delayRequest {}
1556     init-title-lines
1557     set setMax [z39.$setNo resultCount]
1558     show-status Ready 0 1
1559     set status [z39.$setNo responseStatus]
1560     if {![string compare [lindex $status 0] NSD]} {
1561         z39.$setNo nextResultSetPosition 0
1562         set code [lindex $status 1]
1563         set msg [lindex $status 2]
1564         set addinfo [lindex $status 3]
1565 #        tkerror "NSD$code: $msg: $addinfo"
1566         bgerror "NSD$code: $msg: $addinfo"
1567         return
1568     }
1569     show-message "${setMax} hits"
1570     if {$setMax == 0} {
1571         return
1572     }
1573     set setOffset 1
1574     show-status Ready 0 1
1575     set l [format "%-4d %7d" $setNo $setMax]
1576     .top.rset.m add command -label $l -command [list recall-set $setNo]
1577     if {$setMax > 20} {
1578         set setMax 20
1579     }
1580     set no [z39.$setNo numberOfRecordsReturned]
1581     dputs "Returned $no records, setOffset $setOffset"
1582     add-title-lines $setNo $no $setOffset
1583     set setOffset [expr $setOffset + $no]
1584
1585     set toGet [expr $setMax - $setOffset + 1]
1586     if {$toGet > 0} {
1587         if {$setOffset == 1} {
1588             set toGet 1
1589         } elseif {$toGet > $presentChunk} {
1590             set toGet $presentChunk
1591         }
1592         z39 callback {present-response}
1593         z39.$setNo present $setOffset $toGet
1594         show-status Retrieving 1 0
1595     }
1596 }
1597
1598 # Procedure present-more {number}
1599 #  number      number of records to be retrieved
1600 # This procedure starts a present-request. The $number variable indicates
1601 # the total number of records to be retrieved. The global $presentChunk
1602 # specifies the number of records to be retrieved at a time. If $number
1603 # is the empty string all remaining records in the result set are 
1604 # retrieved.
1605 proc present-more {number} {
1606     global setNo setOffset setMax busy cancelFlag delayRequest presentChunk
1607
1608     dputs "present-more"
1609     if {$cancelFlag} {
1610         show-status Retrieving 1 0
1611         set delayRequest "present-more $number"
1612         return
1613     }
1614     set delayRequest {}
1615
1616     if {$setNo == 0} {
1617         dputs "setNo=$setNo"
1618         return
1619     }
1620     set setOffset [z39.$setNo nextResultSetPosition]
1621     dputs "setOffest=${setOffset}"
1622     dputs "setNo=${setNo}"
1623     set max [z39.$setNo resultCount]
1624     if {$max < $setOffset} {
1625         dputs "max=$max"
1626         dputs "setOffset=$setOffset"
1627         show-status Ready 0 1
1628         return
1629     }
1630     if {![string length $number]} {
1631         set setMax $max
1632     } else {
1633         incr setMax $number
1634         if {$setMax > $max} {
1635             set setMax $max
1636         }
1637     }
1638     z39 callback {present-response}
1639     
1640     set toGet [expr $setMax - $setOffset + 1]
1641     if {$toGet <= 0} {
1642         return
1643     }
1644     if {$toGet > $presentChunk} {
1645         set toGet $presentChunk
1646     } 
1647     z39.$setNo present $setOffset $toGet
1648     show-status Retrieving 1 0
1649 }
1650
1651 # Procedure init-title-lines 
1652 # Utility that cleans the main record window.
1653 proc init-title-lines {} {
1654     .data.record delete 1.0 end
1655 }
1656
1657 # Procedure recall-set {setno}
1658 #  setno    Set number to recall
1659 proc recall-set {setno} {
1660     add-title-lines $setno 10000 1
1661 }
1662
1663 # Procedure add-title-lines {setno no offset}
1664 #  setno    Set number
1665 #  no       Number of records
1666 #  offset   Starting offset
1667 # This procedure displays the records $offset .. $offset+$no-1 in result
1668 # set $setno in the main record window by using the display format in the
1669 # global $displayFormat
1670 proc add-title-lines {setno no offset} {
1671     global displayFormats displayFormat setNo busy
1672
1673     dputs "add-title-lines offset=${offset} no=${no}"
1674     if {$setno != -1} {
1675         set setNo $setno
1676     } else {
1677         set setno $setNo
1678     }
1679     if {$offset == 1} {
1680         .bot.a.set configure -text $setno
1681         .data.record delete 1.0 end
1682     }
1683     set ffunc [lindex $displayFormats $displayFormat]
1684     dputs "ffunc=$ffunc"
1685     set ffunc "display-$ffunc"
1686     for {set i 0} {$i < $no} {incr i} {
1687         set o [expr $i + $offset]
1688         set type [z39.$setno type $o]
1689         if {![string length $type]} {
1690             dputs "no more at $o"
1691             break
1692         }
1693         .data.record tag bind r$o <Any-Enter> {}
1694         .data.record tag bind r$o <Any-Leave> {}
1695         set insert0 [.data.record index insert]
1696         $ffunc $setno $o .data.record 1
1697         .data.record tag add r$o $insert0 insert
1698         .data.record tag bind r$o <1> [list popup-marc $setno $o 0 0]
1699         update idletasks
1700     }
1701     if {!$busy} {
1702         show-status Ready 0 1
1703     }
1704 }
1705
1706 # Procedure present-response
1707 # Present-response handler. The incoming records are displayed and a new
1708 # present request is performed until all records ($setMax) is returned
1709 # from the target.
1710 proc present-response {} {
1711     global setNo setOffset setMax cancelFlag delayRequest presentChunk
1712
1713     dputs "In present-response"
1714     apduDump
1715     set no [z39.$setNo numberOfRecordsReturned]
1716     dputs "Returned $no records, setOffset $setOffset"
1717     add-title-lines $setNo $no $setOffset
1718     set setOffset [expr $setOffset + $no]
1719     if {$cancelFlag} {
1720         dputs "Handling cancel"
1721         set cancelFlag 0
1722         if {$delayRequest != ""} {
1723             eval $delayRequest
1724         }
1725         return
1726     }
1727     set status [z39.$setNo responseStatus]
1728     if {![string compare [lindex $status 0] NSD]} {
1729         show-status Ready 0 1
1730         set code [lindex $status 1]
1731         set msg [lindex $status 2]
1732         set addinfo [lindex $status 3]
1733 #        tkerror "NSD$code: $msg: $addinfo"
1734         bgerror "NSD$code: $msg: $addinfo"
1735         return
1736     }
1737     if {$no > 0 && $setOffset <= $setMax} {
1738         dputs "present-request from ${setOffset}"
1739         set toGet [expr $setMax - $setOffset + 1]
1740         if {$toGet > $presentChunk} {
1741             set toGet $presentChunk
1742         }
1743         z39.$setNo present $setOffset $toGet
1744     } else {
1745         show-status Ready 0 1
1746     }
1747 }
1748
1749 # Procedure left-cursor {w}
1750 #  w    entry widget
1751 # Tries to move the cursor left in entry window $w
1752 proc left-cursor {w} {
1753     set i [$w index insert]
1754     if {$i > 0} {
1755         incr i -1
1756         $w icursor $i
1757     }
1758     dputs left
1759 }
1760
1761 # Procedure right-cursor {w}
1762 #  w    entry widget
1763 # Tries to move the cursor right in entry window $w
1764 proc right-cursor {w} {
1765     set i [$w index insert]
1766     incr i
1767     dputs right
1768     $w icursor $i
1769 }
1770
1771 # Procedure bind-fields {list returnAction escapeAction}
1772 #  list          list of entry widgets
1773 #  returnAction  return script
1774 #  escapeAction  escape script
1775 # Each widget in list are assigned bindings for <Tab>, <Left>, <Right>,
1776 # <Return> and <Escape>.
1777 proc bind-fields {list returnAction escapeAction} {
1778     set max [expr [llength $list]-1]
1779     for {set i 0} {$i < $max} {incr i} {
1780         bind [lindex $list $i] <Return> $returnAction
1781         bind [lindex $list $i] <Escape> $escapeAction
1782         bind [lindex $list $i] <Tab> [list focus [lindex $list [expr $i+1]]]
1783         bind [lindex $list $i] <Left> [list left-cursor [lindex $list $i]]
1784         bind [lindex $list $i] <Right> [list right-cursor [lindex $list $i]]
1785     }
1786     bind [lindex $list $i] <Return> $returnAction
1787     bind [lindex $list $i] <Escape> $escapeAction
1788     bind [lindex $list $i] <Tab>  [list focus [lindex $list 0]]
1789     bind [lindex $list $i] <Left> [list left-cursor [lindex $list $i]]
1790     bind [lindex $list $i] <Right> [list right-cursor [lindex $list $i]]
1791     focus [lindex $list 0]
1792 }
1793
1794 # Procedure entry-fields {parent list tlist returnAction escapeAction}
1795 #  list          list of frame widgets
1796 #  tlist         list of text to be used as lead of each entry
1797 #  returnAction  return script
1798 #  escapeAction  escape script
1799 # Makes label and entry widgets in each widget in $list.
1800 proc entry-fields {parent list tlist returnAction escapeAction} {
1801     set alist {}
1802     set i 0
1803     foreach field $list {
1804         set label ${parent}.${field}.label
1805         set entry ${parent}.${field}.entry
1806         label $label -text [lindex $tlist $i] -anchor e
1807         entry $entry -width 32 -relief sunken
1808         pack $label -side left
1809         pack $entry -side right
1810         lappend alist $entry
1811         incr i
1812     }
1813     bind-fields $alist $returnAction $escapeAction
1814 }
1815
1816 # Procedure define-target-dialog
1817 # Dialog that asks for new target to be defined.
1818 proc define-target-dialog {} {
1819     set w .target-define
1820
1821     toplevel $w
1822     place-force $w .
1823     top-down-window $w
1824     frame $w.top.target
1825     pack $w.top.target -side top -anchor e -pady 2 
1826     entry-fields $w.top {target} {{Target:}} \
1827             {define-target-action} {destroy .target-define}
1828     top-down-ok-cancel $w {define-target-action} 1
1829 }
1830
1831 # Procedure place-force {window parent}
1832 #  window      new top level widget
1833 #  parent      parent widget used as base
1834 # Sets geometry of $window relative to $parent window.
1835 proc place-force {window parent} {
1836     set g [wm geometry $parent]
1837     set p1 [string first + $g]
1838     set p2 [string last + $g]
1839     set x [expr 40+[string range $g [expr {$p1 + 1}] [expr {$p2 -1}]]]
1840     set y [expr 60+[string range $g [expr {$p2 + 1}] end]]
1841     wm geometry $window +${x}+${y}
1842 }
1843
1844 # Procedure add-database-action {target w}
1845 #  target      target to be defined
1846 #  w           top level widget for the target definition
1847 # Adds the contents of .database-select.top.database.entry to list of
1848 # databases.
1849 proc add-database-action {target w} {
1850     global profile
1851
1852     $w.top.databases.list insert end [.database-select.top.database.entry get]
1853     destroy .database-select
1854 }
1855
1856 # Procedure add-database {target wp}
1857 #  target      target to be defined
1858 #  wp          top level widget for the target definition
1859 # Makes a dialog in which the user enters new database
1860 proc add-database {target wp} {
1861     global profile
1862
1863     set w .database-select
1864     toplevel $w
1865     set oldFocus [focus]
1866     place-force $w $wp
1867     top-down-window $w
1868     frame $w.top.database
1869     pack $w.top.database -side top -anchor e -pady 2
1870         entry-fields $w.top {database} {{Database to add:}} \
1871             [list add-database-action $target $wp] {destroy .database-select}
1872
1873     top-down-ok-cancel $w [list add-database-action $target $wp] 1
1874     focus $oldFocus
1875 }
1876
1877
1878 # Procedure delete-database {target w}
1879 #  target     target to be defined
1880 #  w          top level widget for the target definition
1881 # Asks the user if he/she really wishes to delete a database and removes
1882 # the database from the database-list if requested.
1883 proc delete-database {target w} {
1884     global profile
1885
1886     set l {}
1887     foreach i [$w.top.databases.list curselection] {
1888         set b [$w.top.databases.list get $i]
1889         set l "$l $b"
1890     }
1891     set a [alert "Are you sure you want to remove the database(s)${l}?"]
1892     if {$a} {
1893         foreach i [lsort -decreasing \
1894                 [$w.top.databases.list curselection]] {
1895             $w.top.databases.list delete $i
1896         }
1897     }
1898 }
1899
1900 # Procedure advanced-setup {target b}
1901 #  target     target to be defined
1902 #  b          window number of target top level
1903 # Makes a dialog in which the user may modify/view advanced settings
1904 # of a target definition (profile).
1905 proc advanced-setup {target b} {
1906     global profile profileS
1907
1908     set w .advanced-setup-$b
1909     toplevelG $w
1910     wm title $w "Advanced setup $target"
1911     top-down-window $w
1912      if {![string length $target]} {
1913         set target Default
1914     }
1915     dputs target
1916     
1917     frame $w.top.largeSetLowerBound
1918     frame $w.top.smallSetUpperBound
1919     frame $w.top.mediumSetPresentNumber
1920     frame $w.top.presentChunk
1921     frame $w.top.maximumRecordSize
1922     frame $w.top.preferredMessageSize
1923
1924     pack $w.top.largeSetLowerBound $w.top.smallSetUpperBound \
1925             $w.top.mediumSetPresentNumber $w.top.presentChunk \
1926             $w.top.maximumRecordSize $w.top.preferredMessageSize \
1927             -side top -anchor e -pady 2
1928     
1929     entry-fields $w.top {largeSetLowerBound smallSetUpperBound \
1930             mediumSetPresentNumber presentChunk maximumRecordSize \
1931             preferredMessageSize} \
1932             {{Large Set Lower Bound:} {Small Set Upper Bound:} \
1933             {Medium Set Present Number:} {Present Chunk:} \
1934             {Maximum Record Size:} {Preferred Message Size:}} \
1935             [list advanced-setup-action $target $b] [list destroy $w]
1936
1937     $w.top.largeSetLowerBound.entry configure -textvariable \
1938         profileS($target,largeSetLowerBound)
1939     $w.top.smallSetUpperBound.entry configure -textvariable \
1940         profileS($target,smallSetUpperBound)
1941     $w.top.mediumSetPresentNumber.entry configure -textvariable \
1942         profileS($target,mediumSetPresentNumber)
1943     $w.top.presentChunk.entry configure -textvariable \
1944         profileS($target,presentChunk)
1945     $w.top.maximumRecordSize.entry configure -textvariable \
1946         profileS($target,maximumRecordSize)
1947     $w.top.preferredMessageSize.entry configure -textvariable \
1948         profileS($target,preferredMessageSize)
1949     
1950     bottom-buttons $w [list {Ok} [list advanced-setup-action $target $b] \
1951             {Cancel} [list destroy $w]] 0   
1952 }
1953
1954 # Procedure advanced-setup-action {target b}
1955 #  target     target to be defined
1956 #  b          window number of target top level
1957 # This procedure is called when the user hits Ok in the advanced target
1958 # setup dialog. The temporary result is stored in the $profileS - array.
1959 proc advanced-setup-action {target b} {
1960     set w .advanced-setup-$b
1961     global profileS
1962     
1963     set profileS($target,LSLB) [$w.top.largeSetLowerBound.entry get]
1964     set profileS($target,SSUB) [$w.top.smallSetUpperBound.entry get]
1965     set profileS($target,MSPN) [$w.top.mediumSetPresentNumber.entry get]
1966     set profileS($target,presentChunk) [$w.top.presentChunk.entry get]
1967     set profileS($target,MRS) [$w.top.maximumRecordSize.entry get]
1968     set profileS($target,PMS) [$w.top.preferredMessageSize.entry get]
1969
1970     dputs "advanced-setup-action"
1971     destroy $w
1972 }
1973
1974 # Procedure database-select-action
1975 # Called when the user commits a database select change. See procedure
1976 # database-select.
1977 proc database-select-action {} {
1978     set w .database-select.top
1979     set b {}
1980     foreach indx [$w.databases.list curselection] {
1981         lappend b [$w.databases.list get $indx]
1982     }
1983     if {$b != ""} {
1984         z39 databaseNames $b
1985     }
1986     destroy .database-select
1987 }
1988
1989 # Procedure database-select
1990 # Makes a dialog in which the user may select a database
1991 proc database-select {} {
1992     set w .database-select
1993     global profile hostid
1994
1995     toplevel $w
1996     set oldFocus [focus]
1997     place-force $w .
1998     top-down-window $w
1999
2000     frame $w.top.databases -relief ridge -border 2
2001     pack $w.top.databases -side left -pady 6 -padx 6 -expand yes -fill x
2002
2003     label $w.top.databases.label -text "List"
2004     listbox $w.top.databases.list -width 20 -height 6 \
2005             -yscrollcommand "$w.top.databases.scroll set"
2006     scrollbar $w.top.databases.scroll -orient vertical -border 1
2007     pack $w.top.databases.label -side top -fill x -padx 2 -pady 2
2008     pack $w.top.databases.list -side left -fill both -expand yes -padx 2 -pady 2
2009     pack $w.top.databases.scroll -side right -fill y -padx 2 -pady 2
2010     $w.top.databases.scroll config -command "$w.top.databases.list yview"
2011
2012     foreach b $profile($hostid,databases) {
2013         $w.top.databases.list insert end $b
2014     }
2015     top-down-ok-cancel $w {database-select-action} 1
2016     focus $oldFocus
2017 }
2018
2019 # Procedure cascase-dblist-select
2020 proc cascade-dblist-select {target db} {
2021     show-target $target $db
2022     z39 databaseNames $db
2023     ready-response-actions $target $db
2024 }
2025
2026 # Procedure cascade-dblist 
2027 # Makes the Service/database list with proper databases for the target
2028 proc cascade-dblist {target base} {
2029     global profile
2030
2031     set w .top.service.m.dblist
2032     $w delete 0 200
2033     if {[info exists profile($target,databases)]} {
2034                 foreach db $profile($target,databases) {
2035                     $w add command -label $db \
2036                         -command [list cascade-dblist-select $target $db]
2037                 }
2038     }
2039 }
2040
2041 # Procedure cascade-target-list
2042 # Makes all target/databases available in the Target|Connect
2043 # menu as well as all targets in the Target|Setup menu.
2044 # This procedure is called whenever target definitions occur.
2045 proc cascade-target-list {} {
2046     global profile
2047     
2048     foreach sub [winfo children .top.target.m.clist] {
2049         destroy $sub
2050     }
2051     .top.target.m.clist delete 0 last
2052     foreach nn [lsort [array names profile *,host]] {
2053                 if {[string length $profile($nn)]} {
2054                     set ll [expr {[string length $nn] - 6}]
2055                     set n [string range $nn 0 $ll]
2056                     
2057                     set nl $profile($n,windowNumber)
2058                     if {[info exists profile($n,databases)]} {
2059                                 set ndb [llength $profile($n,databases)]
2060                     } else {
2061                                 set ndb 0
2062                     }
2063                     if {$ndb > 1} {
2064                                 .top.target.m.clist add cascade -label $n \
2065                                     -menu .top.target.m.clist.$nl
2066                                 irmenu .top.target.m.clist.$nl
2067                                 foreach b $profile($n,databases) {
2068                                     .top.target.m.clist.$nl add command -label $b \
2069                                         -command [list reopen-target $n $b]
2070                                 }
2071                     } elseif {$ndb == 1} {
2072                                 .top.target.m.clist add command -label $n -command \
2073                                 [list reopen-target $n [lindex $profile($n,databases) 0]]
2074                     } else {
2075                                 .top.target.m.clist add command -label $n -command \
2076                                 [list reopen-target $n {}]
2077                     }
2078                 }
2079     }
2080     .top.target.m.slist delete 0 last
2081     foreach nn [lsort [array names profile *,host]] {
2082                 set ll [expr {[string length $nn] - 6}]
2083                 set n [string range $nn 0 $ll]
2084             .top.target.m.slist add command -label $n -command [list protocol-setup $n]
2085     }
2086 }
2087
2088 # Procedure query-select {i}
2089 #  i       Query type number (integer)
2090 # This procedure is called when the user selects a Query type. The current
2091 # query type information given by the globals $queryButtonsFind and
2092 # $queryInfoFind are affected by this operation.
2093 proc query-select {i} {
2094     global queryButtonsFind queryInfoFind queryButtons queryInfo queryAuto queryAutoOld hostid currentDb profile
2095     
2096     if {$queryAutoOld == 1 && $queryAuto == 0} {
2097         set queryAutoOld $queryAuto
2098         return
2099     }
2100     if {$queryAutoOld == 0 && $queryAuto == 1 && [info exists profile($hostid,AttributeDetails,$currentDb,Bib1Use)] == 0} {
2101         set queryAutoOld $queryAuto
2102         return
2103     }
2104     set queryInfoFind [lindex $queryInfo $i]
2105     set queryButtonsFind [lindex $queryButtons $i]
2106     index-lines .lines 1 $queryButtonsFind $queryInfoFind activate-index
2107 }
2108
2109 # Procedure query-new-action 
2110 # Commits a new query type definition by extending the globals
2111 # $queryTypes, $queryButtons and $queryInfo.
2112 proc query-new-action {} {
2113     global queryTypes queryButtons queryInfo settingsChanged
2114
2115     set settingsChanged 1
2116     lappend queryTypes [.query-new.top.index.entry get]
2117     lappend queryButtons {}
2118     lappend queryInfo {}
2119
2120     destroy .query-new
2121     cascade-query-list
2122 }
2123
2124 # Procedure query-new
2125 # Makes a dialog in which the user is requested to enter the name of a
2126 # new query type.
2127 proc query-new {} {
2128     set w .query-new
2129
2130     toplevel $w
2131     set oldFocus [focus]
2132     place-force $w .
2133     top-down-window $w
2134     frame $w.top.index
2135     pack $w.top.index -side top -anchor e -pady 2 
2136     entry-fields $w.top index {{Query Name:}} \
2137             query-new-action {destroy .query-new}
2138     top-down-ok-cancel $w query-new-action 1
2139     focus $oldFocus
2140 }
2141
2142 # Procedure query-delete-action {queryNo}
2143 #  queryNo     query type number (integer)
2144 # Procedure that deletes the query type specified by $queryNo.
2145 proc query-delete-action {queryNo} {
2146     global queryTypes queryButtons queryInfo settingsChanged
2147
2148     set settingsChanged 1
2149
2150     set queryTypes [lreplace $queryTypes $queryNo $queryNo]
2151     set queryButtons [lreplace $queryButtons $queryNo $queryNo]
2152     set queryInfo [lreplace $queryInfo $queryNo $queryNo]
2153     destroy .query-delete
2154     cascade-query-list
2155 }
2156
2157 # Procedure query-delete {queryNo}
2158 #  queryNo     query type number (integer)
2159 # Asks if the user really want to delete a given query type; calls
2160 # query-delete-action if 'yes'.
2161 proc query-delete {queryNo} {
2162     global queryTypes
2163
2164     set w .query-delete
2165
2166     toplevel $w
2167     place-force $w .
2168     top-down-window $w
2169     set n [lindex $queryTypes $queryNo]
2170
2171     label $w.top.warning -bitmap warning
2172     message $w.top.quest -text "Are you sure you want to delete the \
2173                 query type $n ?"  -aspect 300
2174     pack $w.top.warning $w.top.quest -side left -expand yes -padx 10 -pady 5
2175     bottom-buttons $w [list {Ok} [list query-delete-action $queryNo] \
2176                 {Cancel} [list destroy $w]] 1
2177 }
2178
2179 # Procedure cascade-query-list
2180 # Updates the entries below Options|Query to list all query types.
2181 proc cascade-query-list {} {
2182     global queryTypes hostid queryAuto
2183     set w .top.options.m.query
2184     set i 0
2185     $w.clist delete 0 last
2186     foreach n $queryTypes {
2187         if {$n == "Auto"} {
2188                 $w.clist add check -label $n -variable queryAuto -command [list query-select $i]
2189         } else {
2190                 $w.clist add command -label $n -command [list query-select $i]      
2191         }
2192         incr i
2193     }
2194     set i 0
2195     $w.slist delete 0 last
2196     foreach n $queryTypes {
2197         if {$n == "Auto"} {
2198                 if {$hostid == "Default"} {
2199                         $w.slist add command -label $n -state disabled -command [list query-setup $i]
2200                 } else {
2201                         $w.slist add command -label $n -command [list query-setup $i]
2202                 }
2203         } else {
2204                 $w.slist add command -label $n -command [list query-setup $i]
2205         }
2206         incr i
2207     }
2208     set i 0
2209     $w.dlist delete 0 last
2210     foreach n $queryTypes {
2211         $w.dlist add command -label $n -command [list query-delete $i]
2212         incr i
2213     }
2214 }
2215
2216 # Procedure save-geometry
2217 # This procedure saves the per-user related settings in ~/.clientrc.tcl.
2218 # The geometry information stored in the global array $windowGeometry is
2219 # saved. Also a few other user settings, such as current display format, are
2220 # saved.
2221 proc save-geometry {} {
2222     global windowGeometry hotTargets textWrap displayFormat popupMarcdf \
2223                 recordSyntax elementSetNames hostid
2224
2225     set windowGeometry(.) [wm geometry .]
2226
2227     if {[catch {set f [open ~/.clientrc.tcl w]}]} {
2228         return
2229     } 
2230     if {$hostid != "Default"} {
2231         puts $f "set hostid [list $hostid]"
2232         set b [z39 databaseNames]
2233         puts $f "set hostbase [list $b]"
2234     }
2235     puts $f "set hotTargets [list $hotTargets]"
2236     puts $f "set textWrap $textWrap"
2237     puts $f "set displayFormat $displayFormat"
2238     puts $f "set popupMarcdf $popupMarcdf"
2239     puts $f "set recordSyntax $recordSyntax"
2240     puts $f "set elementSetNames $elementSetNames"
2241     foreach n [array names windowGeometry] {
2242         puts -nonewline $f "set [list windowGeometry($n)] "
2243         puts $f [list $windowGeometry($n)]
2244     }
2245     close $f
2246 }
2247
2248 # Procedure save-settings
2249 # This procedure saves the per-host related settings irtdb.tcl which
2250 # is normally kept in the directory /usr/local/lib/irtcl.
2251 # All query types and target defintion profiles are saved.
2252 proc save-settings {} {
2253     global profile libdir settingsChanged queryTypes queryButtons queryInfo queryAuto
2254
2255     if {[file writable [file join $libdir irtdb.tcl]]} {
2256         set f [open [file join $libdir irtdb.tcl] w]
2257     } else {
2258         set f [open "irtdb.tcl" w]
2259     }
2260     puts $f "# Setup file"
2261     foreach n [lsort [array names profile]] {
2262         puts $f "set [list profile($n)] [list $profile($n)]"
2263     }
2264     puts $f "set queryTypes [list $queryTypes]"
2265     puts $f "set queryButtons [list $queryButtons]"
2266     puts $f "set queryInfo [list $queryInfo]"
2267     puts $f "set queryAuto [list $queryAuto]"
2268     close $f
2269     set settingsChanged 0
2270 }
2271
2272 # Procedure alert {ask}
2273 #  ask    prompt string
2274 # Makes a grabbed dialog in which the user is requested to answer
2275 # "Ok" or "Cancel". This procedure returns 1 if the user hits "Ok"; 0
2276 # otherwise.
2277 proc alert {ask} {
2278     set w .alert
2279
2280     global alertAnswer font
2281
2282     toplevel $w
2283     set oldFocus [focus]
2284     place-force $w .
2285     top-down-window $w
2286
2287     label $w.top.warning -bitmap warning
2288     message $w.top.message -text $ask -aspect 300 -font $font(b,normal)
2289     pack $w.top.warning $w.top.message -side left -pady 5 -padx 10 -expand yes
2290   
2291     set alertAnswer 0
2292     top-down-ok-cancel $w {alert-action} 1
2293     focus $oldFocus
2294     return $alertAnswer
2295 }
2296
2297 # Procedure alert-action
2298 # Called when the user hits "Ok" in the .alert-window.
2299 proc alert-action {} {
2300     global alertAnswer
2301     set alertAnswer 1
2302     destroy .alert
2303 }
2304
2305 # Procedure exit-action
2306 # This procedure is called if the user exists the application
2307 proc exit-action {} {
2308     global settingsChanged
2309
2310     if {$settingsChanged} {
2311         save-settings
2312     }
2313     save-geometry
2314     exit 0
2315 }
2316
2317 # Procedure listbuttonaction {w name h user i}
2318 #  w       menubutton widget
2319 #  name    name information
2320 #  h       handler to be invoked
2321 #  user    user information to be passed to handler $h
2322 #  i       index passed as second argument to handler $h
2323 # Utility function to emulate a listbutton. Called when the user
2324 # Modifies the listbutton. See procedure listbuttonx.
2325 proc listbuttonaction {w name h user i} {
2326     $w configure -text [lindex $name 0]
2327     $h [lindex $name 1] $user $i
2328 }
2329
2330 # Procedure listbuttonx {button no names handle user}
2331 #  button  menubutton widget
2332 #  no      initial value index (integer)
2333 #  names   list of name entries. The first entry in each name
2334 #          entry is the actual name
2335 #  handle  user function to be called when the listbutton changes
2336 #          its value
2337 #  user    user argument to the $handle function
2338 # Makes an extended listbutton.
2339 proc listbuttonx {button no names handle user} {
2340         set width 10
2341         foreach name $names {
2342                 set buttonName [lindex $name 0]
2343                 if {[string length $buttonName] > $width} {
2344                         set width [string length $buttonName]
2345                 }
2346         } 
2347     if {[winfo exists $button]} {
2348         $button configure -width $width -text [lindex [lindex $names $no] 0]
2349         ${button}.m delete 0 last
2350     } else {
2351         menubutton $button -text [lindex [lindex $names $no] 0] \
2352                         -width $width -menu ${button}.m -relief raised -border 1
2353         irmenu ${button}.m
2354         ${button}.m configure -tearoff off
2355     }
2356     set i 0
2357     foreach name $names {
2358         ${button}.m add command -label [lindex $name 0] \
2359                         -command [list listbuttonaction ${button} $name $handle $user $i]
2360         incr i
2361     }
2362 }
2363
2364 # Procedure listbutton {button no names}
2365 #  button  menubutton widget
2366 #  no      initial value index (integer)
2367 #  names   list of possible values.
2368 # Makes a listbutton. The functionality is emulated by the use menubutton-
2369 # and menu widgets.
2370 proc listbutton {button no names} {
2371     menubutton $button -text [lindex $names $no] -width 10 -menu ${button}.m \
2372             -relief raised -border 1
2373     irmenu ${button}.m
2374     ${button}.m configure -tearoff off
2375     foreach name $names {
2376         ${button}.m add command -label $name \
2377                 -command [list ${button} configure -text $name]
2378     }
2379 }
2380
2381 # Procedure listbuttonv-action {button var names i}
2382 #  button   menubutton widget
2383 #  var      global variable to be affected
2384 #  names    list of possible names and values
2385 # This procedure is called when the user alters a menu created by the
2386 # listbuttonv procedure. The global variable $var is updated.
2387 proc listbuttonv-action {button var names i} {
2388     global $var
2389
2390     set $var [lindex $names [expr {$i+1}]]
2391     $button configure -text [lindex $names $i]
2392 }
2393
2394 # Procedure listbuttonv {button var names}
2395 #  button   menubutton widget
2396 #  var      global variable to be affected
2397 #  names    List of name/value pairs, i.e. {n1 v1 n2 v2 ...}.
2398 # This procedure emulates a listbutton by means of menu/menubutton widgets.
2399 # The global variable $var is automatically updated and set to one of the
2400 # values v1, v2, ...
2401 proc listbuttonv {button var names} {
2402     global $var
2403
2404     set n "-"
2405     set val [set $var]
2406     set l [llength $names]
2407     for {set i 1} {$i < $l} {incr i 2} {
2408         if {$val == [lindex $names $i]} {
2409             incr i -1
2410             set n [lindex $names $i]
2411             break
2412         }
2413     }
2414     if {[winfo exists $button]} {
2415         $button configure -text $n
2416         return
2417     }
2418     menubutton $button -text $n -menu ${button}.m -relief raised -border 1
2419     irmenu ${button}.m
2420     ${button}.m configure -tearoff off
2421     for {set i 0} {$i < $l} {incr i 2} {
2422         ${button}.m add command -label [lindex $names $i] \
2423                 -command [list listbuttonv-action $button $var $names $i]
2424     }
2425 }
2426
2427 # Procedure query-add-index-action {queryNo}
2428 #  queryNo       query type number (integer)
2429 # Handler that makes a new query index.
2430 proc query-add-index-action {queryNo} {
2431     set w .query-setup
2432
2433     global queryInfoTmp queryButtonsTmp
2434
2435     set newI [.query-add-index.top.index.entry get]
2436     lappend queryInfoTmp [list $newI {}]
2437     $w.top.index.list insert end $newI
2438     destroy .query-add-index
2439     index-lines $w.top.lines 0 $queryButtonsTmp $queryInfoTmp activate-e-index
2440 }
2441
2442 # Procedure query-add-line
2443 #  queryNo      query type number (integer)
2444 # Handler that adds new query line.
2445 proc query-add-line {queryNo} {
2446     set w .query-setup
2447
2448     global queryInfoTmp queryButtonsTmp
2449
2450     lappend queryButtonsTmp {I 0}
2451
2452     index-lines $w.top.lines 0 $queryButtonsTmp $queryInfoTmp activate-e-index
2453 }
2454
2455 # Procedure query-del-line
2456 #  queryNo      query type number (integer)
2457 # Handler that removes query line.
2458 proc query-del-line {queryNo} {
2459     set w .query-setup
2460
2461     global queryInfoTmp queryButtonsTmp
2462
2463     set l [llength $queryButtonsTmp]
2464     if {$l <= 0} {
2465         return
2466     }
2467     incr l -1
2468     set queryButtonsTmp [lreplace $queryButtonsTmp $l $l]
2469     index-lines $w.top.lines 0 $queryButtonsTmp $queryInfoTmp activate-e-index
2470 }
2471
2472 # Procedure query-add-index
2473 #  queryNo      query type number (integer)
2474 # Handler that adds new query index.
2475 proc query-add-index {queryNo} {
2476     set w .query-add-index
2477
2478     toplevel $w
2479     set oldFocus [focus]
2480     place-force $w .query-setup
2481     top-down-window $w
2482     frame $w.top.index
2483     pack $w.top.index -side top -anchor e -pady 2 
2484     entry-fields $w.top {index} {{Index Name:}} \
2485             [list query-add-index-action $queryNo] [list destroy $w]
2486     top-down-ok-cancel $w [list query-add-index-action $queryNo] 1
2487     focus $oldFocus
2488 }
2489
2490 # Procedure query-setup-action
2491 #  queryNo      query type number (integer)
2492 # Handler that updates the query information database stored in the
2493 # globals $queryInfo and $queryButtons. This procedure is executed when
2494 # the user commits the query setup changes by pressing button "Ok".
2495 proc query-setup-action {queryNo} {
2496     global queryButtons queryInfo queryButtonsTmp queryInfoTmp queryButtonsFind \
2497                 queryInfoFind settingsChanged hostid currentDb profile
2498     set settingsChanged 1
2499     set queryInfo [lreplace $queryInfo $queryNo $queryNo $queryInfoTmp]
2500     set queryButtons [lreplace $queryButtons $queryNo $queryNo $queryButtonsTmp]
2501     if {[info exists profile($hostid,AttributeDetails,$currentDb,Bib1Use)]} {
2502         set profile($hostid,queryButtons,$currentDb) $queryButtonsTmp
2503     }
2504     set queryInfoFind $queryInfoTmp
2505     set queryButtonsFind $queryButtonsTmp
2506     destroy .query-setup
2507     index-lines .lines 1 $queryButtonsFind $queryInfoFind activate-index
2508 }
2509
2510 #proc changeQueryButtons {target base}
2511 #target         target name
2512 #base           database name
2513 #Substitutes the third element (the Auto element) in queryButtons with 
2514 #profile(target,queryButtons,base). The third element in queryInfo is also substituted with
2515 #profile(target,AttributeDetails,base,Bib1Use)
2516 proc changeQueryButtons {target base} {
2517         source bib-1.tcl
2518         global profile queryButtons queryInfo
2519         if {[info exists profile($target,queryButtons,$base)]} {
2520                 set queryButtons [lreplace $queryButtons 2 2 $profile($target,queryButtons,$base)]
2521                 foreach tag $profile($target,AttributeDetails,$base,Bib1Use) {
2522                         if {$tag < 1037} {
2523                                 lappend tempList [list $bib1($tag) 1=$tag]
2524                         }
2525                 }
2526                 set queryInfo [lreplace $queryInfo 2 2 $tempList]
2527         }
2528 }
2529
2530 # Procedure activate-e-index {value no i}
2531 #   value   menu name
2532 #   no      query index number
2533 #   i       menu index (integer)
2534 # Procedure called when listbutton is activated in the query type edit
2535 # window. The global $queryButtonsTmp is updated in this operation.
2536 proc activate-e-index {value no i} {
2537     global queryButtonsTmp queryIndexTmp
2538     set queryButtonsTmp [lreplace $queryButtonsTmp $no $no [list I $i]]
2539     dputs $queryButtonsTmp
2540     set queryIndexTmp $i
2541 }
2542
2543 # Procedure activate-index {value no i}
2544 #   value   menu name
2545 #   no      query index number
2546 #   i       menu index (integer)
2547 # Procedure called when listbutton is activated in the main query 
2548 # window. The global $queryButtonsFind is updated in this operation.
2549 proc activate-index {value no i} {
2550     global queryButtonsFind
2551
2552     set queryButtonsFind [lreplace $queryButtonsFind $no $no [list I $i]]
2553     dputs "queryButtonsFind $queryButtonsFind"
2554 }
2555
2556 # Procedure update-attr
2557 # This procedure creates listbuttons for all bib-1 attributes except
2558 # the use-attribute in the .index-setup window.
2559 # The globals $relationTmpValue, $positionTmpValue, $structureTmpValue,
2560 # $truncationTmpValue and $completenessTmpValue are maintainted by the
2561 # listbuttons.
2562 proc update-attr {} {
2563     set w .index-setup
2564     listbuttonv $w.top.relation.b relationTmpValue\
2565             {{None} 0 {Less than} 1 {Greater than or equal} 2 {Equal} 3 \
2566             {Greater than or equal} 4 {Greater than} 5 {Not equal} 6 \
2567             {Phonetic} 100 {Stem} 101 {Relevance} 102 {AlwaysMatches} 103}
2568     listbuttonv $w.top.position.b positionTmpValue {{None} 0 \
2569             {First in field} 1 {First in subfield} 2 {Any position in field} 3}
2570     listbuttonv $w.top.structure.b structureTmpValue {{None} 0 {Phrase} 1 \
2571             {Word} 2 {Key} 3 {Year} 4 {Date (norm)} 5 {Word list}  6 \
2572             {Date (un-norm)} 100 {Name (norm)} 101 {Date (un-norm)} 102 \
2573             {Structure} 103 {urx} 104 {free-form} 105 {doc-text} 106 \
2574             {local-number} 107 {string} 108 {numeric string} 109}
2575     listbuttonv $w.top.truncation.b truncationTmpValue {{Auto} 0 {Right} 1 \
2576             {Left} 2 {Left and right} 3 {No truncation} 100 \
2577             {Process #} 101 {Re-1} 102 {Re-2} 103}
2578     listbuttonv $w.top.completeness.b completenessTmpValue {{None} 0 \
2579             {Incomplete subfield} 1 {Complete subfield} 2 {Complete field} 3}
2580 }
2581
2582 # Procedure use-attr {init}
2583 #  init      init flag
2584 # This procedure creates a listbox with several Bib-1 use attributes.
2585 # If $init is 1 the listbox is created with the attributes. If $init
2586 # is 0 the current selection of the listbox is read and the global
2587 # $useTmpValue is set to the current use-value.
2588 proc use-attr {init} {
2589     set attr {
2590         {None}                           0
2591         {Personal name}                  1 
2592         {Corporate name}                 2 
2593         {Conference name}                3 
2594         {Title}                          4 
2595         {Title-series}                   5 
2596         {Title-uniform}                  6 
2597         {ISBN}                           7 
2598         {ISSN}                           8 
2599         {LC card number}                 9 
2600         {BNB card number}                10
2601         {BGF(sic) number}                11 
2602         {Local number}                   12 
2603         {Dewey classification}           13 
2604         {UDC classification}             14 
2605         {Bliss classification}           15 
2606         {LC call number}                 16 
2607         {NLM call number}                17 
2608         {NAL call number}                18 
2609         {MOS call number}                19 
2610         {Local classification}           20 
2611         {Subject heading}                21 
2612         {Subject-RAMEAU}                 22 
2613         {BDI-index-subject}              23 
2614         {INSPEC-subject}                 24 
2615         {MESH-subject}                   25 
2616         {PA-subject}                     26 
2617         {LC-subject-heading}             27 
2618         {RVM-subject-heading}            28 
2619         {Local subject index}            29 
2620         {Date}                           30 
2621         {Date of publication}            31 
2622         {Date of acquisition}            32 
2623         {Title-key}                      33 
2624         {Title-collective}               34 
2625         {Title-parallel}                 35 
2626         {Title-cover}                    36 
2627         {Title-added-title-page}         37 
2628         {Title-caption}                  38 
2629         {Title-running}                  39 
2630         {Title-spine}                    40 
2631         {Title-other-variant}            41 
2632         {Title-former}                   42 
2633         {Title-abbreviated}              43 
2634         {Title-expanded}                 44 
2635         {Subject-PRECIS}                 45 
2636         {Subject-RSWK}                   46 
2637         {Subject-subdivision}            47 
2638         {Number-natl-bibliography}       48 
2639         {Number-legal-deposit}           49 
2640         {Number-govt-publication}        50 
2641         {Number-publisher-for-music}     51 
2642         {Number-DB}                      52 
2643         {Number-local-call}              53 
2644         {Code-language}                  54 
2645         {Code-geographic-area}           55 
2646         {Code-institution}               56 
2647         {Name and title}                 57 
2648         {Name-geographic}                58 
2649         {Place-publication}              59 
2650         {CODEN}                          60 
2651         {Microform-generation}           61 
2652         {Abstract}                       62 
2653         {Note}                           63 
2654         {Author-title}                 1000 
2655         {Record type}                  1001 
2656         {Name}                         1002 
2657         {Author}                       1003 
2658         {Author-name-personal}         1004 
2659         {Author-name-corporate}        1005 
2660         {Author-name-conference}       1006 
2661         {Identifier-standard}          1007 
2662         {Subject-LC-children's}        1008 
2663         {Subject-name-personal}        1009 
2664         {Body of text}                 1010 
2665         {Date/time added to database}  1011 
2666         {Date/time last modified}      1012 
2667         {Authority/format identifier}  1013 
2668         {Concept-text}                 1014 
2669         {Concept-reference}            1015 
2670         {Any}                          1016 
2671         {Server choice}                1017 
2672         {Publisher}                    1018 
2673         {Record source}                1019 
2674         {Editor}                       1020 
2675         {Bib-level}                    1021 
2676         {Geographic class}             1022 
2677         {Indexed by}                   1023 
2678         {Map scale}                    1024 
2679         {Music key}                    1025 
2680         {Related periodical}           1026 
2681         {Report number}                1027 
2682         {Stock number}                 1028 
2683         {Thematic number}              1030 
2684         {Material type}                1031 
2685         {Doc ID}                       1032 
2686         {Host item}                    1033 
2687         {Content type}                 1034 
2688         {Anywhere}                     1035 
2689     }
2690     set w .index-setup
2691     global useTmpValue
2692     set l [llength $attr]
2693
2694     if {$init} {
2695         set s 0
2696         set lno 0
2697         for {set i 0} {$i < $l} {incr i} {
2698             $w.top.use.list insert end [lindex $attr $i]
2699             incr i
2700             if {$useTmpValue == [lindex $attr $i]} {
2701                 set s $lno
2702             }
2703             incr lno
2704         }
2705         $w.top.use.list selection clear 0 end
2706         $w.top.use.list selection set $s $s
2707         incr s -3
2708         if {$s < 0} {
2709             set s 0
2710         }
2711         $w.top.use.list yview $s
2712     } else {
2713         set lno [lindex [$w.top.use.list curselection] 0]
2714         set i [expr {$lno+$lno+1}]
2715         set useTmpValue [lindex $attr $i]
2716         dputs "useTmpValue=$useTmpValue"
2717     }
2718 }
2719
2720 # Procedure index-setup-action {oldAttr queryNo indexNo}
2721 #  oldAttr     original attributes (?)
2722 #  queryNo     query number
2723 #  indexNo     index number
2724 # Commits setup of a query index. The mapping from the index to 
2725 # the Bib-1 attributes are handled by this function.
2726 proc index-setup-action {oldAttr queryNo indexNo} {
2727     set attr [lindex $oldAttr 0]
2728
2729     global useTmpValue relationTmpValue structureTmpValue truncationTmpValue \
2730         completenessTmpValue positionTmpValue queryInfoTmp
2731
2732     use-attr 0
2733
2734     dputs "index-setup-action"
2735     dputs "queryNo $queryNo"
2736     dputs "indexNo $indexNo"
2737     if {$useTmpValue > 0} {
2738         lappend attr "1=$useTmpValue"
2739     }
2740     if {$relationTmpValue > 0} {
2741         lappend attr "2=$relationTmpValue"
2742     }
2743     if {$positionTmpValue > 0} {
2744         lappend attr "3=$positionTmpValue"
2745     }
2746     if {$structureTmpValue > 0} {
2747         lappend attr "4=$structureTmpValue"
2748     }
2749     if {$truncationTmpValue > 0} {
2750         lappend attr "5=$truncationTmpValue"
2751     }
2752     if {$completenessTmpValue > 0} {
2753         lappend attr "6=$completenessTmpValue"
2754     }
2755     dputs "new attr $attr"
2756     set queryInfoTmp [lreplace $queryInfoTmp $indexNo $indexNo $attr]
2757     destroy .index-setup
2758 }
2759
2760 # Procedure index-setup {attr queryNo indexNo}
2761 #  attr        original attributes
2762 #  queryNo     query number
2763 #  indexNo     index number
2764 # Makes a window with settings of a given query index which the user
2765 # may inspect/modify.
2766 proc index-setup {attr queryNo indexNo} {
2767     set w .index-setup
2768
2769     global relationTmpValue structureTmpValue truncationTmpValue \
2770                 completenessTmpValue positionTmpValue useTmpValue
2771     set relationTmpValue 0
2772     set truncationTmpValue 0
2773     set structureTmpValue 0
2774     set positionTmpValue 0
2775     set completenessTmpValue 0
2776     set useTmpValue 0
2777
2778     catch {destroy $w}
2779     toplevelG $w
2780
2781     set n [lindex $attr 0]
2782     wm title $w "Index setup $n"
2783
2784     top-down-window $w
2785
2786     set len [llength $attr]
2787     for {set i 1} {$i < $len} {incr i} {
2788         set q [lindex $attr $i]
2789         set l [string first = $q]
2790         if {$l > 0} {
2791             set t [string range $q 0 [expr {$l - 1}]]
2792             set v [string range $q [expr {$l + 1}] end]
2793             switch $t {
2794                 1
2795                 { set useTmpValue $v }
2796                 2
2797                 { set relationTmpValue $v }
2798                 3
2799                 { set positionTmpValue $v }
2800                 4
2801                 { set structureTmpValue $v }
2802                 5
2803                 { set truncationTmpValue $v }
2804                 6
2805                 { set completenessTmpValue $v }
2806             }
2807         }
2808     }
2809
2810     frame $w.top.use -relief ridge -border 2
2811     frame $w.top.relation -relief ridge -border 2
2812     frame $w.top.position -relief ridge -border 2
2813     frame $w.top.structure -relief ridge -border 2
2814     frame $w.top.truncation -relief ridge -border 2
2815     frame $w.top.completeness -relief ridge -border 2
2816
2817     update-attr
2818
2819     # Use Attributes
2820
2821     pack $w.top.use -side left -pady 6 -padx 6 -fill y
2822
2823     label $w.top.use.label -text "Use"
2824         listbox $w.top.use.list -width 26 -yscrollcommand "$w.top.use.scroll set"
2825     scrollbar $w.top.use.scroll -orient vertical -border 1
2826     pack $w.top.use.label -side top -fill x -padx 2 -pady 2
2827     pack $w.top.use.list -side left -fill both -expand yes -padx 2 -pady 2
2828     pack $w.top.use.scroll -side right -fill y -padx 2 -pady 2
2829     $w.top.use.scroll config -command "$w.top.use.list yview"
2830
2831     use-attr 1
2832
2833     # Relation Attributes
2834
2835     pack $w.top.relation -pady 6 -padx 6 -side top
2836     label $w.top.relation.label -text "Relation" -width 18
2837     
2838     pack $w.top.relation.label $w.top.relation.b -fill x 
2839
2840     # Position Attributes
2841
2842     pack $w.top.position -pady 6 -padx 6 -side top
2843     label $w.top.position.label -text "Position" -width 18
2844
2845     pack $w.top.position.label $w.top.position.b -fill x
2846
2847     # Structure Attributes
2848
2849     pack $w.top.structure -pady 6 -padx 6 -side top
2850     label $w.top.structure.label -text "Structure" -width 18
2851
2852     pack $w.top.structure.label $w.top.structure.b -fill x
2853
2854     # Truncation Attributes
2855
2856     pack $w.top.truncation -pady 6 -padx 6 -side top
2857     label $w.top.truncation.label -text "Truncation" -width 18
2858
2859     pack $w.top.truncation.label $w.top.truncation.b -fill x
2860
2861     # Completeness Attributes
2862
2863     pack $w.top.completeness -pady 6 -padx 6 -side top
2864     label $w.top.completeness.label -text "Completeness" -width 18
2865
2866     pack $w.top.completeness.label $w.top.completeness.b -fill x
2867
2868     # Ok-cancel
2869     bottom-buttons $w [list \
2870             {Ok} [list index-setup-action $attr $queryNo $indexNo] \
2871             {Cancel} [list destroy $w]] 0
2872
2873 }
2874
2875 # Procedure query-edit-index {queryNo}
2876 #  queryNo     query number
2877 # Determines if a selection of an index is active. If one is selected
2878 # the index-setup dialog is started.
2879 proc query-edit-index {queryNo} {
2880     global queryInfoTmp
2881     set w .query-setup
2882
2883     set i [lindex [$w.top.index.list curselection] 0]
2884     if {![string length $i]} {
2885         return
2886     }
2887     set attr [lindex $queryInfoTmp $i]
2888     dputs "Editing no $i $attr"
2889     index-setup $attr $queryNo $i
2890 }
2891
2892 # Procedure query-delete-index {queryNo}
2893 #  queryNo     query number
2894 # Determines if a selection of an index is active. If one is selected
2895 # the index is deleted.
2896 proc query-delete-index {queryNo} {
2897     global queryInfoTmp queryButtonsTmp
2898     set w .query-setup
2899
2900     set i [lindex [$w.top.index.list curselection] 0]
2901     if {![string length $i]} {
2902         return
2903     }
2904     set queryInfoTmp [lreplace $queryInfoTmp $i $i]
2905     index-lines $w.top.lines 0 $queryButtonsTmp $queryInfoTmp activate-e-index
2906     $w.top.index.list delete $i
2907 }
2908     
2909 # Procedure query-setup {queryNo}
2910 #  queryNo     query number
2911 # Makes a dialog in which a query type an be customized.
2912 proc query-setup {queryNo} {
2913     set w .query-setup
2914
2915     global queryTypes queryButtons queryInfo queryButtonsTmp queryInfoTmp queryIndexTmp
2916     
2917     set queryIndexTmp 0
2918     set queryName [lindex $queryTypes $queryNo]
2919     set queryInfoTmp [lindex $queryInfo $queryNo]
2920     set queryButtonsTmp [lindex $queryButtons $queryNo]
2921
2922     toplevelG $w
2923
2924     wm minsize $w 0 0
2925     wm title $w "Query setup $queryName"
2926
2927     top-down-window $w
2928
2929     frame $w.top.lines -relief ridge -border 2
2930     pack $w.top.lines -side left -pady 6 -padx 6 -fill y
2931
2932     # Index Lines
2933
2934     index-lines $w.top.lines 0 $queryButtonsTmp $queryInfoTmp activate-e-index
2935
2936     button $w.top.lines.add -text "Add" -command [list query-add-line $queryNo]
2937     button $w.top.lines.del -text "Remove" -command [list query-del-line $queryNo]
2938     pack $w.top.lines.del -fill x -side bottom
2939     pack $w.top.lines.add -fill x -pady 10 -side bottom
2940
2941     # Indexes
2942
2943     frame $w.top.index -relief ridge -border 2
2944     pack $w.top.index -pady 6 -padx 6 -side right -fill y
2945
2946     listbox $w.top.index.list -yscrollcommand [list $w.top.index.scroll set]
2947     scrollbar $w.top.index.scroll -orient vertical -border 1 \
2948         -command [list $w.top.index.list yview]
2949     bind $w.top.index.list <Double-1> [list query-edit-index $queryNo]
2950
2951     pack $w.top.index.list -side left -fill both -expand yes -padx 2 -pady 2
2952     pack $w.top.index.scroll -side right -fill y -padx 2 -pady 2
2953
2954     $w.top.index.list selection clear 0 end
2955     $w.top.index.list selection set 0 0
2956     foreach x $queryInfoTmp {
2957     $w.top.index.list insert end [lindex $x 0]
2958     }
2959
2960     # Bottom
2961     bottom-buttons $w [list \
2962             Ok [list query-setup-action $queryNo] \
2963             Add [list query-add-index $queryNo] \
2964             Edit [list query-edit-index $queryNo] \
2965             Delete [list query-delete-index $queryNo] \
2966             Cancel [list destroy $w]] 0
2967 }
2968
2969 # Procedure index-clear
2970 # Handler that clears the search entry fields.
2971 proc index-clear {} {
2972     global queryButtonsFind
2973
2974     set i 0
2975     foreach b $queryButtonsFind {
2976         .lines.$i.e delete 0 end
2977         incr i
2978     }
2979 }
2980
2981 # Procedure index-query
2982 # The purpose of this function is to read the user's query and convert
2983 # it to the prefix query that IrTcl/YAZ uses to represent an RPN query.
2984 # Each entry in a search fields takes the form
2985 #    [relOp][?]term[?]
2986 #  Here, relOp is an optional relational operator and one of:
2987 #      >  < >= <=  <>
2988 #    which sets the Bib-1 relation to greater-than, less-than, etc.
2989 #  The ? (question-mark) is also optional. A (?) on left-side indicates
2990 #    left truncation; (?) on right-side indicates right-truncation; (?)
2991 #    on both sides indicates both-left-and-right truncation.
2992 proc index-query {} {
2993     global queryButtonsFind queryInfoFind
2994
2995     set i 0
2996     set qs {}
2997
2998     foreach b $queryButtonsFind {
2999         set term [string trim [.lines.$i.e get]]
3000         if {$term != ""} {
3001             set attr [lrange [lindex $queryInfoFind [lindex $b 1]] 1 end]
3002
3003             set relation ""
3004             set len [string length $term]
3005             incr len -1
3006
3007             if {$len > 1} {
3008                 if {[string index $term 0] == ">"} {
3009                     if {[string index $term 1] == "=" } {
3010                         set term [string trim [string range $term 2 $len]]
3011                         set relation 4
3012                     } else {
3013                         set term [string trim [string range $term 1 $len]]
3014                         set relation 5
3015                     }
3016                 } elseif {[string index $term 0] == "<"} {
3017                     if {[string index $term 1] == "=" } {
3018                         set term [string trim [string range $term 2 $len]]
3019                         set relation 2
3020                     } elseif {[string index $term 1] == ">"} {
3021                         set term [string trim [string range $term 2 $len]]
3022                         set relation 6
3023                     } else {
3024                         set term [string trim [string range $term 1 $len]]
3025                         set relation 1
3026                     }
3027                 }
3028             } 
3029             set len [string length $term]
3030             incr len -1
3031             set left 0
3032             set right 0
3033             if {[string index $term $len] == "?"} {
3034                 set right 1
3035                 set term [string range $term 0 [expr {$len - 1}]]
3036             }
3037             if {[string index $term 0] == "?"} {
3038                 set left 1
3039                 set term [string range $term 1 end]
3040             }
3041             set term "\{${term}\}"
3042             if {$right && $left} {
3043                 set term "@attr 5=3 ${term}"
3044             } elseif {$right} {
3045                 set term "@attr 5=1 ${term}"
3046             } elseif {$left} {
3047                 set term "@attr 5=2 ${term}"
3048             }
3049             if {$relation != ""} {
3050                 set term "@attr 2=${relation} ${term}"
3051             }
3052             foreach a $attr {
3053                 set term "@attr $a ${term}"
3054             }
3055             if {$qs != ""} {
3056                 set qs "@and ${qs} ${term}"
3057             } else {
3058                 set qs $term
3059             }
3060         }
3061         incr i
3062     }
3063     dputs "qs=  $qs"
3064     return $qs
3065 }
3066
3067 # Procedure index-focus-in {w i}
3068 #  w    index frame
3069 #  i    index number
3070 # This procedure handles <FocusIn> events. A red border is drawed
3071 # around the active search entry field.
3072 proc index-focus-in {w i} {
3073     global curIndexEntry
3074     $w.$i configure -background red
3075     set curIndexEntry $i
3076 }
3077
3078 # Procedure index-lines {w readOp buttonInfo queryInfo handle}
3079 #  w          search fields entry frame
3080 #  realOp     if true, search-request bindings are bound to the entries.
3081 #  buttonInfo query type button information
3082 #  queryInfo  query type field information
3083 #  handle     handler called a when a 'listbutton' changes its value
3084 # Makes one or more search areas - with listbuttons on the left
3085 # and entries on the right. 
3086 proc index-lines {w realOp buttonInfo queryInfo handle} {
3087     set i 0
3088     foreach b $buttonInfo {
3089         if {! [winfo exists $w.$i]} {
3090             frame $w.$i -border 0
3091         }
3092         listbuttonx $w.$i.l [lindex $b 1] $queryInfo $handle $i
3093
3094         if {$realOp} {
3095             if {! [winfo exists $w.$i.e]} {
3096                 entry $w.$i.e -width 32 -relief sunken -border 1
3097                 bind $w.$i.e <FocusIn> [list index-focus-in $w $i]
3098                                 bind $w.$i.e <FocusOut> [list $w.$i configure -background white]
3099                 pack $w.$i.l -side left
3100                 pack $w.$i.e -side left -fill x -expand yes
3101                 pack $w.$i -side top -fill x -padx 2 -pady 2
3102                 bind $w.$i.e <Left> [list left-cursor $w.$i.e]
3103                 bind $w.$i.e <Right> [list right-cursor $w.$i.e]
3104                 bind $w.$i.e <Return> {search-request 0}
3105             }
3106         } else {
3107             pack $w.$i.l -side left
3108             pack $w.$i -side top -fill x -padx 2 -pady 2
3109         }
3110         incr i
3111     }
3112     set j $i
3113     while {[winfo exists $w.$j]} {
3114         destroy $w.$j
3115         incr j
3116     }
3117     if {! $realOp} {
3118         return
3119     }
3120     set j 0
3121     incr i -1
3122     while {$j < $i} {
3123         set k [expr {$j + 1}]
3124         bind $w.$j.e <Tab> "focus $w.$k.e"
3125         set j $k
3126     }
3127     if {$i >= 0} {
3128                 bind $w.$i.e <Tab> "focus $w.0.e"
3129         focus $w.0.e
3130     }
3131 }
3132
3133 #Procedure configureOptionsSyntax {target base}
3134 #target         target name
3135 #base           database name
3136 #Changes the Options|Syntax menu acording to the information obtained via explain.
3137 proc configureOptionsSyntax {target base} {
3138         global profile syntaxList recordSyntax 
3139         set activate 0
3140         set i -1
3141         set w .top.options.m.syntax
3142         if {[info exists profile($target,RecordSyntaxes,$base)]} {
3143                 foreach syntax $syntaxList {
3144                         incr i
3145                         if {$syntax == "sep"} {continue}
3146                         if {[lsearch $profile($target,RecordSyntaxes,$base) $syntax] != -1} {
3147                                 configure-enable-e $w $i
3148                                 if {$activate == 0} {
3149                                         $w invoke $i
3150                                         set recordSyntax $syntax
3151                                         set activate 1
3152                                 }
3153                         } else {
3154                                 configure-disable-e $w $i
3155                         }
3156                 }
3157         } else {
3158                 foreach syntax $syntaxList {
3159                         incr i
3160                         if {$syntax == "sep"} {continue}
3161                         configure-enable-e $w $i
3162                 }
3163                 $w invoke 0
3164         }
3165 }
3166
3167 # Init: The geometry information for the main window is set - either
3168 # to a default value or to the value in windowGeometry(.)
3169 if {[catch {set g $windowGeometry(.)}]} {
3170     wm geometry . 420x340
3171 } else {
3172     wm geometry . $g
3173 }
3174
3175 # Init: Presentation formats are read.
3176 read-formats
3177
3178 # Init: The main window is defined.
3179 frame .top  -border 1 -relief raised
3180 frame .lines  -border 1 -relief raised
3181 frame .mid  -border 1 -relief raised
3182 frame .data -border 1 -relief raised
3183 frame .bot  -border 1 -relief raised
3184 pack .top .lines .mid -side top -fill x
3185 pack .data -side top -fill both -expand yes
3186 pack .bot -fill x
3187
3188 # Init: Definition of File menu.
3189 menubutton .top.file -text File -menu .top.file.m
3190 irmenu .top.file.m
3191 .top.file.m add command -label {Save settings} -command {save-settings}
3192 .top.file.m add separator
3193 .top.file.m add command -label Exit -command {exit-action}
3194
3195 # Init: Definition of Target menu.
3196 menubutton .top.target -text Target -menu .top.target.m
3197 irmenu .top.target.m
3198 .top.target.m add cascade -label Connect -menu .top.target.m.clist
3199 .top.target.m add command -label Disconnect -command {close-target}
3200 .top.target.m add command -label About -command {about-target}
3201 .top.target.m add cascade -label Setup -menu .top.target.m.slist
3202 .top.target.m add command -label {Setup new} -command {define-target-dialog}
3203 .top.target.m add separator
3204 set-target-hotlist 0
3205
3206 configure-disable-e .top.target.m 1
3207 configure-disable-e .top.target.m 2
3208
3209 irmenu .top.target.m.clist
3210 irmenu .top.target.m.slist
3211 cascade-target-list
3212
3213 # Init: Definition of Service menu.
3214 menubutton .top.service -text Service -menu .top.service.m
3215 irmenu .top.service.m
3216 .top.service.m add cascade -label Database -menu .top.service.m.dblist
3217 .top.service.m add cascade -label Present -menu .top.service.m.present
3218 irmenu .top.service.m.present
3219 .top.service.m.present add command -label {10 More} -command [list present-more 10]
3220 .top.service.m.present add command -label All -command [list present-more {}]
3221 .top.service.m add command -label Search -command {search-request 0}
3222 .top.service.m add command -label Scan -command {scan-request}
3223 .top.service.m add command -label Explain -command \
3224     {explain-refresh $hostid {ready-response {}} }
3225
3226 .top.service configure -state disabled
3227
3228 irmenu .top.service.m.dblist
3229
3230 # Init: Definition of Set menu.
3231 menubutton .top.rset -text Set -menu .top.rset.m
3232 irmenu .top.rset.m
3233 .top.rset.m add command -label Load -command {load-set}
3234 .top.rset.m add separator
3235
3236 # Init: Definition of the Options menu.
3237 menubutton .top.options -text Options -menu .top.options.m
3238 irmenu .top.options.m
3239 .top.options.m add cascade -label Query -menu .top.options.m.query
3240 .top.options.m add cascade -label Format -menu .top.options.m.formats
3241 .top.options.m add cascade -label Wrap -menu .top.options.m.wrap
3242 .top.options.m add cascade -label Syntax -menu .top.options.m.syntax
3243 .top.options.m add cascade -label Elements -menu .top.options.m.elements
3244 .top.options.m add radiobutton -label Debug -variable debugMode -value 1
3245
3246 # Init: Definition of the Options|Query menu.
3247 irmenu .top.options.m.query
3248 .top.options.m.query add cascade -label Select -menu .top.options.m.query.clist
3249 .top.options.m.query add cascade -label Edit -menu .top.options.m.query.slist
3250 .top.options.m.query add command -label New -command {query-new}
3251 .top.options.m.query add cascade -label Delete -menu .top.options.m.query.dlist
3252
3253 irmenu .top.options.m.query.slist
3254 irmenu .top.options.m.query.clist
3255 irmenu .top.options.m.query.dlist
3256 cascade-query-list
3257
3258 # Init: Definition of the Options|Formats menu.
3259 irmenu .top.options.m.formats
3260 set i 0
3261 foreach f $displayFormats {
3262     .top.options.m.formats add radiobutton -label $f -value $i \
3263             -command [list set-display-format $i] -variable displayFormat
3264     incr i
3265 }
3266
3267 # Init: Definition of the Options|Wrap menu.
3268 irmenu .top.options.m.wrap
3269 .top.options.m.wrap add radiobutton -label Character \
3270         -value char -variable textWrap -command {set-wrap char}
3271 .top.options.m.wrap add radiobutton -label Word \
3272         -value word -variable textWrap -command {set-wrap word}
3273 .top.options.m.wrap add radiobutton -label None \
3274         -value none -variable textWrap -command {set-wrap none}
3275
3276 # Init: Definition of the Options|Syntax menu.
3277 proc initOptionsSyntax {} {
3278         global syntaxList recordSyntax
3279         set w .top.options.m.syntax
3280         irmenu $w
3281         foreach syntax $syntaxList {
3282                 if {$syntax == "sep"} {
3283                         $w add separator
3284                 } else {
3285                         $w add radiobutton -label $syntax -value $syntax -variable recordSyntax
3286                 }
3287         }
3288 }
3289 initOptionsSyntax
3290
3291 # Init: Definition of the Options|Elements menu.
3292 irmenu .top.options.m.elements
3293 .top.options.m.elements add radiobutton -label Unspecified \
3294         -value None -variable elementSetNames
3295 .top.options.m.elements add radiobutton -label Full \
3296         -value F -variable elementSetNames
3297 .top.options.m.elements add radiobutton -label Brief -value B -variable elementSetNames
3298
3299 # Init: Definition of Help menu.
3300 menubutton .top.help -text "Help" -menu .top.help.m
3301 irmenu .top.help.m
3302
3303 #.top.help.m add command -label "Help on help" -command {tkerror "Help on help not available. Sorry"}
3304 .top.help.m add command -label "Help on help" -command {bgerror "Help on help not available. Sorry"}
3305 .top.help.m add command -label "About" -command {about-origin}
3306
3307 # Init: Pack menu bar items.
3308 pack .top.file .top.target .top.service .top.rset .top.options -side left
3309 pack .top.help -side right
3310
3311 # Init: Define query area.
3312 index-lines .lines 1 $queryButtonsFind [lindex $queryInfo 0] activate-index
3313 image create photo scan -file [file join $libdir bitmaps a-z.gif]
3314 image create photo clear -file [file join $libdir bitmaps trash.gif]
3315 image create photo present -file [file join $libdir bitmaps page.gif]
3316 image create photo search -file [file join $libdir bitmaps search.gif]
3317 button .mid.search -image search -command {search-request 0} -state disabled -relief flat
3318 button .mid.scan -image scan -command scan-request -state disabled -relief flat
3319 button .mid.present -image present -command [list present-more 10] -state disabled -relief flat
3320 button .mid.clear -image clear -command index-clear -relief flat
3321 pack .mid.search .mid.scan .mid.present .mid.clear -side left -fill y -pady 1
3322
3323 # Init: Define record area in main window.
3324 text .data.record -font fixed -height 2 -width 20 -wrap none -borderwidth 0 \
3325         -relief flat -yscrollcommand [list .data.scroll set] -wrap $textWrap
3326 scrollbar .data.scroll -command [list .data.record yview]
3327 .data.record configure -takefocus 0
3328 .data.scroll configure -takefocus 0
3329
3330 pack .data.scroll -side right -fill y
3331 pack .data.record -expand yes -fill both
3332 initBindings
3333
3334 # Init: Define standards tags. These are used in the display
3335 # format procedures.
3336
3337 .data.record tag configure marc-tag -foreground blue
3338 .data.record tag configure marc-id -foreground red
3339 .data.record tag configure marc-data -foreground black
3340 .data.record tag configure marc-head -font $font(n,normal) \
3341         -foreground brown -relief raised -borderwidth 1
3342 .data.record tag configure marc-small-head -foreground brown
3343 .data.record tag configure marc-pref -font $font(n,normal) -foreground blue
3344 .data.record tag configure marc-text -font $font(n,normal) -foreground black
3345 .data.record tag configure marc-it -font $font(n,normal) -foreground black
3346
3347 # Init: Define logo.
3348 button .bot.logo -bitmap @[file join $libdir bitmaps book1] -command cancel-operation
3349 .bot.logo configure -takefocus 0
3350
3351 # Init: Define status information fields at the bottom.
3352 frame .bot.a
3353 pack .bot.a -side left -fill x
3354 pack .bot.logo -side right -padx 2 -pady 2 -ipadx 1
3355
3356 message .bot.a.target -text {} -aspect 2000 -border 1
3357
3358 label .bot.a.status -text "Not connected" -width 15 -relief sunken -anchor w -border 1
3359 label .bot.a.set -text "" -width 5 -relief sunken -anchor w -border 1
3360 label .bot.a.message -text "" -width 15 -relief sunken -anchor w -border 1
3361
3362 pack .bot.a.target -side top -anchor nw -padx 2 -pady 2
3363 pack .bot.a.status .bot.a.set .bot.a.message -side left -padx 2 -pady 2 -ipadx 1 -ipady 1
3364
3365 # Init: Determine if the IrTcl extension is already there. If
3366 #  not, then dynamically load the IrTcl extension.
3367 if {[catch {ir z39}]} {
3368     set e [info sharedlibextension]
3369     puts -nonewline "Loading irtcl$e ..."
3370     load [file join $libdir irtcl$e] irtcl
3371     ir z39
3372     puts "ok"
3373 }
3374
3375 if {[file exists [file join $libdir explain.tcl]]} {
3376     source [file join $libdir explain.tcl]
3377 }
3378
3379 #if {[file exists ${libdir}/setup.tcl]} 
3380     source [file join $libdir setup.tcl]
3381
3382
3383 # Init: Uncomment this line if you wan't to enable logging.
3384 ir-log-init all irtcl irtcl.log
3385
3386 # Init: If hostid is a valid target, a new connection will be established
3387 # immediately.
3388 if {[string compare $hostid Default]} {
3389     catch {open-target $hostid $hostbase}
3390 }
3391
3392 # Init: Enable the logo.
3393 show-logo 1
3394