oasis-gui.tcl

Go to the documentation of this file.
00001 /* !/bin/sh*/
00002 /*  the next line restarts using wish\*/
00003 exec wish "$0" "$@" 
00004 
00005 if {![info exists vTcl(sourcing)]} {
00006     switch $tcl_platform(platform) {
00007     windows {
00008     }
00009     default {
00010         option add *Scrollbar.width 10
00011     }
00012     }
00013     
00014 }
00015 /* */
00016 /*  Visual Tcl v1.51 Project*/
00017 /* */
00018 
00019 /* */
00020 /*  VTCL LIBRARY PROCEDURES*/
00021 /* */
00022 
00023 if {![info exists vTcl(sourcing)]} {
00024 ret  Window (type args) {
00025     global vTcl
00026     set cmd     [lindex $args 0]
00027     set name    [lindex $args 1]
00028     set newname [lindex $args 2]
00029     set rest    [lrange $args 3 end]
00030     if {$name == "" || $cmd == ""} { return }
00031     if {$newname == ""} { set newname $name }
00032     if {$name == "."} { wm withdraw $name; return }
00033     set exists [winfo exists $newname]
00034     switch $cmd {
00035         show {
00036             if {$exists} { wm deiconify $newname; return }
00037             if {[info procs vTclWindow(pre)$name] != ""} {
00038                 eval "vTclWindow(pre)$name $newname $rest"
00039             }
00040             if {[info procs vTclWindow$name] != ""} {
00041                 eval "vTclWindow$name $newname $rest"
00042             }
00043             if {[info procs vTclWindow(post)$name] != ""} {
00044                 eval "vTclWindow(post)$name $newname $rest"
00045             }
00046         }
00047         hide    { if $exists {wm withdraw $newname; return} }
00048         iconify { if $exists {wm iconify $newname; return} }
00049         destroy { if $exists {destroy $newname; return} }
00050     }
00051 }
00052 }
00053 
00054 if {![info exists vTcl(sourcing)]} {
00055 namespace ::mclistbox {
00056 
00057 ret  {::mclistbox::AdjustColumns} (type w , optional height ={)} {
00058     upvar ::mclistbox::${w}::widgets widgets
00059     upvar ::mclistbox::${w}::options options
00060     upvar ::mclistbox::${w}::misc    misc
00061 
00062     if {[string length $height] == 0} {
00063      height =  [winfo height $widgets(text)]
00064     }
00065 
00066     /*  resize the height of each column so it matches the height*/
00067     /*  of the text widget, minus a few pixels. */
00068     incr height -4
00069     foreach id $misc(columns) {
00070     $widgets(frame$id) configure -height $height
00071     }
00072     
00073     /*  if we have a fillcolumn, change its width accordingly*/
00074     if {$options(-fillcolumn) != ""} {
00075 
00076     /*  make sure the column has been defined. If not, bail (?)*/
00077     if {![info exists widgets(frame$options(-fillcolumn))]} {
00078         return
00079     }
00080      frame =  $widgets(frame$options(-fillcolumn))
00081      minwidth =  $misc(min-$frame)
00082 
00083     /*  compute current width of all columns*/
00084      colwidth =  0
00085      col =  0
00086     foreach id $misc(columns) {
00087         if {![ColumnIsHidden $w $id] && $id != $options(-fillcolumn)} {
00088         incr colwidth [winfo reqwidth $widgets(frame$id)]
00089         }
00090     }
00091 
00092     /*  this is just shorthand for later use...*/
00093      id =  $options(-fillcolumn)
00094 
00095     /*  compute optimal width*/
00096      optwidth =  [expr {[winfo width $widgets(text)] -  (2 * [$widgets(text) cget -padx])}]
00097 
00098     /*  compute the width of our fill column*/
00099      newwidth =  [expr {$optwidth - $colwidth}]
00100 
00101     if {$newwidth < $minwidth} {
00102          newwidth =  $minwidth
00103     }
00104 
00105     /*  adjust the width of our fill column frame*/
00106     $widgets(frame$id) configure -width $newwidth
00107         
00108     }
00109     InvalidateScrollbars $w
00110 }
00111 
00112 }
00113 
00114 namespace ::mclistbox {
00115 
00116 ret  {::mclistbox::Build} (type w , type args) {
00117     variable widgetOptions
00118 
00119     # create the namespace for this instance, and define a few
00120     # variables
00121     namespace eval ::mclistbox::$w {
00122 
00123     variable options
00124     variable widgets
00125     variable misc 
00126     }
00127 
00128     # this gives us access to the namespace variables within
00129     # this proc
00130     upvar ::mclistbox::${w}::widgets widgets
00131     upvar ::mclistbox::${w}::options options
00132     upvar ::mclistbox::${w}::misc    misc
00133 
00134     # initially we start out with no columns
00135     set misc(columns) {}
00136 
00137     # this is our widget -- a frame of class Mclistbox. Naturally,
00138     # it will contain other widgets. We create it here because
00139     # we need it to be able to set our default options.
00140     set widgets(this)   [frame  $w -class Mclistbox -takefocus 1]
00141 
00142     # this defines all of the default options. We get the
00143     # values from the option database. Note that if an array
00144     # value is a list of length one it is an alias to another
00145     # option, so we just ignore it
00146     foreach name [array names widgetOptions] {
00147     if {[llength $widgetOptions($name)] == 1} continue
00148     set optName  [lindex $widgetOptions($name) 0]
00149     set optClass [lindex $widgetOptions($name) 1]
00150     set options($name) [option get $w $optName $optClass]
00151     }
00152 
00153     # now apply any of the options supplied on the command
00154     # line. This may overwrite our defaults, which is OK
00155     if {[llength $args] > 0} {
00156     array set options $args
00157     }
00158     
00159     # the columns all go into a text widget since it has the 
00160     # ability to scroll.
00161     set widgets(text) [text $w.text  -width 0  -height 0  -padx 0  -pady 0  -wrap none  -borderwidth 0  -highlightthickness 0  -takefocus 0  -cursor {}  ]
00162 
00163     $widgets(text) configure -state disabled
00164 
00165     # here's the tricky part (shhhh... don't tell anybody!)
00166     # we are going to create column that completely fills 
00167     # the base frame. We will use it to control the sizing
00168     # of the widget. The trick is, we'll pack it in the frame 
00169     # and then place the text widget over it so it is never
00170     # seen.
00171 
00172     set columnWidgets [NewColumn $w {__hidden__}]
00173     set widgets(hiddenFrame)   [lindex $columnWidgets 0]
00174     set widgets(hiddenListbox) [lindex $columnWidgets 1]
00175     set widgets(hiddenLabel)   [lindex $columnWidgets 2]
00176 
00177     # by default geometry propagation is turned off, but for this
00178     # super-secret widget we want it turned on. The idea is, we 
00179     # resize the listbox which resizes the frame which resizes the 
00180     # whole shibang.
00181     pack propagate $widgets(hiddenFrame) on
00182 
00183     pack $widgets(hiddenFrame) -side top -fill both -expand y
00184     place $widgets(text) -x 0 -y 0 -relwidth 1.0 -relheight 1.0
00185     raise $widgets(text)
00186 
00187     # we will later rename the frame's widget proc to be our
00188     # own custom widget proc. We need to keep track of this
00189     # new name, so we'll define and store it here...
00190     set widgets(frame) ::mclistbox::${w}::$w
00191 
00192     # this moves the original frame widget proc into our
00193     # namespace and gives it a handy name
00194     rename ::$w $widgets(frame)
00195 
00196     # now, create our widget proc. Obviously (?) it goes in
00197     # the global namespace. All mclistbox widgets will actually
00198     # share the same widget proc to cut down on the amount of
00199     # bloat. 
00200     proc ::$w {command args}  "eval ::mclistbox::WidgetProc {$w} \$command \$args"
00201 
00202     # ok, the thing exists... let's do a bit more configuration. 
00203     if {[catch "Configure $widgets(this) [array get options]" error]} {
00204     catch {destroy $w}
00205     }
00206 
00207     # and be prepared to handle selections.. (this, for -exportselection
00208     # support)
00209     selection handle $w [list ::mclistbox::SelectionHandler $w get]
00210 
00211     return $w
00212 }
00213 
00214 }
00215 
00216 namespace ::mclistbox {
00217 
00218 ret  {::mclistbox::Canonize} (type w , type object , type opt) {
00219     variable widgetOptions
00220     variable columnOptions
00221     variable widgetCommands
00222     variable columnCommands
00223     variable labelCommands
00224 
00225     switch $object {
00226     command {
00227         if {[lsearch -exact $widgetCommands $opt] >= 0} {
00228         return $opt
00229         }
00230 
00231         # command names aren't stored in an array, and there
00232         # isn't a way to get all the matches in a list, so
00233         # we'll stuff the columns in a temporary array so
00234         # we can use [array names]
00235         set list $widgetCommands
00236         foreach element $list {
00237         set tmp($element) ""
00238         }
00239         set matches [array names tmp ${opt}*]
00240     }
00241 
00242     {label command} {
00243         if {[lsearch -exact $labelCommands $opt] >= 0} {
00244         return $opt
00245         }
00246 
00247         # command names aren't stored in an array, and there
00248         # isn't a way to get all the matches in a list, so
00249         # we'll stuff the columns in a temporary array so
00250         # we can use [array names]
00251         set list $labelCommands
00252         foreach element $list {
00253         set tmp($element) ""
00254         }
00255         set matches [array names tmp ${opt}*]
00256     }
00257 
00258     {column command} {
00259         if {[lsearch -exact $columnCommands $opt] >= 0} {
00260         return $opt
00261         }
00262 
00263         # command names aren't stored in an array, and there
00264         # isn't a way to get all the matches in a list, so
00265         # we'll stuff the columns in a temporary array so
00266         # we can use [array names]
00267         set list $columnCommands
00268         foreach element $list {
00269         set tmp($element) ""
00270         }
00271         set matches [array names tmp ${opt}*]
00272     }
00273 
00274     option {
00275             # JME : ajout pour Tk 8.5
00276             if {$opt == "-state"} {set opt "-relief"}
00277         if {[info exists widgetOptions($opt)]  && [llength $widgetOptions($opt)] == 3} {
00278         return $opt
00279         }
00280         set list [array names widgetOptions]
00281         set matches [array names widgetOptions ${opt}*]
00282     }
00283 
00284     {column option} {
00285         if {[info exists columnOptions($opt)]} {
00286         return $opt
00287         }
00288         set list [array names columnOptions]
00289         set matches [array names columnOptions ${opt}*]
00290     }
00291 
00292     column {
00293         upvar ::mclistbox::${w}::misc    misc
00294 
00295         if {[lsearch -exact $misc(columns) $opt] != -1} {
00296         return $opt
00297         }
00298         
00299         # column names aren't stored in an array, and there
00300         # isn't a way to get all the matches in a list, so
00301         # we'll stuff the columns in a temporary array so
00302         # we can use [array names]
00303         set list $misc(columns)
00304         foreach element $misc(columns) {
00305         set tmp($element) ""
00306         }
00307         set matches [array names tmp ${opt}*]
00308     }
00309     }
00310     if {[llength $matches] == 0} {
00311     set choices [HumanizeList $list]
00312     return -code error "unknown $object \"$opt\"; must be one of $choices"
00313 
00314     } elseif {[llength $matches] == 1} {
00315     # deal with option aliases
00316     set opt [lindex $matches 0]
00317     switch $object {
00318         option {
00319         if {[llength $widgetOptions($opt)] == 1} {
00320             set opt $widgetOptions($opt)
00321         }
00322         }
00323 
00324         {column option} {
00325         if {[llength $columnOptions($opt)] == 1} {
00326             set opt $columnOptions($opt)
00327         }
00328         }
00329     }
00330 
00331     return $opt
00332 
00333     } else {
00334     set choices [HumanizeList $list]
00335     return -code error "ambiguous $object \"$opt\"; must be one of $choices"
00336     }
00337 }
00338 
00339 }
00340 
00341 namespace ::mclistbox {
00342 
00343 ret  {::mclistbox::CheckColumnID} (type w , type id) {
00344     upvar ::mclistbox::${w}::misc    misc
00345 
00346     set id [::mclistbox::Canonize $w column $id]
00347     set index [lsearch -exact $misc(columns) $id]
00348     return $index
00349 }
00350 
00351 }
00352 
00353 namespace ::mclistbox {
00354 
00355 ret  {::mclistbox::Column-add} (type w , type args) {
00356     upvar ::mclistbox::${w}::widgets widgets
00357     upvar ::mclistbox::${w}::options options
00358     upvar ::mclistbox::${w}::misc    misc
00359 
00360     variable widgetOptions
00361 
00362     set id "column-[llength $misc(columns)]" ;# a suitable default
00363 
00364     # if the first argument doesn't have a "-" as the first
00365     # character, it is an id to associate with this column
00366     if {![string match {-*} [lindex $args 0]]} {
00367     # the first arg must be an id. 
00368     set id [lindex $args 0]
00369     set args [lrange $args 1 end]
00370     if {[lsearch -exact $misc(columns) $id] != -1} {
00371         return -code error "column \"$id\" already exists"
00372     }
00373     }
00374 
00375     # define some reasonable defaults, then add any specific
00376     # values supplied by the user
00377     set opts(-bitmap)     {}
00378     set opts(-image)      {}
00379     set opts(-visible)    1
00380     set opts(-resizable)  1
00381     set opts(-position)   "end"
00382     set opts(-width)      20
00383     set opts(-background) $options(-background)
00384     set opts(-foreground) $options(-foreground)
00385     set opts(-font)       $options(-font)
00386     set opts(-label)      $id
00387 
00388     if {[expr {[llength $args]%2}] == 1} {
00389     # hmmm. An odd number of elements in args
00390     # if the last item is a valid option we'll give a different
00391     # error than if its not
00392     set option [::mclistbox::Canonize $w "column option" [lindex $args end]]
00393     return -code error "value for \"[lindex $args end]\" missing"
00394     }
00395     array set opts $args
00396 
00397     # figure out if we have any data in the listbox yet; we'll need
00398     # this information in a minute...
00399     if {[llength $misc(columns)] > 0} {
00400     set col0 [lindex $misc(columns) 0]
00401     set existingRows [$widgets(listbox$col0) size]
00402     } else {
00403     set existingRows 0
00404     }
00405 
00406     # create the widget and assign the associated paths to our array
00407     set widgetlist [NewColumn $w $id]
00408 
00409     set widgets(frame$id)   [lindex $widgetlist 0]
00410     set widgets(listbox$id) [lindex $widgetlist 1]
00411     set widgets(label$id)   [lindex $widgetlist 2]
00412     
00413     # add this column to the list of known columns
00414     lappend misc(columns) $id
00415 
00416     # configure the options. As a side effect, it will be inserted
00417     # in the text widget
00418     eval ::mclistbox::Column-configure {$w} {$id} [array get opts]
00419 
00420     # now, if there is any data already in the listbox, we need to
00421     # add a corresponding number of blank items. At least, I *think*
00422     # that's the right thing to do.
00423     if {$existingRows > 0} {
00424     set blanks {}
00425     for {set i 0} {$i < $existingRows} {incr i} {
00426         lappend blanks {}
00427     }
00428     eval {$widgets(listbox$id)} insert end $blanks
00429     }
00430 
00431     InvalidateScrollbars $w
00432     return $id
00433 }
00434 
00435 }
00436 
00437 namespace ::mclistbox {
00438 
00439 ret  {::mclistbox::Column-configure} (type w , type id , type args) {
00440     variable widgetOptions
00441     variable columnOptions
00442 
00443     upvar ::mclistbox::${w}::widgets widgets
00444     upvar ::mclistbox::${w}::options options
00445     upvar ::mclistbox::${w}::misc    misc
00446 
00447     # bail if they gave us a bogus id
00448     set index [CheckColumnID $w $id]
00449 
00450     # define some shorthand
00451     set listbox $widgets(listbox$id)
00452     set frame   $widgets(frame$id)
00453     set label   $widgets(label$id)
00454 
00455     if {[llength $args] == 0} {
00456     # hmmm. User must be wanting all configuration information
00457     # note that if the value of an array element is of length
00458     # one it is an alias, which needs to be handled slightly
00459     # differently
00460     set results {}
00461     foreach opt [lsort [array names columnOptions]] {
00462         if {[llength $columnOptions($opt)] == 1} {
00463         set alias $columnOptions($opt)
00464         set optName $columnOptions($alias)
00465         lappend results [list $opt $optName]
00466         } else {
00467         set optName  [lindex $columnOptions($opt) 0]
00468         set optClass [lindex $columnOptions($opt) 1]
00469         set default [option get $frame $optName $optClass]
00470         lappend results [list $opt $optName $optClass  $default $options($id:$opt)]
00471         }
00472     }
00473 
00474     return $results
00475 
00476 
00477     } elseif {[llength $args] == 1} {
00478 
00479     # the user must be querying something... I need to get this
00480     # to return a bona fide list like the "real" configure 
00481     # command, but it's not a priority at the moment. I still
00482     # have to work on the option database support foo.
00483     set option [::mclistbox::Canonize $w "column option" [lindex $args 0]]
00484 
00485     set value $options($id:$option)
00486     set optName  [lindex $columnOptions($option) 0]
00487     set optClass [lindex $columnOptions($option) 1]
00488     set default  [option get $frame $optName $optClass]
00489     set results  [list $option $optName $optClass $default $value]
00490     return $results
00491 
00492     }
00493 
00494     # if we have an odd number of values, bail. 
00495     if {[expr {[llength $args]%2}] == 1} {
00496     # hmmm. An odd number of elements in args
00497     return -code error "value for \"[lindex $args end]\" missing"
00498     }
00499     
00500     # Great. An even number of options. Let's make sure they 
00501     # are all valid before we do anything. Note that Canonize
00502     # will generate an error if it finds a bogus option; otherwise
00503     # it returns the canonical option name
00504     foreach {name value} $args {
00505     set name [::mclistbox::Canonize $w "column option" $name]
00506     set opts($name) $value
00507     }
00508 
00509     # if we get to here, the user is wanting to set some options
00510     foreach option [array names opts] {
00511     set value $opts($option)
00512     set options($id:$option) $value
00513 
00514     switch -- $option {
00515         -label {
00516         $label configure -text $value
00517         }
00518         
00519         -image -
00520         -bitmap {
00521         $label configure $option $value
00522         }
00523 
00524         -width {
00525         set font [$listbox cget -font]
00526         set factor [font measure $options(-font) "0"]
00527         set width [expr {$value * $factor}]
00528 
00529         $widgets(frame$id) configure -width $width
00530         set misc(min-$widgets(frame$id)) $width
00531         AdjustColumns $w
00532         }
00533         -font -
00534         -foreground -
00535         -background {
00536         if {[string length $value] == 0} {set value $options($option)}
00537         $listbox configure $option $value
00538         }
00539 
00540         -labelrelief {
00541         $widgets(label$id) configure -relief $value
00542         }
00543 
00544         -resizable {
00545         if {[catch {
00546             if {$value} {
00547             set options($id:-resizable) 1
00548             } else {
00549             set options($id:-resizable) 0
00550             }
00551         } msg]} {
00552             return -code error "expected boolean but got \"$value\""
00553         }
00554         }
00555 
00556         -visible {
00557         if {[catch {
00558             if {$value} {
00559             set options($id:-visible) 1
00560             $widgets(text) configure -state normal
00561             $widgets(text) window configure 1.$index -window $frame
00562             $widgets(text) configure -state disabled
00563 
00564             } else {
00565             set options($id:-visible) 0
00566             $widgets(text) configure -state normal
00567             $widgets(text) window configure 1.$index -window {}
00568             $widgets(text) configure -state disabled
00569             }
00570             InvalidateScrollbars $w
00571         } msg]} {
00572             return -code error "expected boolean but got \"$value\""
00573         }
00574 
00575         }
00576 
00577         -position {
00578         if {[string compare $value "start"] == 0} {
00579             set position 0
00580 
00581         } elseif {[string compare $value "end"] == 0} {
00582 
00583             set position [expr {[llength $misc(columns)] -1}]
00584         } else {
00585 
00586             # ought to check for a legal value here, but I'm 
00587             # lazy
00588             set position $value
00589         }
00590 
00591         if {$position >= [llength $misc(columns)]} {
00592             set max [expr {[llength $misc(columns)] -1}]
00593             return -code error "bad position; must be in the range of 0-$max"
00594         }
00595 
00596         # rearrange misc(columns) to reflect the new ordering
00597         set current [lsearch -exact $misc(columns) $id]
00598         set misc(columns) [lreplace $misc(columns) $current $current]
00599         set misc(columns) [linsert $misc(columns) $position $id]
00600         
00601         set frame $widgets(frame$id)
00602         $widgets(text) configure -state normal
00603         $widgets(text) window create 1.$position  -window $frame -stretch 1
00604         $widgets(text) configure -state disabled
00605         }
00606 
00607     }
00608     }
00609 }
00610 
00611 }
00612 
00613 namespace ::mclistbox {
00614 
00615 ret  {::mclistbox::ColumnIsHidden} (type w , type id) {
00616     upvar ::mclistbox::${w}::widgets widgets
00617     upvar ::mclistbox::${w}::misc    misc
00618     
00619     set retval 1
00620     set col [lsearch -exact $misc(columns) $id]
00621 
00622     if {$col != ""} {
00623     set index "1.$col"
00624     catch {
00625         set window [$widgets(text) window cget $index -window]
00626         if {[string length $window] > 0 && [winfo exists $window]} {
00627         set retval 0
00628         }
00629     }
00630     }
00631     return $retval
00632 }
00633 
00634 }
00635 
00636 namespace ::mclistbox {
00637 
00638 ret  {::mclistbox::Configure} (type w , type args) {
00639     variable widgetOptions
00640 
00641     upvar ::mclistbox::${w}::widgets widgets
00642     upvar ::mclistbox::${w}::options options
00643     upvar ::mclistbox::${w}::misc    misc
00644     
00645     if {[llength $args] == 0} {
00646     # hmmm. User must be wanting all configuration information
00647     # note that if the value of an array element is of length
00648     # one it is an alias, which needs to be handled slightly
00649     # differently
00650     set results {}
00651     foreach opt [lsort [array names widgetOptions]] {
00652         if {[llength $widgetOptions($opt)] == 1} {
00653         set alias $widgetOptions($opt)
00654         set optName $widgetOptions($alias)
00655         lappend results [list $opt $optName]
00656         } else {
00657         set optName  [lindex $widgetOptions($opt) 0]
00658         set optClass [lindex $widgetOptions($opt) 1]
00659         set default [option get $w $optName $optClass]
00660         lappend results [list $opt $optName $optClass  $default $options($opt)]
00661         }
00662     }
00663 
00664     return $results
00665     }
00666     
00667     # one argument means we are looking for configuration
00668     # information on a single option
00669     if {[llength $args] == 1} {
00670     set opt [::mclistbox::Canonize $w option [lindex $args 0]]
00671 
00672     set optName  [lindex $widgetOptions($opt) 0]
00673     set optClass [lindex $widgetOptions($opt) 1]
00674     set default [option get $w $optName $optClass]
00675     set results [list $opt $optName $optClass  $default $options($opt)]
00676     return $results
00677     }
00678 
00679     # if we have an odd number of values, bail. 
00680     if {[expr {[llength $args]%2}] == 1} {
00681     # hmmm. An odd number of elements in args
00682     return -code error "value for \"[lindex $args end]\" missing"
00683     }
00684     
00685     # Great. An even number of options. Let's make sure they 
00686     # are all valid before we do anything. Note that Canonize
00687     # will generate an error if it finds a bogus option; otherwise
00688     # it returns the canonical option name
00689     foreach {name value} $args {
00690     set name [::mclistbox::Canonize $w option $name]
00691     set opts($name) $value
00692     }
00693 
00694     # process all of the configuration options
00695     foreach option [array names opts] {
00696 
00697     set newValue $opts($option)
00698     if {[info exists options($option)]} {
00699         set oldValue $options($option)
00700 #       set options($option) $newValue
00701     }
00702 
00703     switch -- $option {
00704         -exportselection {
00705         if {$newValue} {
00706             SelectionHandler $w own
00707             set options($option) 1
00708         } else {
00709             set options($option) 0
00710         }
00711         }
00712 
00713         -fillcolumn {
00714         # if the fill column changed, we need to adjust
00715         # the columns.
00716         AdjustColumns $w
00717         set options($option) $newValue
00718         }
00719 
00720         -takefocus {
00721         $widgets(frame) configure -takefocus $newValue
00722         set options($option) [$widgets(frame) cget $option]
00723         }
00724 
00725         -background {
00726         foreach id $misc(columns) {
00727             $widgets(listbox$id) configure -background $newValue
00728             $widgets(frame$id) configure   -background $newValue
00729         }
00730         $widgets(frame) configure -background $newValue
00731 
00732         $widgets(text) configure -background $newValue
00733         set options($option) [$widgets(frame) cget $option]
00734         }
00735 
00736         # { the following all must be applied to each listbox }
00737         -foreground -
00738         -font -
00739         -selectborderwidth -
00740         -selectforeground -
00741         -selectbackground -
00742         -setgrid {
00743         foreach id $misc(columns) {
00744             $widgets(listbox$id) configure $option $newValue
00745         }
00746         $widgets(hiddenListbox) configure $option $newValue
00747         set options($option) [$widgets(hiddenListbox) cget $option]
00748         }
00749 
00750         # { the following all must be applied to each listbox and frame }
00751         -cursor {
00752         foreach id $misc(columns) {
00753             $widgets(listbox$id) configure $option $newValue
00754             $widgets(frame$id) configure -cursor $newValue
00755         }
00756 
00757         # -cursor also needs to be applied to the 
00758         # frames of each column
00759         foreach id $misc(columns) {
00760             $widgets(frame$id) configure -cursor $newValue
00761         }
00762 
00763         $widgets(hiddenListbox) configure $option $newValue
00764         set options($option) [$widgets(hiddenListbox) cget $option]
00765         }
00766 
00767         # { this just requires to pack or unpack the labels }
00768         -labels {
00769         if {$newValue} {
00770             set newValue 1
00771             foreach id $misc(columns) {
00772             pack $widgets(label$id)  -side top -fill x -expand n  -before $widgets(listbox$id)
00773             }
00774             pack $widgets(hiddenLabel)  -side top -fill x -expand n  -before $widgets(hiddenListbox)
00775 
00776         } else {
00777             set newValue 
00778             foreach id $misc(columns) {
00779             pack forget $widgets(label$id)
00780             }
00781             pack forget $widgets(hiddenLabel)
00782         }
00783         set options($option) $newValue
00784         }
00785 
00786         -height {
00787         $widgets(hiddenListbox) configure -height $newValue
00788         InvalidateScrollbars $w
00789         set options($option) [$widgets(hiddenListbox) cget $option]
00790         }
00791 
00792         -width {
00793         if {$newValue == 0} {
00794             return -code error "a -width of zero is not supported. "
00795         }
00796 
00797         $widgets(hiddenListbox) configure -width $newValue
00798         InvalidateScrollbars $w
00799         set options($option) [$widgets(hiddenListbox) cget $option]
00800         }
00801 
00802         # { the following all must be applied to each column frame }
00803         -columnborderwidth -
00804         -columnrelief {
00805         regsub {column} $option {} listboxoption
00806         foreach id $misc(columns) {
00807             $widgets(listbox$id) configure $listboxoption $newValue
00808         }
00809         $widgets(hiddenListbox) configure $listboxoption $newValue
00810         set options($option) [$widgets(hiddenListbox) cget  $listboxoption]
00811         }
00812 
00813         -resizablecolumns {
00814         if {$newValue} {
00815             set options($option) 1
00816         } else {
00817             set options($option) 0
00818         }
00819         }
00820         
00821         # { the following all must be applied to each column header }
00822         -labelimage -
00823         -labelheight -
00824         -labelrelief -
00825         -labelfont -
00826         -labelanchor -
00827         -labelbackground -
00828         -labelforeground -
00829         -labelborderwidth {
00830         regsub {label} $option {} labeloption
00831         foreach id $misc(columns) {
00832             $widgets(label$id) configure $labeloption $newValue
00833         }
00834         $widgets(hiddenLabel) configure $labeloption $newValue
00835         set options($option) [$widgets(hiddenLabel) cget $labeloption]
00836         }
00837 
00838         # { the following apply only to the topmost frame}
00839         -borderwidth -
00840         -highlightthickness -
00841         -highlightcolor -
00842         -highlightbackground -
00843         -relief {
00844         $widgets(frame) configure $option $newValue
00845         set options($option) [$widgets(frame) cget $option]
00846         }
00847 
00848         -selectmode {
00849         set options($option) $newValue
00850         }
00851 
00852         -selectcommand {
00853         set options($option) $newValue
00854         }
00855 
00856         -xscrollcommand {
00857         InvalidateScrollbars $w
00858         set options($option) $newValue
00859         }
00860 
00861         -yscrollcommand {
00862         InvalidateScrollbars $w
00863         set options($option) $newValue
00864         }
00865     }
00866     }
00867 }
00868 
00869 }
00870 
00871 namespace ::mclistbox {
00872 
00873 ret  {::mclistbox::DestroyHandler} (type w) {
00874     # kill off any idle event we might have pending
00875     if {[info exists ::mclistbox::${w}::misc(afterid)]} {
00876     catch {
00877         after cancel $::mclistbox::${w}::misc(afterid)
00878         unset ::mclistbox::${w}::misc(afterid)
00879     }
00880     }
00881 
00882     # if the widget actually being destroyed is of class Mclistbox,
00883     # crush the namespace and kill the proc. Get it? Crush. Kill. 
00884     # Destroy. Heh. Danger Will Robinson! Oh, man! I'm so funny it
00885     # brings tears to my eyes.
00886     if {[string compare [winfo class $w] "Mclistbox"] == 0} {
00887     namespace delete ::mclistbox::$w
00888     rename $w {}
00889     }
00890 }
00891 
00892 }
00893 
00894 namespace ::mclistbox {
00895 
00896 ret  {::mclistbox::FindResizableNeighbor} (type w , type id , optional direction =right) {
00897     upvar ::mclistbox::${w}::widgets       widgets
00898     upvar ::mclistbox::${w}::options       options
00899     upvar ::mclistbox::${w}::misc          misc
00900 
00901 
00902     if {$direction == "right"} {
00903     set incr 1
00904     set stop [llength $misc(columns)]
00905     set start [expr {[lsearch -exact $misc(columns) $id] + 1}]
00906     } else {
00907     set incr -1
00908     set stop -1
00909     set start [expr {[lsearch -exact $misc(columns) $id] - 1}]
00910     } 
00911 
00912     for {set i $start} {$i != $stop} {incr i $incr} {
00913     set col [lindex $misc(columns) $i]
00914     if {![ColumnIsHidden $w $col] && $options($col:-resizable)} {
00915         return $col
00916     }
00917     }
00918     return ""
00919 }
00920 
00921 }
00922 
00923 namespace ::mclistbox {
00924 
00925 ret  {::mclistbox::HumanizeList} (type list) {
00926     if {[llength $list] == 1} {
00927     return [lindex $list 0]
00928     } else {
00929     set list [lsort $list]
00930     set secondToLast [expr {[llength $list] -2}]
00931     set most [lrange $list 0 $secondToLast]
00932     set last [lindex $list end]
00933 
00934     return "[join $most {, }] or $last"
00935     }
00936 }
00937 
00938 }
00939 
00940 namespace ::mclistbox {
00941 
00942 ret  {::mclistbox::Init} () {
00943     variable widgetOptions
00944     variable columnOptions
00945     variable widgetCommands
00946     variable columnCommands
00947     variable labelCommands
00948 
00949     # here we match up command line options with option database names
00950     # and classes. As it turns out, this is a handy reference of all of the
00951     # available options. Note that if an item has a value with only one
00952     # item (like -bd, for example) it is a synonym and the value is the
00953     # actual item.
00954 
00955     array set widgetOptions [list  -background          {background          Background}  -bd                  -borderwidth  -bg                  -background  -borderwidth         {borderWidth         BorderWidth}  -columnbd            -columnborderwidth  -columnborderwidth   {columnBorderWidth   BorderWidth}  -columnrelief        {columnRelief        Relief}  -cursor              {cursor              Cursor}  -exportselection     {exportSelection     ExportSelection}  -fg                  -foreground  -fillcolumn          {fillColumn          FillColumn}  -font                {font                Font}  -foreground          {foreground          Foreground}  -height              {height              Height}  -highlightbackground {highlightBackground HighlightBackground}  -highlightcolor      {highlightColor      HighlightColor}  -highlightthickness  {highlightThickness  HighlightThickness}  -labelanchor         {labelAnchor         Anchor}  -labelbackground     {labelBackground     Background}  -labelbd             -labelborderwidth  -labelbg             -labelbackground  -labelborderwidth    {labelBorderWidth    BorderWidth}  -labelfg             -labelforeground  -labelfont           {labelFont           Font}  -labelforeground     {labelForeground     Foreground}  -labelheight         {labelHeight         Height}  -labelimage          {labelImage          Image}  -labelrelief         {labelRelief         Relief}  -labels              {labels              Labels}  -relief              {relief              Relief}  -resizablecolumns    {resizableColumns    ResizableColumns}  -selectbackground    {selectBackground    Foreground}  -selectborderwidth   {selectBorderWidth   BorderWidth}  -selectcommand       {selectCommand       Command}  -selectforeground    {selectForeground    Background}  -selectmode          {selectMode          SelectMode}  -setgrid             {setGrid             SetGrid}  -takefocus           {takeFocus           TakeFocus}  -width               {width               Width}  -xscrollcommand      {xScrollCommand      ScrollCommand}  -yscrollcommand      {yScrollCommand      ScrollCommand}  ]
00956 
00957     # and likewise for column-specific stuff. 
00958     array set columnOptions [list  -background         {background           Background}  -bitmap       {bitmap               Bitmap}  -font               {font                 Font}  -foreground         {foreground           Foreground}  -image              {image                Image}  -label         {label                Label}  -position           {position             Position}  -labelrelief        {labelrelief          Labelrelief}  -resizable          {resizable            Resizable}  -visible            {visible              Visible}  -width              {width                Width}  ]
00959 
00960     # this defines the valid widget commands. It's important to
00961     # list them here; we use this list to validate commands and
00962     # expand abbreviations.
00963     set widgetCommands [list  activate   bbox       cget     column    configure   curselection delete     get      index     insert  label        nearest    scan     see       selection   size         xview      yview
00964     ]
00965 
00966     set columnCommands [list add cget configure delete names nearest]
00967     set labelCommands  [list bind]
00968 
00969     ######################################################################
00970     #- this initializes the option database. Kinda gross, but it works
00971     #- (I think). 
00972     ######################################################################
00973 
00974     set packages [package names]
00975 
00976     # why check for the Tk package? This lets us be sourced into 
00977     # an interpreter that doesn't have Tk loaded, such as the slave
00978     # interpreter used by pkg_mkIndex. In theory it should have no
00979     # side effects when run 
00980     if {[lsearch -exact [package names] "Tk"] != -1} {
00981 
00982     # compute a widget name we can use to create a temporary widget
00983     set tmpWidget ".__tmp__"
00984     set count 0
00985     while {[winfo exists $tmpWidget] == 1} {
00986         set tmpWidget ".__tmp__$count"
00987         incr count
00988     }
00989 
00990     # steal options from the listbox
00991     # we want darn near all options, so we'll go ahead and do
00992     # them all. No harm done in adding the one or two that we
00993     # don't use.
00994     listbox $tmpWidget 
00995     foreach foo [$tmpWidget configure] {
00996         if {[llength $foo] == 5} {
00997         set option [lindex $foo 1]
00998         set value [lindex $foo 4]
00999         option add *Mclistbox.$option $value widgetDefault
01000 
01001         # these options also apply to the individual columns...
01002         if {[string compare $option "foreground"] == 0  || [string compare $option "background"] == 0  || [string compare $option "font"] == 0} {
01003             option add *Mclistbox*MclistboxColumn.$option $value  widgetDefault
01004         }
01005         }
01006     }
01007     destroy $tmpWidget
01008 
01009     # steal some options from label widgets; we only want a subset
01010     # so we'll use a slightly different method. No harm in *not*
01011     # adding in the one or two that we don't use... :-)
01012     label $tmpWidget
01013     foreach option [list Anchor Background Font  Foreground Height Image  ] {
01014         set values [$tmpWidget configure -[string tolower $option]]
01015         option add *Mclistbox.label$option [lindex $values 3]
01016     }
01017     destroy $tmpWidget
01018 
01019     # these are unique to us...
01020     option add *Mclistbox.columnBorderWidth   0      widgetDefault
01021     option add *Mclistbox.columnRelief        flat   widgetDefault
01022     option add *Mclistbox.labelBorderWidth    1      widgetDefault
01023     option add *Mclistbox.labelRelief         raised widgetDefault
01024     option add *Mclistbox.labels              1      widgetDefault
01025     option add *Mclistbox.resizableColumns    1      widgetDefault
01026     option add *Mclistbox.selectcommand       {}     widgetDefault
01027     option add *Mclistbox.fillcolumn          {}     widgetDefault
01028 
01029     # column options
01030     option add *Mclistbox*MclistboxColumn.visible       1      widgetDefault
01031     option add *Mclistbox*MclistboxColumn.resizable     1      widgetDefault
01032     option add *Mclistbox*MclistboxColumn.position      end    widgetDefault
01033     option add *Mclistbox*MclistboxColumn.label         ""     widgetDefault
01034     option add *Mclistbox*MclistboxColumn.width         0      widgetDefault
01035     option add *Mclistbox*MclistboxColumn.bitmap        ""     widgetDefault
01036     option add *Mclistbox*MclistboxColumn.image         ""     widgetDefault
01037     }
01038 
01039     ######################################################################
01040     # define the class bindings
01041     ######################################################################
01042     
01043     SetClassBindings
01044 }
01045 
01046 }
01047 
01048 namespace ::mclistbox {
01049 
01050 ret  {::mclistbox::Insert} (type w , type index , type arglist) {
01051     upvar ::mclistbox::${w}::widgets widgets
01052     upvar ::mclistbox::${w}::options options
01053     upvar ::mclistbox::${w}::misc    misc
01054 
01055     foreach list $arglist {
01056     # make sure we have enough elements for each column
01057     for {set i [llength $list]} {$i < [llength $misc(columns)]} {incr i} {
01058         lappend list {}
01059     }
01060 
01061     set column 0
01062     foreach id $misc(columns) {
01063         $widgets(listbox$id) insert $index [lindex $list $column]
01064         incr column
01065     }
01066 
01067     # we also want to add a bogus item to the hidden listbox. Why?
01068     # For standard listboxes, if you specify a height of zero the
01069     # listbox will resize to be just big enough to hold all the lines.
01070     # Since we use a hidden listbox to regulate the size of the widget
01071     # and we want this same behavior, this listbox needs the same number
01072     # of elements as the visible listboxes
01073     #
01074     # (NB: we might want to make this listbox contain the contents
01075     # of all columns as a properly formatted list; then the get 
01076     # command can query this listbox instead of having to query
01077     # each individual listbox. The disadvantage is that it doubles
01078     # the memory required to hold all the data)
01079     $widgets(hiddenListbox) insert $index "x"
01080     }
01081     return ""
01082 }
01083 
01084 }
01085 
01086 namespace ::mclistbox {
01087 
01088 ret  {::mclistbox::InvalidateScrollbars} (type w) {
01089     upvar ::mclistbox::${w}::widgets widgets
01090     upvar ::mclistbox::${w}::options options
01091     upvar ::mclistbox::${w}::misc    misc
01092 
01093     if {![info exists misc(afterid)]} {
01094     set misc(afterid)  [after idle "catch {::mclistbox::UpdateScrollbars $w}"]
01095     }
01096 }
01097 
01098 }
01099 
01100 namespace ::mclistbox {
01101 
01102 ret  {::mclistbox::LabelEvent} (type w , type id , type code) {
01103     upvar ::mclistbox::${w}::widgets widgets
01104     upvar ::mclistbox::${w}::options options
01105 
01106     # only fire the binding if the cursor is our default cursor
01107     # (ie: if we aren't in a "resize zone")
01108     set cursor [$widgets(label$id) cget -cursor]
01109     if {[string compare $cursor $options(-cursor)] == 0} {
01110     uplevel \#0 $code
01111     }
01112 }
01113 
01114 }
01115 
01116 namespace ::mclistbox {
01117 
01118 ret  {::mclistbox::MassageIndex} (type w , type index) {
01119     upvar ::mclistbox::${w}::widgets   widgets
01120     upvar ::mclistbox::${w}::misc      misc
01121 
01122     if {[regexp {@([0-9]+),([0-9]+)} $index matchvar x y]} {
01123     set id [lindex $misc(columns) 0]
01124     
01125     incr y -[winfo y $widgets(listbox$id)]
01126     incr y -[winfo y $widgets(frame$id)]
01127     incr x [winfo x $widgets(listbox$id)]
01128     incr x [winfo x $widgets(frame$id)]
01129 
01130     set index @${x},${y}
01131     }
01132 
01133     return $index
01134 }
01135 
01136 }
01137 
01138 namespace ::mclistbox {
01139 
01140 ret  {::mclistbox::NewColumn} (type w , type id) {
01141     upvar ::mclistbox::${w}::widgets   widgets
01142     upvar ::mclistbox::${w}::options   options
01143     upvar ::mclistbox::${w}::misc      misc
01144     upvar ::mclistbox::${w}::columnID  columnID
01145 
01146     # the columns are all children of the text widget we created... 
01147     set frame      [frame $w.frame$id  -takefocus 0  -highlightthickness 0  -class MclistboxColumn  -background $options(-background)  ]
01148 
01149     set listbox    [listbox $frame.listbox  -takefocus 0  -bd 0  -setgrid $options(-setgrid)  -exportselection false  -selectmode $options(-selectmode)  -highlightthickness 0  ]
01150 
01151     set label      [label $frame.label  -takefocus 0  -relief raised  -bd 1  -highlightthickness 0  ]
01152 
01153     # define mappings from widgets to columns
01154     set columnID($label) $id
01155     set columnID($frame) $id
01156     set columnID($listbox) $id
01157 
01158     # we're going to associate a new bindtag for the label to
01159     # handle our resize bindings. Why? We want the bindings to
01160     # be specific to this widget but we don't want to use the
01161     # widget name. If we use the widget name then the bindings
01162     # could get mixed up with user-supplied bindigs (via the 
01163     # "label bind" command). 
01164     set tag MclistboxLabel
01165     bindtags $label  [list MclistboxMouseBindings $label]
01166 
01167     # reconfigure the label based on global options
01168     foreach option [list bd image height relief font anchor  background foreground borderwidth] {
01169     if {[info exists options(-label$option)]  && $options(-label$option) != ""} {
01170         $label configure -$option $options(-label$option)
01171     }
01172     }
01173 
01174     # reconfigure the column based on global options
01175     foreach option [list borderwidth relief] {
01176     if {[info exists options(-column$option)]  && $options(-column$option) != ""} {
01177         $frame configure -$option $options(-column$option)
01178     }
01179     }
01180 
01181     # geometry propagation must be off so we can control the size
01182     # of the listbox by setting the size of the containing frame
01183     pack propagate $frame off
01184 
01185     pack $label   -side top -fill x -expand n
01186     pack $listbox -side top -fill both -expand y -pady 2
01187 
01188     # any events that happen in the listbox gets handled by the class
01189     # bindings. This has the unfortunate side effect 
01190     bindtags $listbox [list $w Mclistbox all]
01191 
01192     # return a list of the widgets we created.
01193     return [list $frame $listbox $label]
01194 }
01195 
01196 }
01197 
01198 namespace ::mclistbox {
01199 
01200 ret  {::mclistbox::ResizeEvent} (type w , type type , type widget , type x , type X , type Y) {
01201     upvar ::mclistbox::${w}::widgets       widgets
01202     upvar ::mclistbox::${w}::options       options
01203     upvar ::mclistbox::${w}::misc          misc
01204     upvar ::mclistbox::${w}::columnID      columnID
01205 
01206     # if the widget doesn't allow resizable cursors, there's
01207     # nothing here to do...
01208     if {!$options(-resizablecolumns)} {
01209     return
01210     }
01211 
01212     # this lets us keep track of some internal state while
01213     # the user is dragging the mouse
01214     variable drag
01215 
01216     # this lets us define a small window around the edges of
01217     # the column. 
01218     set threshold [expr {$options(-labelborderwidth) + 4}]
01219 
01220     # this is what we use for the "this is resizable" cursor
01221     set resizeCursor sb_h_double_arrow
01222 
01223     # if we aren't over an area that we care about, bail.
01224     if {![info exists columnID($widget)]} {
01225     return
01226     }
01227 
01228     # id refers to the column name
01229     set id $columnID($widget)
01230 
01231     switch $type {
01232 
01233     buttonpress {
01234         # we will do all the work of initiating a drag only if
01235         # the cursor is not the defined cursor. In theory this
01236         # will only be the case if the mouse moves over the area
01237         # in which a drag can happen.
01238         if {[$widgets(label$id) cget -cursor] == $resizeCursor} {
01239         if {$x <= $threshold} {
01240             set lid [::mclistbox::FindResizableNeighbor $w $id left]
01241             if {$lid == ""} return
01242             set drag(leftFrame)  $widgets(frame$lid)
01243             set drag(rightFrame) $widgets(frame$id)
01244 
01245             set drag(leftListbox)  $widgets(listbox$lid)
01246             set drag(rightListbox) $widgets(listbox$id)
01247 
01248         } else {
01249             set rid [::mclistbox::FindResizableNeighbor $w $id right]
01250             if {$rid == ""} return
01251             set drag(leftFrame)  $widgets(frame$id)
01252             set drag(rightFrame) $widgets(frame$rid)
01253 
01254             set drag(leftListbox)  $widgets(listbox$id)
01255             set drag(rightListbox) $widgets(listbox$rid)
01256 
01257         }
01258         
01259 
01260         set drag(leftWidth)  [winfo width $drag(leftFrame)]
01261         set drag(rightWidth) [winfo width $drag(rightFrame)]
01262 
01263         # it seems to be a fact that windows can never be 
01264         # less than one pixel wide. So subtract that one pixel
01265         # from our max deltas...
01266         set drag(maxDelta)   [expr {$drag(rightWidth) - 1}]
01267         set drag(minDelta)  -[expr {$drag(leftWidth) - 1}]
01268 
01269         set drag(x) $X
01270         }
01271     }
01272 
01273     motion {
01274         if {[info exists drag(x)]} {return}
01275 
01276         # this is just waaaaay too much work for a motion 
01277         # event, IMO.
01278 
01279         set resizable 0
01280 
01281         # is the column the user is over resizable?
01282         if {!$options($id:-resizable)} {return}
01283 
01284         # did the user click on the left of a column? 
01285         if {$x < $threshold} {
01286         set leftColumn [::mclistbox::FindResizableNeighbor $w $id left]
01287         if {$leftColumn != ""} {
01288             set resizable 1
01289         }
01290 
01291         } elseif {$x > [winfo width $widget] - $threshold} {
01292         set rightColumn [::mclistbox::FindResizableNeighbor $w $id  right]
01293         if {$rightColumn != ""} {
01294             set resizable 1
01295         }
01296         }
01297 
01298         # if it's resizable, change the cursor 
01299         set cursor [$widgets(label$id) cget -cursor]
01300         if {$resizable && $cursor != $resizeCursor} {
01301         $widgets(label$id) configure -cursor $resizeCursor
01302 
01303         } elseif {!$resizable && $cursor == $resizeCursor} {
01304         $widgets(label$id) configure -cursor $options(-cursor)
01305         }
01306     }
01307 
01308     drag {
01309         # if the state is set up, do the drag...
01310         if {[info exists drag(x)]} {
01311 
01312         set delta [expr {$X - $drag(x)}]
01313         if {$delta >= $drag(maxDelta)} {
01314             set delta $drag(maxDelta)
01315 
01316         } elseif {$delta <= $drag(minDelta)} {
01317             set delta $drag(minDelta)
01318         }
01319 
01320         set lwidth [expr {$drag(leftWidth) + $delta}]
01321         set rwidth [expr {$drag(rightWidth) - $delta}]
01322 
01323         $drag(leftFrame)   configure -width $lwidth
01324         $drag(rightFrame)  configure -width $rwidth
01325 
01326         }
01327     }
01328 
01329     buttonrelease {
01330         set fillColumnID $options(-fillcolumn)
01331         if {[info exists drag(x)] && $fillColumnID != {}} {
01332         set fillColumnFrame $widgets(frame$fillColumnID)
01333         if {[string compare $drag(leftFrame) $fillColumnFrame] == 0  || [string compare $drag(rightFrame) $fillColumnFrame] == 0} {
01334             set width [$fillColumnFrame cget -width]
01335             set misc(minFillColumnSize) $width
01336         }
01337         set misc(min-$drag(leftFrame))  [$drag(leftFrame) cget -width]
01338         set misc(min-$drag(rightFrame)) [$drag(rightFrame) cget -width]
01339         }
01340 
01341         # reset the state and the cursor
01342         catch {unset drag}
01343         $widgets(label$id) configure -cursor $options(-cursor)
01344 
01345     }
01346     }
01347 }
01348 
01349 }
01350 
01351 namespace ::mclistbox {
01352 
01353 ret  {::mclistbox::SelectionHandler} (type w , type type , optional offset ={) {length {}}} {
01354     upvar ::mclistbox::${w}::options   options
01355     upvar ::mclistbox::${w}::misc      misc
01356     upvar ::mclistbox::${w}::widgets   widgets
01357 
01358     switch -exact $type {
01359 
01360     own {
01361         selection own  -command [list ::mclistbox::SelectionHandler $w lose]  -selection PRIMARY  $w
01362     }
01363 
01364     lose {
01365         if {$options(-exportselection)} {
01366         foreach id $misc(columns) {
01367             $widgets(listbox$id) selection clear 0 end
01368         }
01369         }
01370     }
01371 
01372     get {
01373          end =  [expr {$length + $off - 1}]
01374 
01375         set =  column [lindex $misc(columns) 0]
01376          curselection =  [$widgets(listbox$column) curselection]
01377 
01378         /*  this is really, really slow (relatively speaking).*/
01379         /*  but the only way I can think of to speed this up*/
01380         /*  is to duplicate all the data in our hidden listbox,*/
01381         /*  which I really don't want to do because of memory*/
01382         /*  considerations.*/
01383          data =  ""
01384         foreach index $curselection {
01385          rowdata =  [join [::mclistbox::WidgetProc-get $w $index]  "\t"]
01386         lappend data $rowdata
01387         }
01388          data =  [join $data "\n"]
01389         return [string range $data $off $end = ]
01390     }
01391 
01392     }
01393 }
01394 
01395 }
01396 
01397 namespace ::mclistbox {
01398 
01399 ret  {::mclistbox::SetBindings} (type w) {
01400     upvar ::mclistbox::${w}::widgets widgets
01401     upvar ::mclistbox::${w}::options options
01402     upvar ::mclistbox::${w}::misc    misc
01403 
01404     # we must do this so that the columns fill the text widget in
01405     # the y direction
01406     bind $widgets(text) <Configure>  [list ::mclistbox::AdjustColumns $w %h]
01407 }
01408 
01409 }
01410 
01411 namespace ::mclistbox {
01412 
01413 ret  {::mclistbox::SetClassBindings} () {
01414     # this allows us to clean up some things when we go away
01415     bind Mclistbox <Destroy> [list ::mclistbox::DestroyHandler %W]
01416 
01417     # steal all of the standard listbox bindings. Note that if a user
01418     # clicks in a column, %W will return that column. This is bad,
01419     # so we have to make a substitution in all of the bindings to
01420     # compute the real widget name (ie: the name of the topmost 
01421     # frame)
01422     foreach event [bind Listbox] {
01423     set binding [bind Listbox $event]
01424     regsub -all {%W} $binding {[::mclistbox::convert %W -W]} binding
01425     regsub -all {%x} $binding {[::mclistbox::convert %W -x %x]} binding
01426     regsub -all {%y} $binding {[::mclistbox::convert %W -y %y]} binding
01427     bind Mclistbox $event $binding
01428     }
01429 
01430     # these define bindings for the column labels for resizing. Note
01431     # that we need both the name of this widget (calculated by $this)
01432     # as well as the specific widget that the event occured over.
01433     # Also note that $this is a constant string that gets evaluated
01434     # when the binding fires.
01435     # What a pain.
01436     set this {[::mclistbox::convert %W -W]}
01437     bind MclistboxMouseBindings <ButtonPress-1>  "::mclistbox::ResizeEvent $this buttonpress %W %x %X %Y"
01438     bind MclistboxMouseBindings <ButtonRelease-1>  "::mclistbox::ResizeEvent $this buttonrelease %W %x %X %Y"
01439     bind MclistboxMouseBindings <Enter>  "::mclistbox::ResizeEvent $this motion %W %x %X %Y"
01440     bind MclistboxMouseBindings <Motion>  "::mclistbox::ResizeEvent $this motion %W %x %X %Y"
01441     bind MclistboxMouseBindings <B1-Motion>  "::mclistbox::ResizeEvent $this drag %W %x %X %Y"
01442 }
01443 
01444 }
01445 
01446 namespace ::mclistbox {
01447 
01448 ret  {::mclistbox::UpdateScrollbars} (type w) {
01449     upvar ::mclistbox::${w}::widgets widgets
01450     upvar ::mclistbox::${w}::options options
01451     upvar ::mclistbox::${w}::misc    misc
01452 
01453     if {![winfo ismapped $w]} {
01454     catch {unset misc(afterid)}
01455     return
01456     }
01457 
01458     update idletasks
01459     if {[llength $misc(columns)] > 0} {
01460     if {[string length $options(-yscrollcommand)] != 0} {
01461         set col0 [lindex $misc(columns) 0]
01462         set yview [$widgets(listbox$col0) yview]
01463         eval $options(-yscrollcommand) $yview
01464     }
01465 
01466     if {[string length $options(-xscrollcommand)] != 0} {
01467         set col0 [lindex $misc(columns) 0]
01468         set xview [$widgets(text) xview]
01469         eval $options(-xscrollcommand) $xview
01470     }
01471     }
01472     catch {unset misc(afterid)}
01473 }
01474 
01475 }
01476 
01477 namespace ::mclistbox {
01478 
01479 ret  {::mclistbox::WidgetProc} (type w , type command , type args) {
01480     variable widgetOptions
01481 
01482     upvar ::mclistbox::${w}::widgets   widgets
01483     upvar ::mclistbox::${w}::options   options
01484     upvar ::mclistbox::${w}::misc      misc
01485     upvar ::mclistbox::${w}::columnID  columnID
01486 
01487     set command [::mclistbox::Canonize $w command $command]
01488 
01489     # some commands have subcommands. We'll check for that here 
01490     # and mung the command and args so that we can treat them as 
01491     # distinct commands in the following switch statement
01492     if {[string compare $command "column"] == 0} {
01493     set subcommand [::mclistbox::Canonize $w "column command"  [lindex $args 0]]
01494     set command "$command-$subcommand"
01495     set args [lrange $args 1 end]
01496 
01497     } elseif {[string compare $command "label"] == 0} {
01498     set subcommand [::mclistbox::Canonize $w "label command"  [lindex $args 0]]
01499     set command "$command-$subcommand"
01500     set args [lrange $args 1 end]
01501     }
01502 
01503     set result ""
01504     catch {unset priorSelection}
01505 
01506     # here we go. Error checking be damned!
01507     switch $command {
01508     xview {
01509         # note that at present, "xview <index>" is broken. I'm
01510         # not even sure how to do it. Unless I attach our hidden
01511         # listbox to the scrollbar and use it. Hmmm..... I'll
01512         # try that later. (FIXME)
01513         set result [eval {$widgets(text)} xview $args]
01514         InvalidateScrollbars $w
01515     }
01516 
01517     yview {
01518         if {[llength $args] == 0} {
01519         # length of zero means to fetch the yview; we can
01520         # get this from a single listbox
01521         set result [$widgets(hiddenListbox) yview]
01522 
01523         } else {
01524 
01525         # if it's one argument, it's an index. We'll pass that 
01526         # index through the index command to properly translate
01527         # @x,y indicies, and place the value back in args
01528         if {[llength $args] == 1} {
01529             set index [::mclistbox::MassageIndex $w [lindex $args 0]]
01530             set args [list $index]
01531         }
01532 
01533         # run the yview command on every column.
01534         foreach id $misc(columns) {
01535             eval {$widgets(listbox$id)} yview $args
01536         }
01537         eval {$widgets(hiddenListbox)} yview $args
01538 
01539         InvalidateScrollbars $w
01540 
01541         set result ""
01542         }
01543     }
01544 
01545     activate {
01546         if {[llength $args] != 1} {
01547         return -code error "wrong \# of args: should be $w activate index"
01548         }
01549         set index [::mclistbox::MassageIndex $w [lindex $args 0]]
01550 
01551         foreach id $misc(columns) {
01552         $widgets(listbox$id) activate $index
01553         }
01554         set result ""
01555     }
01556 
01557     bbox {
01558         if {[llength $args] != 1} {
01559         return -code error "wrong \# of args: should be $w bbox index"
01560         }
01561         # get a real index. This will adjust @x,y indicies
01562         # to account for the label, if any.
01563         set index [::mclistbox::MassageIndex $w [lindex $args 0]]
01564 
01565         set id [lindex $misc(columns) 0]
01566 
01567         # we can get the x, y, and height from the first 
01568         # column.
01569         set bbox [$widgets(listbox$id) bbox $index]
01570         if {[string length $bbox] == 0} {return ""}
01571 
01572         foreach {x y w h} $bbox {}
01573         
01574         # the x and y coordinates have to be adjusted for the
01575         # fact that the listbox is inside a frame, and the 
01576         # frame is inside a text widget. All of those add tiny
01577         # offsets. Feh.
01578         incr y [winfo y $widgets(listbox$id)]
01579         incr y [winfo y $widgets(frame$id)]
01580         incr x [winfo x $widgets(listbox$id)]
01581         incr x [winfo x $widgets(frame$id)]
01582 
01583         # we can get the width by looking at the relative x 
01584         # coordinate of the right edge of the last column
01585         set id [lindex $misc(columns) end]
01586         set w [expr {[winfo width $widgets(frame$id)] +  [winfo x $widgets(frame$id)]}]
01587         set bbox [list $x $y [expr {$x + $w}] $h]
01588         set result $bbox
01589     }
01590 
01591     label-bind {
01592         # we are just too clever for our own good. (that's a 
01593         # polite way of saying this is more complex than it
01594         # needs to be)
01595 
01596         set id [lindex $args 0]
01597         set index [CheckColumnID $w $id]
01598 
01599         set args [lrange $args 1 end]
01600         if {[llength $args] == 0} {
01601         set result [bind $widgets(label$id)]
01602         } else {
01603 
01604         # when we create a binding, we'll actually have the 
01605         # binding run our own command with the user's command
01606         # as an argument. This way we can do some sanity checks
01607         # before running the command. So, when querying a binding
01608         # we need to only return the user's code
01609         set sequence [lindex $args 0]
01610         if {[llength $args] == 1} {
01611             set result [lindex [bind $widgets(label$id) $sequence] end]
01612         } else {
01613         
01614             # replace %W with our toplevel frame, then
01615             # do the binding
01616             set code [lindex $args 1]
01617             regsub -all {%W} $code $w code
01618             
01619             set result [bind $widgets(label$id) $sequence  [list ::mclistbox::LabelEvent $w $id $code]]
01620         }
01621         }
01622     }
01623 
01624     column-add {
01625         eval ::mclistbox::Column-add {$w} $args
01626         AdjustColumns $w
01627         set result ""
01628     }
01629 
01630     column-delete {
01631         foreach id $args {
01632         set index [CheckColumnID $w $id]
01633 
01634         # remove the reference from our list of columns
01635         set misc(columns) [lreplace $misc(columns) $index $index]
01636 
01637         # whack the widget
01638         destroy $widgets(frame$id)
01639 
01640         # clear out references to the individual widgets
01641         unset widgets(frame$id)
01642         unset widgets(listbox$id)
01643         unset widgets(label$id)
01644         }
01645         InvalidateScrollbars $w
01646         set result ""
01647     }
01648 
01649     column-cget {
01650         if {[llength $args] != 2} {
01651         return -code error "wrong # of args: should be \"$w column cget name option\""
01652         }
01653         set id [::mclistbox::Canonize $w column [lindex $args 0]]
01654         set option [lindex $args 1]
01655         set data [::mclistbox::Column-configure $w $id $option]
01656         set result [lindex $data 4]
01657     }
01658 
01659     column-configure {
01660         set id [::mclistbox::Canonize $w column [lindex $args 0]]
01661         set args [lrange $args 1 end]
01662         set result [eval ::mclistbox::Column-configure {$w} {$id} $args]
01663     }
01664 
01665     column-names {
01666         if {[llength $args] != 0} {
01667         return -code error "wrong # of args: should be \"$w column names\""
01668         }
01669         set result $misc(columns)
01670     }
01671 
01672     column-nearest {
01673         if {[llength $args] != 1} {
01674         return -code error "wrong # of args: should be \"$w column nearest x\""
01675         }
01676 
01677         set x [lindex $args 0]
01678         set tmp [$widgets(text) index @$x,0]
01679         set tmp [split $tmp "."]
01680         set index [lindex $tmp 1]
01681 
01682         set result [lindex $misc(columns) $index]
01683     }
01684 
01685     cget {
01686         if {[llength $args] != 1} {
01687         return -code error "wrong # args: should be $w cget option"
01688         }
01689         set opt [::mclistbox::Canonize $w option [lindex $args 0]]
01690 
01691         set result $options($opt)
01692     }
01693 
01694 
01695     configure {
01696         set result [eval ::mclistbox::Configure {$w} $args]
01697 
01698     }
01699 
01700     curselection {
01701         set id [lindex $misc(columns) 0]
01702         set result [$widgets(listbox$id) curselection]
01703     }
01704 
01705     delete {
01706         if {[llength $args] < 1 || [llength $args] > 2} {
01707         return -code error "wrong \# of args: should be $w delete first ?last?"
01708         }
01709 
01710         # it's possible that the selection will change because
01711         # of something we do. So, grab the current selection before
01712         # we do anything. Just before returning we'll see if the
01713         # selection has changed. If so, we'll call our selectcommand
01714         if {$options(-selectcommand) != ""} {
01715         set col0 [lindex $misc(columns) 0]
01716         set priorSelection [$widgets(listbox$col0) curselection]
01717         }
01718 
01719         set index1 [::mclistbox::MassageIndex $w [lindex $args 0]]
01720         if {[llength $args] == 2} {
01721         set index2 [::mclistbox::MassageIndex $w [lindex $args 1]]
01722         } else {
01723         set index2 ""
01724         }
01725 
01726         # note we do an eval here to make index2 "disappear" if it
01727         # is set to an empty string.
01728         foreach id $misc(columns) {
01729         eval {$widgets(listbox$id)} delete $index1 $index2
01730         }
01731         eval {$widgets(hiddenListbox)} delete $index1 $index2
01732 
01733         InvalidateScrollbars $w
01734 
01735         set result ""
01736     }
01737 
01738     get {
01739         if {[llength $args] < 1 || [llength $args] > 2} {
01740         return -code error "wrong \# of args: should be $w get first ?last?"
01741         }
01742         set index1 [::mclistbox::MassageIndex $w [lindex $args 0]]
01743         if {[llength $args] == 2} {
01744         set index2 [::mclistbox::MassageIndex $w [lindex $args 1]]
01745         } else {
01746         set index2 ""
01747         }
01748 
01749         set result [eval ::mclistbox::WidgetProc-get {$w} $index1 $index2]
01750 
01751     }
01752 
01753     index {
01754 
01755         if {[llength $args] != 1} {
01756         return -code error "wrong \# of args: should be $w index index"
01757         }
01758 
01759         set index [::mclistbox::MassageIndex $w [lindex $args 0]]
01760         set id [lindex $misc(columns) 0]
01761 
01762         set result [$widgets(listbox$id) index $index]
01763     }
01764 
01765     insert {
01766         if {[llength $args] < 1} {
01767         return -code error "wrong \# of args: should be $w insert ?element  element...?"
01768         }
01769 
01770         # it's possible that the selection will change because
01771         # of something we do. So, grab the current selection before
01772         # we do anything. Just before returning we'll see if the
01773         # selection has changed. If so, we'll call our selectcommand
01774         if {$options(-selectcommand) != ""} {
01775         set col0 [lindex $misc(columns) 0]
01776         set priorSelection [$widgets(listbox$col0) curselection]
01777         }
01778 
01779         set index [::mclistbox::MassageIndex $w [lindex $args 0]]
01780 
01781         ::mclistbox::Insert $w $index [lrange $args 1 end]
01782 
01783         InvalidateScrollbars $w
01784         set result ""
01785     }
01786 
01787     nearest {
01788         if {[llength $args] != 1} {
01789         return -code error "wrong \# of args: should be $w nearest y"
01790         }
01791 
01792         # translate the y coordinate into listbox space
01793         set id [lindex $misc(columns) 0]
01794         set y [lindex $args 0]
01795         incr y -[winfo y $widgets(listbox$id)]
01796         incr y -[winfo y $widgets(frame$id)]
01797 
01798         set col0 [lindex $misc(columns) 0]
01799 
01800         set result [$widgets(listbox$col0) nearest $y]
01801     }
01802 
01803     scan {
01804         foreach {subcommand x y} $args {}
01805         switch $subcommand {
01806         mark {
01807             # we have to treat scrolling in x and y differently;
01808             # scrolling in the y direction affects listboxes and
01809             # scrolling in the x direction affects the text widget.
01810             # to facilitate that, we need to keep a local copy
01811             # of the scan mark.
01812             set misc(scanmarkx) $x
01813             set misc(scanmarky) $y
01814             
01815             # set the scan mark for each column
01816             foreach id $misc(columns) {
01817             $widgets(listbox$id) scan mark $x $y
01818             }
01819 
01820             # we can't use the x coordinate given us, since it 
01821             # is relative to whatever column we are over. So,
01822             # we'll just usr the results of [winfo pointerx].
01823             $widgets(text) scan mark [winfo pointerx $w]  $y
01824         }
01825         dragto {
01826             # we want the columns to only scan in the y direction,
01827             # so we'll force the x componant to remain constant
01828             foreach id $misc(columns) {
01829             $widgets(listbox$id) scan dragto $misc(scanmarkx) $y
01830             }
01831 
01832             # since the scan mark of the text widget was based
01833             # on the pointer location, so must be the x
01834             # coordinate to the dragto command. And since we
01835             # want the text widget to only scan in the x
01836             # direction, the y componant will remain constant
01837             $widgets(text) scan dragto  [winfo pointerx $w] $misc(scanmarky)
01838 
01839             # make sure the scrollbars reflect the changes.
01840             InvalidateScrollbars $w
01841         }
01842 
01843         set result ""
01844         }
01845     }
01846 
01847     see {
01848         if {[llength $args] != 1} {
01849         return -code error "wrong \# of args: should be $w see index"
01850         }
01851         set index [::mclistbox::MassageIndex $w [lindex $args 0]]
01852 
01853         foreach id $misc(columns) {
01854         $widgets(listbox$id) see $index
01855         }
01856         InvalidateScrollbars $w
01857         set result {}
01858     }
01859 
01860     selection {
01861         # it's possible that the selection will change because
01862         # of something we do. So, grab the current selection before
01863         # we do anything. Just before returning we'll see if the
01864         # selection has changed. If so, we'll call our selectcommand
01865         if {$options(-selectcommand) != ""} {
01866         set col0 [lindex $misc(columns) 0]
01867         set priorSelection [$widgets(listbox$col0) curselection]
01868         }
01869 
01870         set subcommand [lindex $args 0]
01871         set args [lrange $args 1 end]
01872 
01873         set prefix "wrong \# of args: should be $w"
01874         switch $subcommand {
01875         includes {
01876             if {[llength $args] != 1} {
01877             return -code error "$prefix selection $subcommand index"
01878             }
01879             set index [::mclistbox::MassageIndex $w [lindex $args 0]]
01880             set id [lindex $misc(columns) 0]
01881             set result [$widgets(listbox$id) selection includes $index]
01882         }
01883 
01884         set {
01885             switch [llength $args] {
01886             1 {
01887                 set index1 [::mclistbox::MassageIndex $w  [lindex $args 0]]
01888                 set index2 ""
01889             }
01890             2 {
01891                 set index1 [::mclistbox::MassageIndex $w  [lindex $args 0]]
01892                 set index2 [::mclistbox::MassageIndex $w  [lindex $args 1]]
01893             }
01894             default {
01895                 return -code error "$prefix selection clear first ?last?"
01896             }
01897             }
01898 
01899             if {$options(-exportselection)} {
01900             SelectionHandler $w own
01901             }
01902             if {$index1 != ""} {
01903             foreach id $misc(columns) {
01904                 eval {$widgets(listbox$id)} selection set  $index1 $index2
01905             }
01906             }
01907 
01908             set result ""
01909         }
01910 
01911         anchor {
01912             if {[llength $args] != 1} {
01913             return -code error "$prefix selection $subcommand index"
01914             }
01915             set index [::mclistbox::MassageIndex $w [lindex $args 0]]
01916 
01917             if {$options(-exportselection)} {
01918             SelectionHandler $w own
01919             }
01920             foreach id $misc(columns) {
01921             $widgets(listbox$id) selection anchor $index
01922             }
01923             set result ""
01924         }
01925 
01926         clear {
01927             switch [llength $args] {
01928             1 {
01929                 set index1 [::mclistbox::MassageIndex $w  [lindex $args 0]]
01930                 set index2 ""
01931             }
01932             2 {
01933                 set index1 [::mclistbox::MassageIndex $w  [lindex $args 0]]
01934                 set index2 [::mclistbox::MassageIndex $w  [lindex $args 1]]
01935             }
01936             default {
01937                 return -code error "$prefix selection clear first ?last?"
01938             }
01939             }
01940 
01941             if {$options(-exportselection)} {
01942             SelectionHandler $w own
01943             }
01944             foreach id $misc(columns) {
01945             eval {$widgets(listbox$id)} selection clear  $index1 $index2
01946             }
01947             set result ""
01948         }
01949         }
01950     }
01951 
01952     size {
01953         set id [lindex $misc(columns) 0]
01954         set result [$widgets(listbox$id) size]
01955     }
01956     }
01957 
01958     # if the user has a selectcommand defined and the selection changed,
01959     # run the selectcommand
01960     if {[info exists priorSelection] && $options(-selectcommand) != ""} {
01961     set column [lindex $misc(columns) 0]
01962     set currentSelection [$widgets(listbox$column) curselection]
01963     if {[string compare $priorSelection $currentSelection] != 0} {
01964         # this logic keeps us from getting into some sort of
01965         # infinite loop of the selectcommand changes the selection
01966         # (not particularly well tested, but it seems like the
01967         # right thing to do...)
01968         if {![info exists misc(skipRecursiveCall)]} {
01969         set misc(skipRecursiveCall) 1
01970         uplevel \#0 $options(-selectcommand) $currentSelection
01971         catch {unset misc(skipRecursiveCall)}
01972         }
01973     }
01974     }
01975 
01976     return $result
01977 }
01978 
01979 }
01980 
01981 namespace ::mclistbox {
01982 
01983 ret  {::mclistbox::WidgetProc-get} (type w , type args) {
01984     upvar ::mclistbox::${w}::widgets widgets
01985     upvar ::mclistbox::${w}::options options
01986     upvar ::mclistbox::${w}::misc    misc
01987 
01988     set returnType "list"
01989     # the listbox "get" command returns different things
01990     # depending on whether it has one or two args. Internally
01991     # we *always* want a valid list, so we'll force a second
01992     # arg which in turn forces the listbox to return a list,
01993     # even if its a list of one element
01994     if {[llength $args] == 1} {
01995     lappend args [lindex $args 0]
01996     set returnType "listOfLists"
01997     }
01998 
01999     # get all the data from each column
02000     foreach id $misc(columns) {
02001     set data($id) [eval {$widgets(listbox$id)} get $args]
02002     }
02003 
02004     # now join the data together one row at a time. Ugh.
02005     set result {}
02006     set rows [llength $data($id)]
02007     for {set i 0} {$i < $rows} {incr i} {
02008     set this {}
02009     foreach column $misc(columns) {
02010         lappend this [lindex $data($column) $i]
02011     }
02012     lappend result $this
02013     }
02014     
02015     # now to unroll the list if necessary. If the user gave
02016     # us only one indicie we want to return a single list
02017     # of values. If they gave use two indicies we want to return
02018     # a list of lists.
02019     if {[string compare $returnType "list"] == 0} {
02020     return $result
02021     } else {
02022     return [lindex $result 0]
02023     }
02024 }
02025 
02026 }
02027 
02028 namespace ::mclistbox {
02029 
02030 ret  {::mclistbox::convert} (type w , type args) {
02031     set result {}
02032     if {![winfo exists $w]} {
02033     return -code error "window \"$w\" doesn't exist"
02034     }
02035 
02036     while {[llength $args] > 0} {
02037     set option [lindex $args 0]
02038     set args [lrange $args 1 end]
02039 
02040     switch -exact -- $option {
02041         -x {
02042         set value [lindex $args 0]
02043         set args [lrange $args 1 end]
02044         set win $w
02045         while {[winfo class $win] != "Mclistbox"} {
02046             incr value [winfo x $win]
02047             set win [winfo parent $win]
02048             if {$win == "."} break
02049         }
02050         lappend result $value
02051         }
02052 
02053         -y {
02054         set value [lindex $args 0]
02055         set args [lrange $args 1 end]
02056         set win $w
02057         while {[winfo class $win] != "Mclistbox"} {
02058             incr value [winfo y $win]
02059             set win [winfo parent $win]
02060             if {$win == "."} break
02061         }
02062         lappend result $value
02063         }
02064 
02065         -w -
02066         -W {
02067         set win $w
02068         while {[winfo class $win] != "Mclistbox"} {
02069             set win [winfo parent $win]
02070             if {$win == "."} break;
02071         }
02072         lappend result $win
02073         }
02074     }
02075     }
02076     return $result
02077 }
02078 
02079 }
02080 
02081 namespace ::mclistbox {
02082 
02083 ret  {::mclistbox::mclistbox} (type args) {
02084     variable widgetOptions
02085 
02086     # perform a one time initialization
02087     if {![info exists widgetOptions]} {
02088       __mclistbox_Setup
02089     Init
02090     }
02091 
02092     # make sure we at least have a widget name
02093     if {[llength $args] == 0} {
02094     return -code error "wrong # args: should be \"mclistbox pathName ?options?\""
02095     }
02096 
02097     # ... and make sure a widget doesn't already exist by that name
02098     if {[winfo exists [lindex $args 0]]} {
02099     return -code error "window name \"[lindex $args 0]\" already exists"
02100     }
02101 
02102     # and check that all of the args are valid
02103     foreach {name value} [lrange $args 1 end] {
02104     Canonize [lindex $args 0] option $name
02105     }
02106 
02107     # build it...
02108     set w [eval Build $args]
02109 
02110     # set some bindings...
02111     SetBindings $w
02112 
02113     # and we are done!
02114     return $w
02115 }
02116 
02117 }
02118 
02119 ret  {__mclistbox_Setup} () {
02120     namespace eval ::mclistbox {
02121 
02122         # this is the public interface
02123         namespace export mclistbox
02124 
02125         # these contain references to available options
02126         variable widgetOptions
02127         variable columnOptions
02128 
02129         # these contain references to available commands and subcommands
02130         variable widgetCommands
02131         variable columnCommands
02132         variable labelCommands
02133     }
02134 }
02135 
02136 ret  {vTcl:DefineAlias} (type target , type alias , type widgetProc , type top_, type or_, type alias , type cmdalias) {
02137     global widget
02138 
02139     set widget($alias) $target
02140     set widget(rev,$target) $alias
02141 
02142     if {$cmdalias} {
02143         interp alias {} $alias {} $widgetProc $target
02144     }
02145 
02146     if {$top_or_alias != ""} {
02147         set widget($top_or_alias,$alias) $target
02148 
02149         if {$cmdalias} {
02150             interp alias {} $top_or_alias.$alias {} $widgetProc $target
02151         }
02152     }
02153 }
02154 
02155 ret  {vTcl:Toplevel:WidgetProc} (type w , type args) {
02156     if {[llength $args] == 0} {
02157         return -code error "wrong # args: should be \"$w option ?arg arg ...?\""
02158     }
02159 
02160     ## The first argument is a switch, they must be doing a configure.
02161     if {[string index $args 0] == "-"} {
02162         set command configure
02163 
02164         ## There's only one argument, must be a cget.
02165         if {[llength $args] == 1} {
02166             set command cget
02167         }
02168     } else {
02169         set command [lindex $args 0]
02170         set args [lrange $args 1 end]
02171     }
02172 
02173     switch -- $command {
02174         "hide" -
02175         "Hide" {
02176             Window hide $w
02177         }
02178 
02179         "show" -
02180         "Show" {
02181             Window show $w
02182         }
02183 
02184         "ShowModal" {
02185             Window show $w
02186             raise $w
02187             grab $w
02188             tkwait window $w
02189             grab release $w
02190         }
02191 
02192         default {
02193             eval $w $command $args
02194         }
02195     }
02196 }
02197 
02198 ret  {vTcl:WidgetProc} (type w , type args) {
02199     if {[llength $args] == 0} {
02200         return -code error "wrong # args: should be \"$w option ?arg arg ...?\""
02201     }
02202 
02203     ## The first argument is a switch, they must be doing a configure.
02204     if {[string index $args 0] == "-"} {
02205         set command configure
02206 
02207         ## There's only one argument, must be a cget.
02208         if {[llength $args] == 1} {
02209             set command cget
02210         }
02211     } else {
02212         set command [lindex $args 0]
02213         set args [lrange $args 1 end]
02214     }
02215 
02216     eval $w $command $args
02217 }
02218 }
02219 
02220 if {[info exists vTcl(sourcing)]} {
02221 ret  vTcl:project:info () {
02222     namespace eval ::widgets::.gui {
02223         array set save {}
02224     }
02225     namespace eval ::widgets::.gui.cpd17 {
02226         array set save {-background 1 -borderwidth 1 -height 1 -relief 1 -width 1}
02227     }
02228     namespace eval ::widgets::.gui.cpd17.01 {
02229         array set save {-anchor 1 -borderwidth 1 -font 1 -menu 1 -padx 1 -pady 1 -text 1 -underline 1 -width 1}
02230     }
02231     namespace eval ::widgets::.gui.cpd17.01.02 {
02232         array set save {-cursor 1 -tearoff 1}
02233     }
02234     namespace eval ::widgets::.gui.cpd17.settingbutton {
02235         array set save {-font 1 -menu 1 -padx 1 -pady 1 -text 1 -underline 1}
02236     }
02237     namespace eval ::widgets::.gui.cpd17.settingbutton.m {
02238         array set save {}
02239     }
02240     namespace eval ::widgets::.gui.pr {
02241         array set save {-borderwidth 1 -height 1 -relief 1 -width 1}
02242     }
02243     namespace eval ::widgets::.gui.pr.tools {
02244         array set save {-height 1 -width 1}
02245     }
02246     namespace eval ::widgets::.gui.pr.tools.infolabel {
02247         array set save {-anchor 1 -background 1 -borderwidth 1 -font 1 -relief 1 -text 1}
02248     }
02249     namespace eval ::widgets::.gui.pr.tools.resizescrl {
02250         array set save {-bigincrement 1 -borderwidth 1 -command 1 -from 1 -highlightthickness 1 -orient 1 -relief 1 -resolution 1 -showvalue 1 -sliderlength 1 -tickinterval 1 -to 1 -troughcolor 1 -variable 1 -width 1}
02251     }
02252     namespace eval ::widgets::.gui.pr.toolframe {
02253         array set save {-borderwidth 1 -height 1 -relief 1 -width 1}
02254     }
02255     namespace eval ::widgets::.gui.pr.toolframe.toolsbox {
02256         array set save {-background 1 -height 1 -highlightbackground 1 -highlightcolor 1 -relief 1 -width 1}
02257     }
02258     namespace eval ::widgets::.gui.pr.toolframe.toolsbox.fobt {
02259         array set save {-borderwidth 1 -command 1 -text 1}
02260     }
02261     namespace eval ::widgets::.gui.pr.toolframe.toolsbox.but34 {
02262         array set save {-command 1 -padx 1 -text 1}
02263     }
02264     namespace eval ::widgets::.gui.pr.toolframe.toolsbox.but35 {
02265         array set save {-command 1 -padx 1 -text 1}
02266     }
02267     namespace eval ::widgets::.gui.pr.toolframe.toolsbox.but32 {
02268         array set save {-anchor 1 -borderwidth 1 -command 1}
02269     }
02270     namespace eval ::widgets::.gui.pr.toolframe.entityframe {
02271         array set save {-borderwidth 1 -height 1 -width 1}
02272     }
02273     namespace eval ::widgets::.gui.pr.toolframe.entityframe.comprb {
02274         array set save {-anchor 1 -borderwidth 1 -command 1 -font 1 -highlightthickness 1 -padx 1 -pady 1 -selectcolor 1 -text 1 -value 1 -variable 1 -width 1}
02275     }
02276     namespace eval ::widgets::.gui.pr.toolframe.entityframe.ficrb {
02277         array set save {-anchor 1 -command 1 -font 1 -highlightthickness 1 -padx 1 -pady 1 -text 1 -value 1 -variable 1}
02278     }
02279     namespace eval ::widgets::.gui.pr.toolframe.entityframe.cnxrb {
02280         array set save {-anchor 1 -command 1 -font 1 -highlightthickness 1 -padx 1 -pady 1 -text 1 -value 1 -variable 1}
02281     }
02282     namespace eval ::widgets::.gui.pr.toolframe.entityframe.fieldsrb {
02283         array set save {-anchor 1 -command 1 -font 1 -highlightthickness 1 -padx 1 -pady 1 -text 1 -value 1 -variable 1 -width 1}
02284     }
02285     namespace eval ::widgets::.gui.pr.toolframe.buttonfrc {
02286         array set save {-height 1 -width 1}
02287     }
02288     namespace eval ::widgets::.gui.pr.toolframe.buttonfrc.editb {
02289         array set save {-command 1 -font 1 -padx 1 -pady 1 -text 1}
02290     }
02291     namespace eval ::widgets::.gui.pr.toolframe.buttonfrc.deleteb {
02292         array set save {-command 1 -font 1 -padx 1 -pady 1 -state 1 -text 1}
02293     }
02294     namespace eval ::widgets::.gui.pr.toolframe.cpd18 {
02295         array set save {-borderwidth 1 -height 1 -relief 1 -width 1}
02296     }
02297     namespace eval ::widgets::.gui.pr.toolframe.cpd18.02 {
02298         array set save {-borderwidth 1 -command 1 -orient 1 -width 1}
02299     }
02300     namespace eval ::widgets::.gui.pr.toolframe.cpd18.03 {
02301         array set save {-borderwidth 1 -command 1 -width 1}
02302     }
02303     namespace eval ::widgets::.gui.pr.toolframe.cpd18.01 {
02304         array set save {-background 1 -borderwidth 1 -font 1 -height 1 -labelanchor 1 -labelborderwidth 1 -labelrelief 1 -relief 1 -selectborderwidth 1 -selectmode 1 -width 1 -xscrollcommand 1 -yscrollcommand 1}
02305     }
02306     namespace eval ::widgets::.gui.pr.zoomframe {
02307         array set save {-height 1 -relief 1 -width 1}
02308     }
02309     namespace eval ::widgets::.gui.pr.zoomframe.viewframe {
02310         array set save {-borderwidth 1 -height 1 -relief 1 -width 1}
02311     }
02312     namespace eval ::widgets::.gui.pr.zoomframe.viewframe.viewcommcheck {
02313         array set save {-command 1 -font 1 -pady 1 -text 1 -variable 1}
02314     }
02315     namespace eval ::widgets::.gui.pr.zoomframe.zm {
02316         array set save {-borderwidth 1 -command 1 -padx 1 -pady 1 -relief 1 -text 1}
02317     }
02318     namespace eval ::widgets::.gui.pr.zoomframe.zp {
02319         array set save {-borderwidth 1 -command 1 -padx 1 -pady 1 -relief 1 -text 1}
02320     }
02321     namespace eval ::widgets::.gui.pr.cpd22 {
02322         array set save {-borderwidth 1 -height 1 -relief 1 -width 1}
02323     }
02324     namespace eval ::widgets::.gui.pr.cpd22.01 {
02325         array set save {-borderwidth 1 -command 1 -jump 1 -orient 1 -width 1}
02326     }
02327     namespace eval ::widgets::.gui.pr.cpd22.02 {
02328         array set save {-borderwidth 1 -command 1 -jump 1 -width 1}
02329     }
02330     namespace eval ::widgets::.gui.pr.cpd22.03 {
02331         array set save {-background 1 -borderwidth 1 -closeenough 1 -height 1 -relief 1 -scrollregion 1 -width 1 -xscrollcommand 1 -yscrollcommand 1}
02332     }
02333     namespace eval ::widgets::.ballon_object {
02334         array set save {-borderwidth 1}
02335     }
02336     namespace eval ::widgets::.ballon_object.text {
02337         array set save {-background 1 -font 1 -foreground 1}
02338     }
02339     namespace eval ::widgets_bindings {
02340         set tagslist {}
02341     }
02342 }
02343 }
02344 /* */
02345 /*  USER DEFINED PROCEDURES*/
02346 /* */
02347 
02348 namespace ::combobox {
02349 
02350 ret  {::combobox::Build} (type w , type args) {
02351 variable widgetOptions
02352 
02353     if {[winfo exists $w]} {
02354     error "window name \"$w\" already exists"
02355     }
02356 
02357     # create the namespace for this instance, and define a few
02358     # variables
02359     namespace eval ::combobox::$w {
02360 
02361     variable ignoreTrace 0
02362     variable oldFocus    {}
02363     variable oldGrab     {}
02364     variable oldValue    {}
02365     variable options
02366     variable this
02367     variable widgets
02368 
02369     set widgets(foo) foo  ;# coerce into an array
02370     set options(foo) foo  ;# coerce into an array
02371 
02372     unset widgets(foo)
02373     unset options(foo)
02374     }
02375 
02376     # import the widgets and options arrays into this proc so
02377     # we don't have to use fully qualified names, which is a
02378     # pain.
02379     upvar ::combobox::${w}::widgets widgets
02380     upvar ::combobox::${w}::options options
02381 
02382     # this is our widget -- a frame of class Combobox. Naturally,
02383     # it will contain other widgets. We create it here because
02384     # we need it in order to set some default options.
02385     set widgets(this)   [frame  $w -class Combobox -takefocus 0]
02386     set widgets(entry)  [entry  $w.entry -takefocus 1]
02387     set widgets(button) [label  $w.button -takefocus 0]
02388 
02389     # this defines all of the default options. We get the
02390     # values from the option database. Note that if an array
02391     # value is a list of length one it is an alias to another
02392     # option, so we just ignore it
02393     foreach name [array names widgetOptions] {
02394     if {[llength $widgetOptions($name)] == 1} continue
02395 
02396     set optName  [lindex $widgetOptions($name) 0]
02397     set optClass [lindex $widgetOptions($name) 1]
02398 
02399     set value [option get $w $optName $optClass]
02400     set options($name) $value
02401     }
02402 
02403     # a couple options aren't available in earlier versions of
02404     # tcl, so we'll set them to sane values. For that matter, if
02405     # they exist but are empty, set them to sane values.
02406     if {[string length $options(-disabledforeground)] == 0} {
02407         set options(-disabledforeground) $options(-foreground)
02408     }
02409     if {[string length $options(-disabledbackground)] == 0} {
02410         set options(-disabledbackground) $options(-background)
02411     }
02412 
02413     # if -value is set to null, we'll remove it from our
02414     # local array. The assumption is, if the user sets it from
02415     # the option database, they will set it to something other
02416     # than null (since it's impossible to determine the difference
02417     # between a null value and no value at all).
02418     if {[info exists options(-value)]  && [string length $options(-value)] == 0} {
02419     unset options(-value)
02420     }
02421 
02422     # we will later rename the frame's widget proc to be our
02423     # own custom widget proc. We need to keep track of this
02424     # new name, so we'll define and store it here...
02425     set widgets(frame) ::combobox::${w}::$w
02426 
02427     # gotta do this sooner or later. Might as well do it now
02428     pack $widgets(entry)  -side left  -fill both -expand yes
02429     pack $widgets(button) -side right -fill y    -expand no
02430 
02431     # I should probably do this in a catch, but for now it's
02432     # good enough... What it does, obviously, is put all of
02433     # the option/values pairs into an array. Make them easier
02434     # to handle later on...
02435     array set options $args
02436 
02437     # now, the dropdown list... the same renaming nonsense
02438     # must go on here as well...
02439     set widgets(dropdown)   [toplevel  $w.top]
02440     set widgets(listbox) [listbox   $w.top.list]
02441     set widgets(vsb)     [scrollbar $w.top.vsb]
02442 
02443     pack $widgets(listbox) -side left -fill both -expand y
02444 
02445     # fine tune the widgets based on the options (and a few
02446     # arbitrary values...)
02447 
02448     # NB: we are going to use the frame to handle the relief
02449     # of the widget as a whole, so the entry widget will be
02450     # flat. This makes the button which drops down the list
02451     # to appear "inside" the entry widget.
02452 
02453     $widgets(vsb) configure  -command "$widgets(listbox) yview"  -highlightthickness 0
02454 
02455     $widgets(button) configure  -highlightthickness 0  -borderwidth 1  -relief raised  -width [expr {[winfo reqwidth $widgets(vsb)] - 2}]
02456 
02457     $widgets(entry) configure  -borderwidth 0  -relief flat  -highlightthickness 0
02458 
02459     $widgets(dropdown) configure  -borderwidth 1  -relief sunken
02460 
02461     $widgets(listbox) configure  -selectmode browse  -background [$widgets(entry) cget -bg]  -yscrollcommand "$widgets(vsb) set"  -exportselection false  -borderwidth 0
02462 
02463 
02464 #    trace variable ::combobox::${w}::entryTextVariable w  #        [list ::combobox::EntryTrace $w]
02465     
02466     # do some window management foo on the dropdown window
02467     wm overrideredirect $widgets(dropdown) 1
02468     wm transient        $widgets(dropdown) [winfo toplevel $w]
02469     wm group            $widgets(dropdown) [winfo parent $w]
02470     wm resizable        $widgets(dropdown) 0 0
02471     wm withdraw         $widgets(dropdown)
02472 
02473     # this moves the original frame widget proc into our
02474     # namespace and gives it a handy name
02475     rename ::$w $widgets(frame)
02476 
02477     # now, create our widget proc. Obviously (?) it goes in
02478     # the global namespace. All combobox widgets will actually
02479     # share the same widget proc to cut down on the amount of
02480     # bloat.
02481     proc ::$w {command args}  "eval ::combobox::WidgetProc $w \$command \$args"
02482 
02483 
02484     # ok, the thing exists... let's do a bit more configuration.
02485     if {[catch "::combobox::Configure [list $widgets(this)] [array get options]" error]} {
02486     catch {destroy $w}
02487     error "internal error: $error"
02488     }
02489 
02490     return ""
02491 }
02492 
02493 }
02494 
02495 namespace ::combobox {
02496 
02497 ret  {::combobox::CallCommand} (type w , type newValue) {
02498 upvar ::combobox::${w}::widgets widgets
02499     upvar ::combobox::${w}::options options
02500 
02501     # call the associated command, if defined and -commandstate is
02502     # set to "normal"
02503     if {$options(-commandstate) == "normal" &&  [string length $options(-command)] > 0} {
02504     set args [list $widgets(this) $newValue]
02505     uplevel \#0 $options(-command) $args
02506     }
02507 }
02508 
02509 }
02510 
02511 namespace ::combobox {
02512 
02513 ret  {::combobox::Canonize} (type w , type object , type opt) {
02514 variable widgetOptions
02515     variable columnOptions
02516     variable widgetCommands
02517     variable listCommands
02518     variable scanCommands
02519 
02520     switch $object {
02521     command {
02522         if {[lsearch -exact $widgetCommands $opt] >= 0} {
02523         return $opt
02524         }
02525 
02526         # command names aren't stored in an array, and there
02527         # isn't a way to get all the matches in a list, so
02528         # we'll stuff the commands in a temporary array so
02529         # we can use [array names]
02530         set list $widgetCommands
02531         foreach element $list {
02532         set tmp($element) ""
02533         }
02534         set matches [array names tmp ${opt}*]
02535     }
02536 
02537     {list command} {
02538         if {[lsearch -exact $listCommands $opt] >= 0} {
02539         return $opt
02540         }
02541 
02542         # command names aren't stored in an array, and there
02543         # isn't a way to get all the matches in a list, so
02544         # we'll stuff the commands in a temporary array so
02545         # we can use [array names]
02546         set list $listCommands
02547         foreach element $list {
02548         set tmp($element) ""
02549         }
02550         set matches [array names tmp ${opt}*]
02551     }
02552 
02553     {scan command} {
02554         if {[lsearch -exact $scanCommands $opt] >= 0} {
02555         return $opt
02556         }
02557 
02558         # command names aren't stored in an array, and there
02559         # isn't a way to get all the matches in a list, so
02560         # we'll stuff the commands in a temporary array so
02561         # we can use [array names]
02562         set list $scanCommands
02563         foreach element $list {
02564         set tmp($element) ""
02565         }
02566         set matches [array names tmp ${opt}*]
02567     }
02568 
02569     option {
02570         if {[info exists widgetOptions($opt)]  && [llength $widgetOptions($opt)] == 2} {
02571         return $opt
02572         }
02573         set list [array names widgetOptions]
02574         set matches [array names widgetOptions ${opt}*]
02575     }
02576 
02577     }
02578 
02579     if {[llength $matches] == 0} {
02580     set choices [HumanizeList $list]
02581     error "unknown $object \"$opt\"; must be one of $choices"
02582 
02583     } elseif {[llength $matches] == 1} {
02584     set opt [lindex $matches 0]
02585 
02586     # deal with option aliases
02587     switch $object {
02588         option {
02589         set opt [lindex $matches 0]
02590         if {[llength $widgetOptions($opt)] == 1} {
02591             set opt $widgetOptions($opt)
02592         }
02593         }
02594     }
02595 
02596     return $opt
02597 
02598     } else {
02599     set choices [HumanizeList $list]
02600     error "ambiguous $object \"$opt\"; must be one of $choices"
02601     }
02602 }
02603 
02604 }
02605 
02606 namespace ::combobox {
02607 
02608 ret  {::combobox::ComputeGeometry} (type w) {
02609 upvar ::combobox::${w}::widgets widgets
02610     upvar ::combobox::${w}::options options
02611 
02612     if {$options(-height) == 0 && $options(-maxheight) != "0"} {
02613     # if this is the case, count the items and see if
02614     # it exceeds our maxheight. If so, set the listbox
02615     # size to maxheight...
02616     set nitems [$widgets(listbox) size]
02617     if {$nitems > $options(-maxheight)} {
02618         # tweak the height of the listbox
02619         $widgets(listbox) configure -height $options(-maxheight)
02620     } else {
02621         # un-tweak the height of the listbox
02622         $widgets(listbox) configure -height 0
02623     }
02624     update idletasks
02625     }
02626 
02627     # compute height and width of the dropdown list
02628     set bd [$widgets(dropdown) cget -borderwidth]
02629     set height [expr {[winfo reqheight $widgets(dropdown)] + $bd + $bd}]
02630     if {[string length $options(-dropdownwidth)] == 0 ||
02631         $options(-dropdownwidth) == 0} {
02632         set width [winfo width $widgets(this)]
02633     } else {
02634         set m [font measure [$widgets(listbox) cget -font] "m"]
02635         set width [expr {$options(-dropdownwidth) * $m}]
02636     }
02637 
02638     # figure out where to place it on the screen, trying to take into
02639     # account we may be running under some virtual window manager
02640     set screenWidth  [winfo screenwidth $widgets(this)]
02641     set screenHeight [winfo screenheight $widgets(this)]
02642     set rootx        [winfo rootx $widgets(this)]
02643     set rooty        [winfo rooty $widgets(this)]
02644     set vrootx       [winfo vrootx $widgets(this)]
02645     set vrooty       [winfo vrooty $widgets(this)]
02646 
02647     # the x coordinate is simply the rootx of our widget, adjusted for
02648     # the virtual window. We won't worry about whether the window will
02649     # be offscreen to the left or right -- we want the illusion that it
02650     # is part of the entry widget, so if part of the entry widget is off-
02651     # screen, so will the list. If you want to change the behavior,
02652     # simply change the if statement... (and be sure to update this
02653     # comment!)
02654     set x  [expr {$rootx + $vrootx}]
02655     if {0} {
02656     set rightEdge [expr {$x + $width}]
02657     if {$rightEdge > $screenWidth} {
02658         set x [expr {$screenWidth - $width}]
02659     }
02660     if {$x < 0} {set x 0}
02661     }
02662 
02663     # the y coordinate is the rooty plus vrooty offset plus
02664     # the height of the static part of the widget plus 1 for a
02665     # tiny bit of visual separation...
02666     set y [expr {$rooty + $vrooty + [winfo reqheight $widgets(this)] + 1}]
02667     set bottomEdge [expr {$y + $height}]
02668 
02669     if {$bottomEdge >= $screenHeight} {
02670     # ok. Fine. Pop it up above the entry widget isntead of
02671     # below.
02672     set y [expr {($rooty - $height - 1) + $vrooty}]
02673 
02674     if {$y < 0} {
02675         # this means it extends beyond our screen. How annoying.
02676         # Now we'll try to be real clever and either pop it up or
02677         # down, depending on which way gives us the biggest list.
02678         # then, we'll trim the list to fit and force the use of
02679         # a scrollbar
02680 
02681         # (sadly, for windows users this measurement doesn't
02682         # take into consideration the height of the taskbar,
02683         # but don't blame me -- there isn't any way to detect
02684         # it or figure out its dimensions. The same probably
02685         # applies to any window manager with some magic windows
02686         # glued to the top or bottom of the screen)
02687 
02688         if {$rooty > [expr {$screenHeight / 2}]} {
02689         # we are in the lower half of the screen --
02690         # pop it up. Y is zero; that parts easy. The height
02691         # is simply the y coordinate of our widget, minus
02692         # a pixel for some visual separation. The y coordinate
02693         # will be the topof the screen.
02694         set y 1
02695         set height [expr {$rooty - 1 - $y}]
02696 
02697         } else {
02698         # we are in the upper half of the screen --
02699         # pop it down
02700         set y [expr {$rooty + $vrooty +  [winfo reqheight $widgets(this)] + 1}]
02701         set height [expr {$screenHeight - $y}]
02702 
02703         }
02704 
02705         # force a scrollbar
02706         HandleScrollbar $widgets(this) crop
02707     }   
02708     }
02709 
02710     if {$y < 0} {
02711     # hmmm. Bummer.
02712     set y 0
02713     set height $screenheight
02714     }
02715 
02716     set geometry [format "=%dx%d+%d+%d" $width $height $x $y]
02717 
02718     return $geometry
02719 }
02720 
02721 }
02722 
02723 namespace ::combobox {
02724 
02725 ret  {::combobox::Configure} (type w , type args) {
02726 variable widgetOptions
02727     variable defaultEntryCursor
02728 
02729     upvar ::combobox::${w}::widgets widgets
02730     upvar ::combobox::${w}::options options
02731 
02732     if {[llength $args] == 0} {
02733     # hmmm. User must be wanting all configuration information
02734     # note that if the value of an array element is of length
02735     # one it is an alias, which needs to be handled slightly
02736     # differently
02737     set results {}
02738     foreach opt [lsort [array names widgetOptions]] {
02739         if {[llength $widgetOptions($opt)] == 1} {
02740         set alias $widgetOptions($opt)
02741         set optName $widgetOptions($alias)
02742         lappend results [list $opt $optName]
02743         } else {
02744         set optName  [lindex $widgetOptions($opt) 0]
02745         set optClass [lindex $widgetOptions($opt) 1]
02746         set default [option get $w $optName $optClass]
02747         if {[info exists options($opt)]} {
02748             lappend results [list $opt $optName $optClass  $default $options($opt)]
02749         } else {
02750             lappend results [list $opt $optName $optClass  $default ""]
02751         }
02752         }
02753     }
02754 
02755     return $results
02756     }
02757 
02758     # one argument means we are looking for configuration
02759     # information on a single option
02760     if {[llength $args] == 1} {
02761     set opt [::combobox::Canonize $w option [lindex $args 0]]
02762 
02763     set optName  [lindex $widgetOptions($opt) 0]
02764     set optClass [lindex $widgetOptions($opt) 1]
02765     set default [option get $w $optName $optClass]
02766     set results [list $opt $optName $optClass  $default $options($opt)]
02767     return $results
02768     }
02769 
02770     # if we have an odd number of values, bail.
02771     if {[expr {[llength $args]%2}] == 1} {
02772     # hmmm. An odd number of elements in args
02773     error "value for \"[lindex $args end]\" missing"
02774     }
02775 
02776     # Great. An even number of options. Let's make sure they
02777     # are all valid before we do anything. Note that Canonize
02778     # will generate an error if it finds a bogus option; otherwise
02779     # it returns the canonical option name
02780     foreach {name value} $args {
02781     set name [::combobox::Canonize $w option $name]
02782     set opts($name) $value
02783     }
02784 
02785     # process all of the configuration options
02786     # some (actually, most) options require us to
02787     # do something, like change the attributes of
02788     # a widget or two. Here's where we do that...
02789     #
02790     # note that the handling of disabledforeground and
02791     # disabledbackground is a little wonky. First, we have
02792     # to deal with backwards compatibility (ie: tk 8.3 and below
02793     # didn't have such options for the entry widget), and
02794     # we have to deal with the fact we might want to disable
02795     # the entry widget but use the normal foreground/background
02796     # for when the combobox is not disabled, but not editable either.
02797 
02798     set updateVisual 0
02799     foreach option [array names opts] {
02800     set newValue $opts($option)
02801     if {[info exists options($option)]} {
02802         set oldValue $options($option)
02803     }
02804 
02805     switch -- $option {
02806         -background {
02807         set updateVisual 1
02808         set options($option) $newValue
02809         }
02810 
02811         -borderwidth {
02812         $widgets(frame) configure -borderwidth $newValue
02813         set options($option) $newValue
02814         }
02815 
02816         -command {
02817         # nothing else to do...
02818         set options($option) $newValue
02819         }
02820 
02821         -commandstate {
02822         # do some value checking...
02823         if {$newValue != "normal" && $newValue != "disabled"} {
02824             set options($option) $oldValue
02825             set message "bad state value \"$newValue\";"
02826             append message " must be normal or disabled"
02827             error $message
02828         }
02829         set options($option) $newValue
02830         }
02831 
02832         -cursor {
02833         $widgets(frame) configure -cursor $newValue
02834         $widgets(entry) configure -cursor $newValue
02835         $widgets(listbox) configure -cursor $newValue
02836         set options($option) $newValue
02837         }
02838 
02839         -disabledforeground {
02840         set updateVisual 1
02841         set options($option) $newValue
02842         }
02843 
02844         -disabledbackground {
02845         set updateVisual 1
02846         set options($option) $newValue
02847         }
02848 
02849             -dropdownwidth {
02850                 set options($option) $newValue
02851             }
02852 
02853         -editable {
02854         set updateVisual 1
02855         if {$newValue} {
02856             # it's editable...
02857             $widgets(entry) configure  -state normal  -cursor $defaultEntryCursor
02858         } else {
02859             $widgets(entry) configure  -state disabled  -cursor $options(-cursor)
02860         }
02861         set options($option) $newValue
02862         }
02863 
02864         -font {
02865         $widgets(entry) configure -font $newValue
02866         $widgets(listbox) configure -font $newValue
02867         set options($option) $newValue
02868         }
02869 
02870         -foreground {
02871         set updateVisual 1
02872         set options($option) $newValue
02873         }
02874 
02875         -height {
02876         $widgets(listbox) configure -height $newValue
02877         HandleScrollbar $w
02878         set options($option) $newValue
02879         }
02880 
02881         -highlightbackground {
02882         $widgets(frame) configure -highlightbackground $newValue
02883         set options($option) $newValue
02884         }
02885 
02886         -highlightcolor {
02887         $widgets(frame) configure -highlightcolor $newValue
02888         set options($option) $newValue
02889         }
02890 
02891         -highlightthickness {
02892         $widgets(frame) configure -highlightthickness $newValue
02893         set options($option) $newValue
02894         }
02895     
02896         -image {
02897         if {[string length $newValue] > 0} {
02898             $widgets(button) configure -image $newValue
02899         } else {
02900             $widgets(button) configure -image ::combobox::bimage
02901         }
02902         set options($option) $newValue
02903         }
02904 
02905         -maxheight {
02906         # ComputeGeometry may dork with the actual height
02907         # of the listbox, so let's undork it
02908         $widgets(listbox) configure -height $options(-height)
02909         HandleScrollbar $w
02910         set options($option) $newValue
02911         }
02912 
02913         -opencommand {
02914         # nothing else to do...
02915         set options($option) $newValue
02916         }
02917 
02918         -relief {
02919         $widgets(frame) configure -relief $newValue
02920         set options($option) $newValue
02921         }
02922 
02923         -selectbackground {
02924         $widgets(entry) configure -selectbackground $newValue
02925         $widgets(listbox) configure -selectbackground $newValue
02926         set options($option) $newValue
02927         }
02928 
02929         -selectborderwidth {
02930         $widgets(entry) configure -selectborderwidth $newValue
02931         $widgets(listbox) configure -selectborderwidth $newValue
02932         set options($option) $newValue
02933         }
02934 
02935         -selectforeground {
02936         $widgets(entry) configure -selectforeground $newValue
02937         $widgets(listbox) configure -selectforeground $newValue
02938         set options($option) $newValue
02939         }
02940 
02941         -state {
02942         if {$newValue == "normal"} {
02943             set updateVisual 1
02944             # it's enabled
02945 
02946             set editable [::combobox::GetBoolean  $options(-editable)]
02947             if {$editable} {
02948             $widgets(entry) configure -state normal
02949             $widgets(entry) configure -takefocus 1
02950             }
02951 
02952                     # note that $widgets(button) is actually a label,
02953                     # not a button. And being able to disable labels
02954                     # wasn't possible until tk 8.3. (makes me wonder
02955             # why I chose to use a label, but that answer is
02956             # lost to antiquity)
02957                     if {[info patchlevel] >= 8.3} {
02958                         $widgets(button) configure -state normal
02959                     }
02960 
02961         } elseif {$newValue == "disabled"}  {
02962             set updateVisual 1
02963             # it's disabled
02964             $widgets(entry) configure -state disabled
02965             $widgets(entry) configure -takefocus 0
02966                     # note that $widgets(button) is actually a label,
02967                     # not a button. And being able to disable labels
02968                     # wasn't possible until tk 8.3. (makes me wonder
02969             # why I chose to use a label, but that answer is
02970             # lost to antiquity)
02971                     if {$::tcl_version >= 8.3} {
02972                         $widgets(button) configure -state disabled
02973                     }
02974 
02975         } else {
02976             set options($option) $oldValue
02977             set message "bad state value \"$newValue\";"
02978             append message " must be normal or disabled"
02979             error $message
02980         }
02981 
02982         set options($option) $newValue
02983         }
02984 
02985         -takefocus {
02986         $widgets(entry) configure -takefocus $newValue
02987         set options($option) $newValue
02988         }
02989 
02990         -textvariable {
02991         $widgets(entry) configure -textvariable $newValue
02992         set options($option) $newValue
02993         }
02994 
02995         -value {
02996         ::combobox::SetValue $widgets(this) $newValue
02997         set options($option) $newValue
02998         }
02999 
03000         -width {
03001         $widgets(entry) configure -width $newValue
03002         $widgets(listbox) configure -width $newValue
03003         set options($option) $newValue
03004         }
03005 
03006         -xscrollcommand {
03007         $widgets(entry) configure -xscrollcommand $newValue
03008         set options($option) $newValue
03009         }
03010     }   
03011 
03012     if {$updateVisual} {UpdateVisualAttributes $w}
03013     }
03014 }
03015 
03016 }
03017 
03018 namespace ::combobox {
03019 
03020 ret  {::combobox::DestroyHandler} (type w) {
03021 # if the widget actually being destroyed is of class Combobox,
03022     # crush the namespace and kill the proc. Get it? Crush. Kill.
03023     # Destroy. Heh. Danger Will Robinson! Oh, man! I'm so funny it
03024     # brings tears to my eyes.
03025     if {[string compare [winfo class $w] "Combobox"] == 0} {
03026     upvar ::combobox::${w}::widgets  widgets
03027     upvar ::combobox::${w}::options  options
03028 
03029     # delete the namespace and the proc which represents
03030     # our widget
03031     namespace delete ::combobox::$w
03032     rename $w {}
03033     }
03034 
03035     return ""
03036 }
03037 
03038 }
03039 
03040 namespace ::combobox {
03041 
03042 ret  {::combobox::DoInternalWidgetCommand} (type w , type subwidget , type command , type args) {
03043 upvar ::combobox::${w}::widgets widgets
03044     upvar ::combobox::${w}::options options
03045 
03046     set subcommand $command
03047     set command [concat $widgets($subwidget) $command $args]
03048     if {[catch $command result]} {
03049     # replace the subwidget name with the megawidget name
03050     regsub $widgets($subwidget) $result $widgets(this) result
03051 
03052     # replace specific instances of the subwidget command
03053     # with out megawidget command
03054     switch $subwidget,$subcommand {
03055         listbox,index  {regsub "index"  $result "list index"  result}
03056         listbox,insert {regsub "insert" $result "list insert" result}
03057         listbox,delete {regsub "delete" $result "list delete" result}
03058         listbox,get    {regsub "get"    $result "list get"    result}
03059         listbox,size   {regsub "size"   $result "list size"   result}
03060     }
03061     error $result
03062 
03063     } else {
03064     return $result
03065     }
03066 }
03067 
03068 }
03069 
03070 namespace ::combobox {
03071 
03072 ret  {::combobox::Find} (type w , optional exact =0) {
03073 upvar ::combobox::${w}::widgets widgets
03074     upvar ::combobox::${w}::options options
03075 
03076     ## *sigh* this logic is rather gross and convoluted. Surely
03077     ## there is a more simple, straight-forward way to implement
03078     ## all this. As the saying goes, I lack the time to make it
03079     ## shorter...
03080 
03081     # use what is already in the entry widget as a pattern
03082     set pattern [$widgets(entry) get]
03083 
03084     if {[string length $pattern] == 0} {
03085     # clear the current selection
03086     $widgets(listbox) see 0
03087     $widgets(listbox) selection clear 0 end
03088     $widgets(listbox) selection anchor 0
03089     $widgets(listbox) activate 0
03090     return
03091     }
03092 
03093     # we're going to be searching this list...
03094     set list [$widgets(listbox) get 0 end]
03095 
03096     # if we are doing an exact match, try to find,
03097     # well, an exact match
03098     set exactMatch -1
03099     if {$exact} {
03100     set exactMatch [lsearch -exact $list $pattern]
03101     }
03102 
03103     # search for it. We'll try to be clever and not only
03104     # search for a match for what they typed, but a match for
03105     # something close to what they typed. We'll keep removing one
03106     # character at a time from the pattern until we find a match
03107     # of some sort.
03108     set index -1
03109     while {$index == -1 && [string length $pattern]} {
03110     set index [lsearch -glob $list "$pattern*"]
03111     if {$index == -1} {
03112         regsub {.$} $pattern {} pattern
03113     }
03114     }
03115 
03116     # this is the item that most closely matches...
03117     set thisItem [lindex $list $index]
03118 
03119     # did we find a match? If so, do some additional munging...
03120     if {$index != -1} {
03121 
03122     # we need to find the part of the first item that is
03123     # unique WRT the second... I know there's probably a
03124     # simpler way to do this...
03125 
03126     set nextIndex [expr {$index + 1}]
03127     set nextItem [lindex $list $nextIndex]
03128 
03129     # we don't really need to do much if the next
03130     # item doesn't match our pattern...
03131     if {[string match $pattern* $nextItem]} {
03132         # ok, the next item matches our pattern, too
03133         # now the trick is to find the first character
03134         # where they *don't* match...
03135         set marker [string length $pattern]
03136         while {$marker <= [string length $pattern]} {
03137         set a [string index $thisItem $marker]
03138         set b [string index $nextItem $marker]
03139         if {[string compare $a $b] == 0} {
03140             append pattern $a
03141             incr marker
03142         } else {
03143             break
03144         }
03145         }
03146     } else {
03147         set marker [string length $pattern]
03148     }
03149     
03150     } else {
03151     set marker end
03152     set index 0
03153     }
03154 
03155     # ok, we know the pattern and what part is unique;
03156     # update the entry widget and listbox appropriately
03157     if {$exact && $exactMatch == -1} {
03158     # this means we didn't find an exact match
03159     $widgets(listbox) selection clear 0 end
03160     $widgets(listbox) see $index
03161 
03162 
03163     } elseif {!$exact}  {
03164     # this means we found something, but it isn't an exact
03165     # match. If we find something that *is* an exact match we
03166     # don't need to do the following, since it would merely
03167     # be replacing the data in the entry widget with itself
03168     set oldstate [$widgets(entry) cget -state]
03169     $widgets(entry) configure -state normal
03170     $widgets(entry) delete 0 end
03171     $widgets(entry) insert end $thisItem
03172     $widgets(entry) selection clear
03173     $widgets(entry) selection range $marker end
03174     $widgets(listbox) activate $index
03175     $widgets(listbox) selection clear 0 end
03176     $widgets(listbox) selection anchor $index
03177     $widgets(listbox) selection set $index
03178     $widgets(listbox) see $index
03179     $widgets(entry) configure -state $oldstate
03180     }
03181 }
03182 
03183 }
03184 
03185 namespace ::combobox {
03186 
03187 ret  {::combobox::GetBoolean} (type value , optional errorValue =1) {
03188 if {[catch {expr {([string trim $value])?1:0}} res]} {
03189     return $errorValue
03190     } else {
03191     return $res
03192     }
03193 }
03194 
03195 }
03196 
03197 namespace ::combobox {
03198 
03199 ret  {::combobox::HandleEvent} (type w , type event , type args) {
03200 upvar ::combobox::${w}::widgets  widgets
03201     upvar ::combobox::${w}::options  options
03202     upvar ::combobox::${w}::oldValue oldValue
03203 
03204     # for all of these events, if we have a special action we'll
03205     # do that and do a "return -code break" to keep additional
03206     # bindings from firing. Otherwise we'll let the event fall
03207     # on through.
03208     switch $event {
03209 
03210         "<MouseWheel>" {
03211         if {[winfo ismapped $widgets(dropdown)]} {
03212                 set D [lindex $args 0]
03213                 # the '120' number in the following expression has
03214                 # it's genesis in the tk bind manpage, which suggests
03215                 # that the smallest value of %D for mousewheel events
03216                 # will be 120. The intent is to scroll one line at a time.
03217                 $widgets(listbox) yview scroll [expr {-($D/120)}] units
03218             }
03219         }
03220 
03221     "<Any-KeyPress>" {
03222         # if the widget is editable, clear the selection.
03223         # this makes it more obvious what will happen if the
03224         # user presses <Return> (and helps our code know what
03225         # to do if the user presses return)
03226         if {$options(-editable)} {
03227         $widgets(listbox) see 0
03228         $widgets(listbox) selection clear 0 end
03229         $widgets(listbox) selection anchor 0
03230         $widgets(listbox) activate 0
03231         }
03232     }
03233 
03234     "<FocusIn>" {
03235         set oldValue [$widgets(entry) get]
03236     }
03237 
03238     "<FocusOut>" {
03239         if {![winfo ismapped $widgets(dropdown)]} {
03240         # did the value change?
03241         set newValue [$widgets(entry) get]
03242         if {$oldValue != $newValue} {
03243             CallCommand $widgets(this) $newValue
03244         }
03245         }
03246     }
03247 
03248     "<1>" {
03249         set editable [::combobox::GetBoolean $options(-editable)]
03250         if {!$editable} {
03251         if {[winfo ismapped $widgets(dropdown)]} {
03252             $widgets(this) close
03253             return -code break;
03254 
03255         } else {
03256             if {$options(-state) != "disabled"} {
03257             $widgets(this) open
03258             return -code break;
03259             }
03260         }
03261         }
03262     }
03263 
03264     "<Double-1>" {
03265         if {$options(-state) != "disabled"} {
03266         $widgets(this) toggle
03267         return -code break;
03268         }
03269     }
03270 
03271     "<Tab>" {
03272         if {[winfo ismapped $widgets(dropdown)]} {
03273         ::combobox::Find $widgets(this) 0
03274         return -code break;
03275         } else {
03276         ::combobox::SetValue $widgets(this) [$widgets(this) get]
03277         }
03278     }
03279 
03280     "<Escape>" {
03281 #       $widgets(entry) delete 0 end
03282 #       $widgets(entry) insert 0 $oldValue
03283         if {[winfo ismapped $widgets(dropdown)]} {
03284         $widgets(this) close
03285         return -code break;
03286         }
03287     }
03288 
03289     "<Return>" {
03290         # did the value change?
03291         set newValue [$widgets(entry) get]
03292         if {$oldValue != $newValue} {
03293         CallCommand $widgets(this) $newValue
03294         }
03295 
03296         if {[winfo ismapped $widgets(dropdown)]} {
03297         ::combobox::Select $widgets(this)  [$widgets(listbox) curselection]
03298         return -code break;
03299         }
03300 
03301     }
03302 
03303     "<Next>" {
03304         $widgets(listbox) yview scroll 1 pages
03305         set index [$widgets(listbox) index @0,0]
03306         $widgets(listbox) see $index
03307         $widgets(listbox) activate $index
03308         $widgets(listbox) selection clear 0 end
03309         $widgets(listbox) selection anchor $index
03310         $widgets(listbox) selection set $index
03311 
03312     }
03313 
03314     "<Prior>" {
03315         $widgets(listbox) yview scroll -1 pages
03316         set index [$widgets(listbox) index @0,0]
03317         $widgets(listbox) activate $index
03318         $widgets(listbox) see $index
03319         $widgets(listbox) selection clear 0 end
03320         $widgets(listbox) selection anchor $index
03321         $widgets(listbox) selection set $index
03322     }
03323 
03324     "<Down>" {
03325         if {[winfo ismapped $widgets(dropdown)]} {
03326         ::combobox::tkListboxUpDown $widgets(listbox) 1
03327         return -code break;
03328 
03329         } else {
03330         if {$options(-state) != "disabled"} {
03331             $widgets(this) open
03332             return -code break;
03333         }
03334         }
03335     }
03336     "<Up>" {
03337         if {[winfo ismapped $widgets(dropdown)]} {
03338         ::combobox::tkListboxUpDown $widgets(listbox) -1
03339         return -code break;
03340 
03341         } else {
03342         if {$options(-state) != "disabled"} {
03343             $widgets(this) open
03344             return -code break;
03345         }
03346         }
03347     }
03348     }
03349 
03350     return ""
03351 }
03352 
03353 }
03354 
03355 namespace ::combobox {
03356 
03357 ret  {::combobox::HandleScrollbar} (type w , optional action =unknown) {
03358 upvar ::combobox::${w}::widgets widgets
03359     upvar ::combobox::${w}::options options
03360 
03361     if {$options(-height) == 0} {
03362     set hlimit $options(-maxheight)
03363     } else {
03364     set hlimit $options(-height)
03365     }       
03366 
03367     switch $action {
03368     "grow" {
03369         if {$hlimit > 0 && [$widgets(listbox) size] > $hlimit} {
03370         pack $widgets(vsb) -side right -fill y -expand n
03371         }
03372     }
03373 
03374     "shrink" {
03375         if {$hlimit > 0 && [$widgets(listbox) size] <= $hlimit} {
03376         pack forget $widgets(vsb)
03377         }
03378     }
03379 
03380     "crop" {
03381         # this means the window was cropped and we definitely
03382         # need a scrollbar no matter what the user wants
03383         pack $widgets(vsb) -side right -fill y -expand n
03384     }
03385 
03386     default {
03387         if {$hlimit > 0 && [$widgets(listbox) size] > $hlimit} {
03388         pack $widgets(vsb) -side right -fill y -expand n
03389         } else {
03390         pack forget $widgets(vsb)
03391         }
03392     }
03393     }
03394 
03395     return ""
03396 }
03397 
03398 }
03399 
03400 namespace ::combobox {
03401 
03402 ret  {::combobox::HumanizeList} (type list) {
03403 if {[llength $list] == 1} {
03404     return [lindex $list 0]
03405     } else {
03406     set list [lsort $list]
03407     set secondToLast [expr {[llength $list] -2}]
03408     set most [lrange $list 0 $secondToLast]
03409     set last [lindex $list end]
03410 
03411     return "[join $most {, }] or $last"
03412     }
03413 }
03414 
03415 }
03416 
03417 namespace ::combobox {
03418 
03419 ret  {::combobox::Init} () {
03420 variable widgetOptions
03421     variable widgetCommands
03422     variable scanCommands
03423     variable listCommands
03424     variable defaultEntryCursor
03425 
03426     array set widgetOptions [list  -background          {background          Background}  -bd                  -borderwidth  -bg                  -background  -borderwidth         {borderWidth         BorderWidth}  -command             {command             Command}  -commandstate        {commandState        State}  -cursor              {cursor              Cursor}  -disabledbackground  {disabledBackground  DisabledBackground}  -disabledforeground  {disabledForeground  DisabledForeground}  -dropdownwidth       {dropdownWidth       DropdownWidth}  -editable            {editable            Editable}  -fg                  -foreground  -font                {font                Font}  -foreground          {foreground          Foreground}  -height              {height              Height}  -highlightbackground {highlightBackground HighlightBackground}  -highlightcolor      {highlightColor      HighlightColor}  -highlightthickness  {highlightThickness  HighlightThickness}  -image               {image               Image}  -maxheight           {maxHeight           Height}  -opencommand         {opencommand         Command}  -relief              {relief              Relief}  -selectbackground    {selectBackground    Foreground}  -selectborderwidth   {selectBorderWidth   BorderWidth}  -selectforeground    {selectForeground    Background}  -state               {state               State}  -takefocus           {takeFocus           TakeFocus}  -textvariable        {textVariable        Variable}  -value               {value               Value}  -width               {width               Width}  -xscrollcommand      {xScrollCommand      ScrollCommand}  ]
03427 
03428 
03429     set widgetCommands [list  bbox      cget     configure    curselection  delete    get      icursor      index         insert    list     scan         selection     xview     select   toggle       open          close      ]
03430 
03431     set listCommands [list  delete       get       index        insert       size  ]
03432 
03433     set scanCommands [list mark dragto]
03434 
03435     # why check for the Tk package? This lets us be sourced into
03436     # an interpreter that doesn't have Tk loaded, such as the slave
03437     # interpreter used by pkg_mkIndex. In theory it should have no
03438     # side effects when run
03439     if {[lsearch -exact [package names] "Tk"] != -1} {
03440 
03441     ##################################################################
03442     #- this initializes the option database. Kinda gross, but it works
03443     #- (I think).
03444     ##################################################################
03445 
03446     # the image used for the button...
03447     if {$::tcl_platform(platform) == "windows"} {
03448         image create bitmap ::combobox::bimage -data {
03449         #define down_arrow_width 12
03450         #define down_arrow_height 12
03451         static char down_arrow_bits[] = {
03452             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
03453             0xfc,0xf1,0xf8,0xf0,0x70,0xf0,0x20,0xf0,
03454             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00;
03455         }
03456         }
03457     } else {
03458         image create bitmap ::combobox::bimage -data  {
03459         #define down_arrow_width 15
03460         #define down_arrow_height 15
03461         static char down_arrow_bits[] = {
03462             0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,
03463             0x00,0x80,0xf8,0x8f,0xf0,0x87,0xe0,0x83,
03464             0xc0,0x81,0x80,0x80,0x00,0x80,0x00,0x80,
03465             0x00,0x80,0x00,0x80,0x00,0x80
03466         }
03467         }
03468     }
03469 
03470     # compute a widget name we can use to create a temporary widget
03471     set tmpWidget ".__tmp__"
03472     set count 0
03473     while {[winfo exists $tmpWidget] == 1} {
03474         set tmpWidget ".__tmp__$count"
03475         incr count
03476     }
03477 
03478     # get the scrollbar width. Because we try to be clever and draw our
03479     # own button instead of using a tk widget, we need to know what size
03480     # button to create. This little hack tells us the width of a scroll
03481     # bar.
03482     #
03483     # NB: we need to be sure and pick a window  that doesn't already
03484     # exist...
03485     scrollbar $tmpWidget
03486     set sb_width [winfo reqwidth $tmpWidget]
03487     destroy $tmpWidget
03488 
03489     # steal options from the entry widget
03490     # we want darn near all options, so we'll go ahead and do
03491     # them all. No harm done in adding the one or two that we
03492     # don't use.
03493     entry $tmpWidget
03494     foreach foo [$tmpWidget configure] {
03495         # the cursor option is special, so we'll save it in
03496         # a special way
03497         if {[lindex $foo 0] == "-cursor"} {
03498         set defaultEntryCursor [lindex $foo 4]
03499         }
03500         if {[llength $foo] == 5} {
03501         set option [lindex $foo 1]
03502         set value [lindex $foo 4]
03503         option add *Combobox.$option $value widgetDefault
03504 
03505         # these options also apply to the dropdown listbox
03506         if {[string compare $option "foreground"] == 0  || [string compare $option "background"] == 0  || [string compare $option "font"] == 0} {
03507             option add *Combobox*ComboboxListbox.$option $value  widgetDefault
03508         }
03509         }
03510     }
03511     destroy $tmpWidget
03512 
03513     # these are unique to us...
03514     option add *Combobox.dropdownWidth       {}     widgetDefault
03515     option add *Combobox.openCommand         {}     widgetDefault
03516     option add *Combobox.cursor              {}     widgetDefault
03517     option add *Combobox.commandState        normal widgetDefault
03518     option add *Combobox.editable            1      widgetDefault
03519     option add *Combobox.maxHeight           10     widgetDefault
03520     option add *Combobox.height              0
03521     }
03522 
03523     # set class bindings
03524     SetClassBindings
03525 }
03526 
03527 }
03528 
03529 namespace ::combobox {
03530 
03531 ret  {::combobox::Select} (type w , type index) {
03532 upvar ::combobox::${w}::widgets widgets
03533     upvar ::combobox::${w}::options options
03534 
03535     # the catch is because I'm sloppy -- presumably, the only time
03536     # an error will be caught is if there is no selection.
03537     if {![catch {set data [$widgets(listbox) get [lindex $index 0]]}]} {
03538     ::combobox::SetValue $widgets(this) $data
03539 
03540     $widgets(listbox) selection clear 0 end
03541     $widgets(listbox) selection anchor $index
03542     $widgets(listbox) selection set $index
03543 
03544     }
03545     $widgets(entry) selection range 0 end
03546 
03547     $widgets(this) close
03548 
03549     return ""
03550 }
03551 
03552 }
03553 
03554 namespace ::combobox {
03555 
03556 ret  {::combobox::SetBindings} (type w) {
03557 upvar ::combobox::${w}::widgets  widgets
03558     upvar ::combobox::${w}::options  options
03559 
03560     # juggle the bindtags. The basic idea here is to associate the
03561     # widget name with the entry widget, so if a user does a bind
03562     # on the combobox it will get handled properly since it is
03563     # the entry widget that has keyboard focus.
03564     bindtags $widgets(entry)  [concat $widgets(this) [bindtags $widgets(entry)]]
03565 
03566     bindtags $widgets(button)  [concat $widgets(this) [bindtags $widgets(button)]]
03567 
03568     # override the default bindings for tab and shift-tab. The
03569     # focus procs take a widget as their only parameter and we
03570     # want to make sure the right window gets used (for shift-
03571     # tab we want it to appear as if the event was generated
03572     # on the frame rather than the entry.
03573     bind $widgets(entry) <Tab>  "::combobox::tkTabToWindow \[tk_focusNext $widgets(entry)\]; break"
03574     bind $widgets(entry) <Shift-Tab>  "::combobox::tkTabToWindow \[tk_focusPrev $widgets(this)\]; break"
03575 
03576     # this makes our "button" (which is actually a label)
03577     # do the right thing
03578     bind $widgets(button) <ButtonPress-1> [list $widgets(this) toggle]
03579 
03580     # this lets the autoscan of the listbox work, even if they
03581     # move the cursor over the entry widget.
03582     bind $widgets(entry) <B1-Enter> "break"
03583 
03584     bind $widgets(listbox) <ButtonRelease-1>  "::combobox::Select [list $widgets(this)]  \[$widgets(listbox) nearest %y\]; break"
03585 
03586     bind $widgets(vsb) <ButtonPress-1>   {continue}
03587     bind $widgets(vsb) <ButtonRelease-1> {continue}
03588 
03589     bind $widgets(listbox) <Any-Motion> {
03590     %W selection clear 0 end
03591     %W activate @%x,%y
03592     %W selection anchor @%x,%y
03593     %W selection set @%x,%y @%x,%y
03594     # need to do a yview if the cursor goes off the top
03595     # or bottom of the window... (or do we?)
03596     }
03597 
03598     # these events need to be passed from the entry widget
03599     # to the listbox, or otherwise need some sort of special
03600     # handling.
03601     foreach event [list <Up> <Down> <Tab> <Return> <Escape>  <Next> <Prior> <Double-1> <1> <Any-KeyPress>  <FocusIn> <FocusOut>] {
03602     bind $widgets(entry) $event  [list ::combobox::HandleEvent $widgets(this) $event]
03603     }
03604 
03605     # like the other events, <MouseWheel> needs to be passed from
03606     # the entry widget to the listbox. However, in this case we
03607     # need to add an additional parameter
03608     catch {
03609     bind $widgets(entry) <MouseWheel>  [list ::combobox::HandleEvent $widgets(this) <MouseWheel> %D]
03610     }
03611 }
03612 
03613 }
03614 
03615 namespace ::combobox {
03616 
03617 ret  {::combobox::SetClassBindings} () {
03618 # make sure we clean up after ourselves...
03619     bind Combobox <Destroy> [list ::combobox::DestroyHandler %W]
03620 
03621     # this will (hopefully) close (and lose the grab on) the
03622     # listbox if the user clicks anywhere outside of it. Note
03623     # that on Windows, you can click on some other app and
03624     # the listbox will still be there, because tcl won't see
03625     # that button click
03626     set this {[::combobox::convert %W -W]}
03627     bind Combobox <Any-ButtonPress>   "$this close"
03628     bind Combobox <Any-ButtonRelease> "$this close"
03629 
03630     # this helps (but doesn't fully solve) focus issues. The general
03631     # idea is, whenever the frame gets focus it gets passed on to
03632     # the entry widget
03633     bind Combobox <FocusIn> {::combobox::tkTabToWindow [::combobox::convert %W -W].entry}
03634 
03635     # this closes the listbox if we get hidden
03636     bind Combobox <Unmap> {[::combobox::convert %W -W] close}
03637 
03638     return ""
03639 }
03640 
03641 }
03642 
03643 namespace ::combobox {
03644 
03645 ret  {::combobox::SetValue} (type w , type newValue) {
03646 upvar ::combobox::${w}::widgets     widgets
03647     upvar ::combobox::${w}::options     options
03648     upvar ::combobox::${w}::ignoreTrace ignoreTrace
03649     upvar ::combobox::${w}::oldValue    oldValue
03650 
03651     if {[info exists options(-textvariable)]  && [string length $options(-textvariable)] > 0} {
03652     set variable ::$options(-textvariable)
03653     set $variable $newValue
03654     } else {
03655     set oldstate [$widgets(entry) cget -state]
03656     $widgets(entry) configure -state normal
03657     $widgets(entry) delete 0 end
03658     $widgets(entry) insert 0 $newValue
03659     $widgets(entry) configure -state $oldstate
03660     }
03661 
03662     # set our internal textvariable; this will cause any public
03663     # textvariable (ie: defined by the user) to be updated as
03664     # well
03665 #    set ::combobox::${w}::entryTextVariable $newValue
03666 
03667     # redefine our concept of the "old value". Do it before running
03668     # any associated command so we can be sure it happens even
03669     # if the command somehow fails.
03670     set oldValue $newValue
03671 
03672 
03673     # call the associated command. The proc will handle whether or
03674     # not to actually call it, and with what args
03675     CallCommand $w $newValue
03676 
03677     return ""
03678 }
03679 
03680 }
03681 
03682 namespace ::combobox {
03683 
03684 ret  {::combobox::UpdateVisualAttributes} (type w) {
03685 upvar ::combobox::${w}::widgets     widgets
03686     upvar ::combobox::${w}::options     options
03687 
03688     if {$options(-state) == "normal"} {
03689 
03690     set foreground $options(-foreground)
03691     set background $options(-background)
03692     
03693     } elseif {$options(-state) == "disabled"} {
03694 
03695     set foreground $options(-disabledforeground)
03696     set background $options(-disabledbackground)
03697     }
03698 
03699     $widgets(entry)   configure -foreground $foreground -background $background
03700     $widgets(listbox) configure -foreground $foreground -background $background
03701     $widgets(button)  configure -foreground $foreground
03702     $widgets(vsb)     configure -background $background -troughcolor $background
03703     $widgets(frame)   configure -background $background
03704 
03705     # we need to set the disabled colors in case our widget is disabled.
03706     # We could actually check for disabled-ness, but we also need to
03707     # check whether we're enabled but not editable, in which case the
03708     # entry widget is disabled but we still want the enabled colors. It's
03709     # easier just to set everything and be done with it.
03710 
03711     if {$::tcl_version >= 8.4} {
03712     $widgets(entry) configure  -disabledforeground $foreground  -disabledbackground $background
03713     $widgets(button)  configure -disabledforeground $foreground
03714     $widgets(listbox) configure -disabledforeground $foreground
03715     }
03716 }
03717 
03718 }
03719 
03720 namespace ::combobox {
03721 
03722 ret  {::combobox::WidgetProc} (type w , type command , type args) {
03723 upvar ::combobox::${w}::widgets widgets
03724     upvar ::combobox::${w}::options options
03725     upvar ::combobox::${w}::oldFocus oldFocus
03726     upvar ::combobox::${w}::oldFocus oldGrab
03727 
03728     set command [::combobox::Canonize $w command $command]
03729 
03730     # this is just shorthand notation...
03731     set doWidgetCommand  [list ::combobox::DoInternalWidgetCommand $widgets(this)]
03732 
03733     if {$command == "list"} {
03734     # ok, the next argument is a list command; we'll
03735     # rip it from args and append it to command to
03736     # create a unique internal command
03737     #
03738     # NB: because of the sloppy way we are doing this,
03739     # we'll also let the user enter our secret command
03740     # directly (eg: listinsert, listdelete), but we
03741     # won't document that fact
03742     set command "list-[lindex $args 0]"
03743     set args [lrange $args 1 end]
03744     }
03745 
03746     set result ""
03747 
03748     # many of these commands are just synonyms for specific
03749     # commands in one of the subwidgets. We'll get them out
03750     # of the way first, then do the custom commands.
03751     switch $command {
03752     bbox -
03753     delete -
03754     get -
03755     icursor -
03756     index -
03757     insert -
03758     scan -
03759     selection -
03760     xview {
03761         set result [eval $doWidgetCommand entry $command $args]
03762     }
03763     list-get    {set result [eval $doWidgetCommand listbox get $args]}
03764     list-index  {set result [eval $doWidgetCommand listbox index $args]}
03765     list-size   {set result [eval $doWidgetCommand listbox size $args]}
03766 
03767     select {
03768         if {[llength $args] == 1} {
03769         set index [lindex $args 0]
03770         set result [Select $widgets(this) $index]
03771         } else {
03772         error "usage: $w select index"
03773         }
03774     }
03775 
03776     subwidget {
03777         set knownWidgets [list button entry listbox dropdown vsb]
03778         if {[llength $args] == 0} {
03779         return $knownWidgets
03780         }
03781 
03782         set name [lindex $args 0]
03783         if {[lsearch $knownWidgets $name] != -1} {
03784         set result $widgets($name)
03785         } else {
03786         error "unknown subwidget $name"
03787         }
03788     }
03789 
03790     curselection {
03791         set result [eval $doWidgetCommand listbox curselection]
03792     }
03793 
03794     list-insert {
03795         eval $doWidgetCommand listbox insert $args
03796         set result [HandleScrollbar $w "grow"]
03797     }
03798 
03799     list-delete {
03800         eval $doWidgetCommand listbox delete $args
03801         set result [HandleScrollbar $w "shrink"]
03802     }
03803 
03804     toggle {
03805         # ignore this command if the widget is disabled...
03806         if {$options(-state) == "disabled"} return
03807 
03808         # pops down the list if it is not, hides it
03809         # if it is...
03810         if {[winfo ismapped $widgets(dropdown)]} {
03811         set result [$widgets(this) close]
03812         } else {
03813         set result [$widgets(this) open]
03814         }
03815     }
03816 
03817     open {
03818 
03819         # if this is an editable combobox, the focus should
03820         # be set to the entry widget
03821         if {$options(-editable)} {
03822         focus $widgets(entry)
03823         $widgets(entry) select range 0 end
03824         $widgets(entry) icur end
03825         }
03826 
03827         # if we are disabled, we won't allow this to happen
03828         if {$options(-state) == "disabled"} {
03829         return 0
03830         }
03831 
03832         # if there is a -opencommand, execute it now
03833         if {[string length $options(-opencommand)] > 0} {
03834         # hmmm... should I do a catch, or just let the normal
03835         # error handling handle any errors? For now, the latter...
03836         uplevel \#0 $options(-opencommand)
03837         }
03838 
03839         # compute the geometry of the window to pop up, and set
03840         # it, and force the window manager to take notice
03841         # (even if it is not presently visible).
03842         #
03843         # this isn't strictly necessary if the window is already
03844         # mapped, but we'll go ahead and set the geometry here
03845         # since its harmless and *may* actually reset the geometry
03846         # to something better in some weird case.
03847         set geometry [::combobox::ComputeGeometry $widgets(this)]
03848         wm geometry $widgets(dropdown) $geometry
03849         update idletasks
03850 
03851         # if we are already open, there's nothing else to do
03852         if {[winfo ismapped $widgets(dropdown)]} {
03853         return 0
03854         }
03855 
03856         # save the widget that currently has the focus; we'll restore
03857         # the focus there when we're done
03858         set oldFocus [focus]
03859 
03860         # ok, tweak the visual appearance of things and
03861         # make the list pop up
03862         $widgets(button) configure -relief sunken
03863         raise $widgets(dropdown) [winfo parent $widgets(this)]
03864         wm deiconify $widgets(dropdown)
03865         raise $widgets(dropdown)
03866 
03867         # force focus to the entry widget so we can handle keypress
03868         # events for traversal
03869         focus -force $widgets(entry)
03870 
03871         # select something by default, but only if its an
03872         # exact match...
03873         ::combobox::Find $widgets(this) 1
03874 
03875         # save the current grab state for the display containing
03876         # this widget. We'll restore it when we close the dropdown
03877         # list
03878         set status "none"
03879         set grab [grab current $widgets(this)]
03880         if {$grab != ""} {set status [grab status $grab]}
03881         set oldGrab [list $grab $status]
03882         unset grab status
03883 
03884         # *gasp* do a global grab!!! Mom always told me not to
03885         # do things like this, but sometimes a man's gotta do
03886         # what a man's gotta do.
03887         grab -global $widgets(this)
03888 
03889         # fake the listbox into thinking it has focus. This is
03890         # necessary to get scanning initialized properly in the
03891         # listbox.
03892         event generate $widgets(listbox) <B1-Enter>
03893 
03894         return 1
03895     }
03896 
03897     close {
03898         # if we are already closed, don't do anything...
03899         if {![winfo ismapped $widgets(dropdown)]} {
03900         return 0
03901         }
03902 
03903         # restore the focus and grab, but ignore any errors...
03904         # we're going to be paranoid and release the grab before
03905         # trying to set any other grab because we really really
03906         # really want to make sure the grab is released.
03907         catch {focus $oldFocus} result
03908         catch {grab release $widgets(this)}
03909         catch {
03910         set status [lindex $oldGrab 1]
03911         if {$status == "global"} {
03912             grab -global [lindex $oldGrab 0]
03913         } elseif {$status == "local"} {
03914             grab [lindex $oldGrab 0]
03915         }
03916         unset status
03917         }
03918 
03919         # hides the listbox
03920         $widgets(button) configure -relief raised
03921         wm withdraw $widgets(dropdown)
03922 
03923         # select the data in the entry widget. Not sure
03924         # why, other than observation seems to suggest that's
03925         # what windows widgets do.
03926         set editable [::combobox::GetBoolean $options(-editable)]
03927         if {$editable} {
03928         $widgets(entry) selection range 0 end
03929         $widgets(button) configure -relief raised
03930         }
03931 
03932 
03933         # magic tcl stuff (see tk.tcl in the distribution
03934         # lib directory)
03935         ::combobox::tkCancelRepeat
03936 
03937         return 1
03938     }
03939 
03940     cget {
03941         if {[llength $args] != 1} {
03942         error "wrong # args: should be $w cget option"
03943         }
03944         set opt [::combobox::Canonize $w option [lindex $args 0]]
03945 
03946         if {$opt == "-value"} {
03947         set result [$widgets(entry) get]
03948         } else {
03949         set result $options($opt)
03950         }
03951     }
03952 
03953     configure {
03954         set result [eval ::combobox::Configure {$w} $args]
03955     }
03956 
03957     default {
03958         error "bad option \"$command\""
03959     }
03960     }
03961 
03962     return $result
03963 }
03964 
03965 }
03966 
03967 namespace ::combobox {
03968 
03969 ret  {::combobox::combobox} (type w , type args) {
03970 variable widgetOptions
03971     variable widgetCommands
03972     variable scanCommands
03973     variable listCommands
03974 
03975     # perform a one time initialization
03976     if {![info exists widgetOptions]} {
03977     Init
03978     }
03979 
03980     # build it...
03981     eval Build $w $args
03982 
03983     # set some bindings...
03984     SetBindings $w
03985 
03986     # and we are done!
03987     return $w
03988 }
03989 
03990 }
03991 
03992 namespace ::combobox {
03993 
03994 ret  {::combobox::convert} (type w , type args) {
03995 set result {}
03996     if {![winfo exists $w]} {
03997     error "window \"$w\" doesn't exist"
03998     }
03999 
04000     while {[llength $args] > 0} {
04001     set option [lindex $args 0]
04002     set args [lrange $args 1 end]
04003 
04004     switch -exact -- $option {
04005         -x {
04006         set value [lindex $args 0]
04007         set args [lrange $args 1 end]
04008         set win $w
04009         while {[winfo class $win] != "Combobox"} {
04010             incr value [winfo x $win]
04011             set win [winfo parent $win]
04012             if {$win == "."} break
04013         }
04014         lappend result $value
04015         }
04016 
04017         -y {
04018         set value [lindex $args 0]
04019         set args [lrange $args 1 end]
04020         set win $w
04021         while {[winfo class $win] != "Combobox"} {
04022             incr value [winfo y $win]
04023             set win [winfo parent $win]
04024             if {$win == "."} break
04025         }
04026         lappend result $value
04027         }
04028 
04029         -w -
04030         -W {
04031         set win $w
04032         while {[winfo class $win] != "Combobox"} {
04033             set win [winfo parent $win]
04034             if {$win == "."} break;
04035         }
04036         lappend result $win
04037         }
04038     }
04039     }
04040     return $result
04041 }
04042 
04043 }
04044 
04045 namespace ::dom::libxml2 {
04046 
04047 ret  {::dom::libxml2::parse} (type xml , type args) {
04048 array set options {
04049     -keep normal
04050     -retainpath /*
04051     }
04052     array set options $args
04053 
04054     if {[catch {eval ::xml::parser -parser libxml2 [array get options]} parser]} {
04055     return -code error "unable to create XML parser due to \"$parser\""
04056     }
04057 
04058     if {[catch {$parser parse $xml} msg]} {
04059     return -code error $msg
04060     }
04061 
04062     set doc [$parser get document]
04063     set dom [dom::libxml2::adoptdocument $doc]
04064     $parser free
04065 
04066     return $dom
04067 }
04068 
04069 }
04070 
04071 namespace ::dom {
04072 
04073 ret  {::dom::parse} (type xml , type args) {
04074 return [eval ::dom::libxml2::parse [list $xml] $args]
04075 }
04076 
04077 }
04078 
04079 namespace ::sgml {
04080 
04081 ret  {::sgml::Boolean} (type value) {
04082 regsub {1|true|yes|on} $value 1 value
04083     regsub {0|false|no|off} $value 0 value
04084     return $value
04085 }
04086 
04087 }
04088 
04089 namespace ::sgml {
04090 
04091 ret  {::sgml::CModelMakeSyntaxTree} (type state , type spec) {
04092 upvar #0 $state var
04093     variable Wsp
04094     variable name
04095 
04096     # Translate the spec into a Tcl list.
04097 
04098     # none of the Tcl special characters are allowed in a content model spec.
04099     if {[regexp {\$|\[|\]|\{|\}} $spec]} {
04100     return -code error "illegal characters in specification"
04101     }
04102 
04103     regsub -all [format {(%s)[%s]*(\?|\*|\+)?[%s]*(,|\|)?} $name $Wsp $Wsp] $spec [format {%sCModelSTname %s {\1} {\2} {\3}} \n $state] spec
04104     regsub -all {\(} $spec "\nCModelSTopenParen $state " spec
04105     regsub -all [format {\)[%s]*(\?|\*|\+)?[%s]*(,|\|)?} $Wsp $Wsp] $spec [format {%sCModelSTcloseParen %s {\1} {\2}} \n $state] spec
04106 
04107     array set var {stack {} state start}
04108     eval $spec
04109 
04110     # Peel off the outer seq, its redundant
04111     return [lindex [lindex $var(stack) 1] 0]
04112 }
04113 
04114 }
04115 
04116 namespace ::sgml {
04117 
04118 ret  {::sgml::CModelMakeTransitionTable} (type state , type st) {
04119 upvar #0 $state var
04120 
04121     # Construct nullable, firstpos and lastpos functions
04122     array set var {number 0}
04123     foreach {nullable firstpos lastpos} [    TraverseDepth1st $state $st {
04124         # Evaluated for leaf nodes
04125         # Compute nullable(n)
04126         # Compute firstpos(n)
04127         # Compute lastpos(n)
04128         set nullable [nullable leaf $rep $name]
04129         set firstpos [list {} $var(number)]
04130         set lastpos [list {} $var(number)]
04131         set var(pos:$var(number)) $name
04132     } {
04133         # Evaluated for nonterminal nodes
04134         # Compute nullable, firstpos, lastpos
04135         set firstpos [firstpos $cs $firstpos $nullable]
04136         set lastpos  [lastpos  $cs $lastpos  $nullable]
04137         set nullable [nullable nonterm $rep $cs $nullable]
04138     }    ] break
04139 
04140     set accepting [incr var(number)]
04141     set var(pos:$accepting) #
04142 
04143     # var(pos:N) maps from position to symbol.
04144     # Construct reverse map for convenience.
04145     # NB. A symbol may appear in more than one position.
04146     # var is about to be reset, so use different arrays.
04147 
04148     foreach {pos symbol} [array get var pos:*] {
04149     set pos [lindex [split $pos :] 1]
04150     set pos2symbol($pos) $symbol
04151     lappend sym2pos($symbol) $pos
04152     }
04153 
04154     # Construct the followpos functions
04155     catch {unset var}
04156     followpos $state $st $firstpos $lastpos
04157 
04158     # Construct transition table
04159     # Dstates is [union $marked $unmarked]
04160     set unmarked [list [lindex $firstpos 1]]
04161     while {[llength $unmarked]} {
04162     set T [lindex $unmarked 0]
04163     lappend marked $T
04164     set unmarked [lrange $unmarked 1 end]
04165 
04166     # Find which input symbols occur in T
04167     set symbols {}
04168     foreach pos $T {
04169         if {$pos != $accepting && [lsearch $symbols $pos2symbol($pos)] < 0} {
04170         lappend symbols $pos2symbol($pos)
04171         }
04172     }
04173     foreach a $symbols {
04174         set U {}
04175         foreach pos $sym2pos($a) {
04176         if {[lsearch $T $pos] >= 0} {
04177             # add followpos($pos)
04178                 if {$var($pos) == {}} {
04179                     lappend U $accepting
04180                 } else {
04181                     eval lappend U $var($pos)
04182                 }
04183         }
04184         }
04185         set U [makeSet $U]
04186         if {[llength $U] && [lsearch $marked $U] < 0 && [lsearch $unmarked $U] < 0} {
04187         lappend unmarked $U
04188         }
04189         set Dtran($T,$a) $U
04190     }
04191     
04192     }
04193 
04194     return [list [array get Dtran] [array get sym2pos] $accepting]
04195 }
04196 
04197 }
04198 
04199 namespace ::sgml {
04200 
04201 ret  {::sgml::CModelParse} (type state , type value) {
04202 upvar #0 $state var
04203 
04204     # First build syntax tree
04205     set syntaxTree [CModelMakeSyntaxTree $state $value]
04206 
04207     # Build transition table
04208     set transitionTable [CModelMakeTransitionTable $state $syntaxTree]
04209 
04210     return [list $syntaxTree $transitionTable]
04211 }
04212 
04213 }
04214 
04215 namespace ::sgml {
04216 
04217 ret  {::sgml::CModelSTcloseParen} (type state , type rep , type cs , type args) {
04218 upvar #0 $state var
04219 
04220     if {[llength $args]} {
04221     return -code error "syntax error in specification: \"$args\""
04222     }
04223 
04224     set cp [lindex $var(stack) end]
04225     set var(stack) [lreplace $var(stack) end end]
04226     set var(state) [lreplace $var(state) end end]
04227     CModelSTcp $state $cp $rep $cs
04228 }
04229 
04230 }
04231 
04232 namespace ::sgml {
04233 
04234 ret  {::sgml::CModelSTcp} (type state , type cp , type rep , type cs) {
04235 upvar #0 $state var
04236 
04237     switch -glob -- [lindex $var(state) end]=$cs {
04238     start= {
04239         set var(state) [lreplace $var(state) end end end]
04240         # Add (dummy) grouping, either choice or sequence will do
04241         CModelSTcsSet $state ,
04242         CModelSTcpAdd $state $cp $rep
04243     }
04244     :choice= -
04245     :seq= {
04246         set var(state) [lreplace $var(state) end end end]
04247         CModelSTcpAdd $state $cp $rep
04248     }
04249     start=| -
04250     start=, {
04251         set var(state) [lreplace $var(state) end end [expr {$cs == "," ? ":seq" : ":choice"}]]
04252         CModelSTcsSet $state $cs
04253         CModelSTcpAdd $state $cp $rep
04254     }
04255     :choice=| -
04256     :seq=, {
04257         CModelSTcpAdd $state $cp $rep
04258     }
04259     :choice=, -
04260     :seq=| {
04261         return -code error "syntax error in specification: incorrect delimiter after \"$cp\", should be \"[expr {$cs == "," ? "|" : ","}]\""
04262     }
04263     end=* {
04264         return -code error "syntax error in specification: no delimiter before \"$cp\""
04265     }
04266     default {
04267         return -code error "syntax error"
04268     }
04269     }
04270 }
04271 
04272 }
04273 
04274 namespace ::sgml {
04275 
04276 ret  {::sgml::CModelSTcpAdd} (type state , type cp , type rep) {
04277 upvar #0 $state var
04278 
04279     if {[llength $var(stack)]} {
04280     set top [lindex $var(stack) end]
04281         lappend top [list $rep $cp]
04282     set var(stack) [lreplace $var(stack) end end $top]
04283     } else {
04284     set var(stack) [list $rep $cp]
04285     }
04286 }
04287 
04288 }
04289 
04290 namespace ::sgml {
04291 
04292 ret  {::sgml::CModelSTcsSet} (type state , type cs) {
04293 upvar #0 $state var
04294 
04295     set cs [expr {$cs == "," ? ":seq" : ":choice"}]
04296 
04297     if {[llength $var(stack)]} {
04298     set var(stack) [lreplace $var(stack) end end $cs]
04299     } else {
04300     set var(stack) [list $cs {}]
04301     }
04302 }
04303 
04304 }
04305 
04306 namespace ::sgml {
04307 
04308 ret  {::sgml::CModelSTname} (type state , type name , type rep , type cs , type args) {
04309 if {[llength $args]} {
04310     return -code error "syntax error in specification: \"$args\""
04311     }
04312 
04313     CModelSTcp $state $name $rep $cs
04314 }
04315 
04316 }
04317 
04318 namespace ::sgml {
04319 
04320 ret  {::sgml::CModelSTopenParen} (type state , type args) {
04321 upvar #0 $state var
04322 
04323     if {[llength $args]} {
04324     return -code error "syntax error in specification: \"$args\""
04325     }
04326 
04327     lappend var(state) start
04328     lappend var(stack) [list {} {}]
04329 }
04330 
04331 }
04332 
04333 namespace ::sgml {
04334 
04335 ret  {::sgml::DTD:ATTLIST} (type opts , type name , type attspec) {
04336 variable attlist_exp
04337     variable attlist_enum_exp
04338     variable attlist_fixed_exp
04339 
04340     array set options $opts
04341 
04342     # Parse the attribute list.  If it were regular, could just use foreach,
04343     # but some attributes may have values.
04344     regsub -all {([][$\\])} $attspec {\\\1} attspec
04345     regsub -all $attlist_exp $attspec "\}\nDTDAttribute {$options(-attlistdeclcommand)} $name $options(attlistdecls) {\\1} {\\2} {\\3} {} \{" attspec
04346     regsub -all $attlist_enum_exp $attspec "\}\nDTDAttribute {$options(-attlistdeclcommand)} $name $options(attlistdecls) {\\1} {\\2} {} {\\4} \{" attspec
04347     regsub -all $attlist_fixed_exp $attspec "\}\nDTDAttribute {$options(-attlistdeclcommand)} $name $options(attlistdecls) {\\1} {\\2} {\\3} {\\4} \{" attspec
04348 
04349     eval "noop \{$attspec\}"
04350 
04351     return {}
04352 }
04353 
04354 }
04355 
04356 namespace ::sgml {
04357 
04358 ret  {::sgml::DTD:ELEMENT} (type opts , type name , type modspec) {
04359 variable Wsp
04360     array set options $opts
04361 
04362     upvar #0 $options(elementdecls) elements
04363 
04364     if {$options(-validate) && [info exists elements($name)]} {
04365     eval $options(-errorcommand) [list elementdeclared "element \"$name\" already declared"]
04366     } else {
04367     switch -- $modspec {
04368         EMPTY {
04369             set elements($name) {}
04370         uplevel #0 $options(-elementdeclcommand) $name {{}}
04371         }
04372         ANY {
04373             set elements($name) *
04374         uplevel #0 $options(-elementdeclcommand) $name *
04375         }
04376         default {
04377         # Don't parse the content model for now,
04378         # just pass the model to the application
04379         if {0 && [regexp [format {^\([%s]*#PCDATA[%s]*(\|([^)]+))?[%s]*\)*[%s]*$} $Wsp $Wsp $Wsp $Wsp] discard discard mtoks]} {
04380             set cm($name) [list MIXED [split $mtoks |]]
04381         } elseif {0} {
04382             if {[catch {CModelParse $state(state) $value} result]} {
04383             eval $options(-errorcommand) [list element? $result]
04384             } else {
04385             set cm($id) [list ELEMENT $result]
04386             }
04387         } else {
04388             set elements($name) $modspec
04389             uplevel #0 $options(-elementdeclcommand) $name [list $modspec]
04390         }
04391         }
04392     }
04393     }
04394 }
04395 
04396 }
04397 
04398 namespace ::sgml {
04399 
04400 ret  {::sgml::DTD:ENTITY} (type opts , type name , type param , type value) {
04401 array set options $opts
04402 
04403     if {[string compare % $param]} {
04404     # Entity declaration - general or external
04405     upvar #0 $options(entities) ents
04406     upvar #0 $options(extentities) externals
04407 
04408     if {[info exists ents($name)] || [info exists externals($name)]} {
04409         eval $options(-warningcommand) entity [list "entity \"$name\" already declared"]
04410     } else {
04411         if {[catch {uplevel #0 $options(-parseentitydeclcommand) [list $value]} value]} {
04412         return -code error "unable to parse entity declaration due to \"$value\""
04413         }
04414         switch -glob [lindex $value 0],[lindex $value 3] {
04415         internal, {
04416             set ents($name) [EntitySubst [array get options] [lindex $value 1]]
04417             uplevel #0 $options(-entitydeclcommand) [list $name $ents($name)]
04418         }
04419         internal,* {
04420             return -code error "unexpected NDATA declaration"
04421         }
04422         external, {
04423             set externals($name) [lrange $value 1 2]
04424             uplevel #0 $options(-entitydeclcommand) [eval list $name [lrange $value 1 2]]
04425         }
04426         external,* {
04427             set externals($name) [lrange $value 1 3]
04428             uplevel #0 $options(-unparsedentitydeclcommand) [eval list $name [lrange $value 1 3]]
04429         }
04430         default {
04431             return -code error "internal error: unexpected parser state"
04432         }
04433         }
04434     }
04435     } else {
04436     # Parameter entity declaration
04437     upvar #0 $options(parameterentities) PEnts
04438     upvar #0 $options(externalparameterentities) ExtPEnts
04439 
04440     if {[info exists PEnts($name)] || [info exists ExtPEnts($name)]} {
04441         eval $options(-warningcommand) parameterentity [list "parameter entity \"$name\" already declared"]
04442     } else {
04443         if {[catch {uplevel #0 $options(-parseentitydeclcommand) [list $value]} value]} {
04444         return -code error "unable to parse parameter entity declaration due to \"$value\""
04445         }
04446         if {[string length [lindex $value 3]]} {
04447         return -code error "NDATA illegal in parameter entity declaration"
04448         }
04449         switch [lindex $value 0] {
04450         internal {
04451             # Substitute character references and PEs (XML: 4.5)
04452             set value [EntitySubst [array get options] [lindex $value 1]]
04453 
04454             set PEnts($name) $value
04455             uplevel #0 $options(-parameterentitydeclcommand) [list $name $value]
04456         }
04457         external -
04458         default {
04459             # Get the replacement text now.
04460             # Could wait until the first reference, but easier
04461             # to just do it now.
04462 
04463             set token [uri::geturl [uri::resolve $options(-baseuri) [lindex $value 1]]]
04464 
04465             set ExtPEnts($name) [lindex [array get $token data] 1]
04466             uplevel #0 $options(-parameterentitydeclcommand) [eval list $name [lrange $value 1 2]]
04467         }
04468         }
04469     }
04470     }
04471 }
04472 
04473 }
04474 
04475 namespace ::sgml {
04476 
04477 ret  {::sgml::DTD:NOTATION} (type opts , type name , type value) {
04478 return {}
04479 
04480     variable notation_exp
04481     upvar opts state
04482 
04483     if {[regexp $notation_exp $value x scheme data] == 2} {
04484     } else {
04485     eval $state(-errorcommand) [list notationvalue "notation value \"$value\" incorrectly specified"]
04486     }
04487 }
04488 
04489 }
04490 
04491 namespace ::sgml {
04492 
04493 ret  {::sgml::DTDAttribute} (type args) {
04494 # BUG: Some problems with parameter passing - deal with it later
04495     foreach {callback name var att type default value text} $args break
04496 
04497     upvar #0 $var atts
04498 
04499     if {[string length [string trim $text]]} {
04500     return -code error "unexpected text \"$text\" in attribute definition"
04501     }
04502 
04503     # What about overridden attribute defns?
04504     # A non-validating app may want to know about them
04505     # (eg. an editor)
04506     if {![info exists atts($name/$att)]} {
04507     set atts($name/$att) [list $type $default $value]
04508     uplevel #0 $callback [list $name $att $type $default $value]
04509     }
04510 
04511     return {}
04512 }
04513 
04514 }
04515 
04516 namespace ::sgml {
04517 
04518 ret  {::sgml::DeProtect} (type cmd , type text) {
04519 set text [lindex $text 0]
04520     if {[string compare {} $text]} {
04521     regsub -all {\\([]$[{}\\])} $text {\1} text
04522     uplevel #0 $cmd [list $text]
04523     }
04524 }
04525 
04526 }
04527 
04528 namespace ::sgml {
04529 
04530 ret  {::sgml::DeProtect1} (type cmd , type text) {
04531 if {[string compare {} $text]} {
04532     regsub -all {\\([]$[{}\\])} $text {\1} text
04533     uplevel #0 $cmd [list $text]
04534     }
04535 }
04536 
04537 }
04538 
04539 namespace ::sgml {
04540 
04541 ret  {::sgml::Entity} (type opts , type entityrefcmd , type pcdatacmd , type entities , type ref) {
04542 array set options $opts
04543     upvar #0 $options(-statevariable) state
04544 
04545     if {![string length $entities]} {
04546     set entities [namespace current]::EntityPredef
04547     }
04548 
04549     switch -glob -- $ref {
04550     %* {
04551         # Parameter entity - not recognised outside of a DTD
04552     }
04553     #x* {
04554         # Character entity - hex
04555         if {[catch {format %c [scan [string range $ref 2 end] %x tmp; set tmp]} char]} {
04556         return -code error "malformed character entity \"$ref\""
04557         }
04558         uplevel #0 $pcdatacmd [list $char]
04559 
04560         return {}
04561 
04562     }
04563     #* {
04564         # Character entity - decimal
04565         if {[catch {format %c [scan [string range $ref 1 end] %d tmp; set tmp]} char]} {
04566         return -code error "malformed character entity \"$ref\""
04567         }
04568         uplevel #0 $pcdatacmd [list $char]
04569 
04570         return {}
04571 
04572     }
04573     default {
04574         # General entity
04575         upvar #0 $entities map
04576         if {[info exists map($ref)]} {
04577 
04578         if {![regexp {<|&} $map($ref)]} {
04579 
04580             # Simple text replacement - optimise
04581             uplevel #0 $pcdatacmd [list $map($ref)]
04582 
04583             return {}
04584 
04585         }
04586 
04587         # Otherwise an additional round of parsing is required.
04588         # This only applies to XML, since HTML doesn't have general entities
04589 
04590         # Must parse the replacement text for start & end tags, etc
04591         # This text must be self-contained: balanced closing tags, and so on
04592 
04593         set tokenised [tokenise $map($ref) $::xml::tokExpr $::xml::substExpr]
04594         set options(-final) 0
04595         eval parseEvent [list $tokenised] [array get options]
04596 
04597         return {}
04598 
04599         } elseif {[string compare $entityrefcmd "::sgml::noop"]} {
04600 
04601         set result [uplevel #0 $entityrefcmd [list $ref]]
04602 
04603         if {[string length $result]} {
04604             uplevel #0 $pcdatacmd [list $result]
04605         }
04606 
04607         return {}
04608 
04609         } else {
04610 
04611         # Reconstitute entity reference
04612 
04613         uplevel #0 $options(-errorcommand) [list illegalentity "undefined entity reference \"$ref\""]
04614 
04615         return {}
04616 
04617         }
04618     }
04619     }
04620 
04621     # If all else fails leave the entity reference untouched
04622     uplevel #0 $pcdatacmd [list &$ref\;]
04623 
04624     return {}
04625 }
04626 
04627 }
04628 
04629 namespace ::sgml {
04630 
04631 ret  {::sgml::EntitySubst} (type opts , type value) {
04632 array set options $opts
04633 
04634     # Protect Tcl special characters
04635     regsub -all {([{}\\])} $value {\\\1} value
04636 
04637     # Find entity references
04638     regsub -all (&#\[0-9\]+|&#x\[0-9a-fA-F\]+|%${::sgml::Name})\; $value "\[EntitySubstValue [list $options(parameterentities)] {\\1}\]" value
04639 
04640     set result [subst $value]
04641 
04642     return $result
04643 }
04644 
04645 }
04646 
04647 namespace ::sgml {
04648 
04649 ret  {::sgml::EntitySubstValue} (type PEvar , type ref) {
04650 switch -glob -- $ref {
04651     &#x* {
04652         scan [string range $ref 3 end] %x hex
04653         return [format %c $hex]
04654     }
04655     &#* {
04656         return [format %c [string range $ref 2 end]]
04657     }
04658     %* {
04659         upvar #0 $PEvar PEs
04660         set ref [string range $ref 1 end]
04661         if {[info exists PEs($ref)]} {
04662         return $PEs($ref)
04663         } else {
04664         return -code error "parameter entity \"$ref\" not declared"
04665         }
04666     }
04667     default {
04668         return -code error "internal error - unexpected entity reference"
04669     }
04670     }
04671     return {}
04672 }
04673 
04674 }
04675 
04676 namespace ::sgml {
04677 
04678 ret  {::sgml::Error} (type args) {
04679 uplevel return -code error [list $args]
04680 }
04681 
04682 }
04683 
04684 namespace ::sgml {
04685 
04686 ret  {::sgml::Normalize} (type name , type req) {
04687 if {$req} {
04688     return [string toupper $name]
04689     } else {
04690     return $name
04691     }
04692 }
04693 
04694 }
04695 
04696 namespace ::sgml {
04697 
04698 ret  {::sgml::PCDATA} (type opts , type pcdata) {
04699 array set options $opts
04700 
04701     if {$options(-ignorewhitespace) &&  ![string length [string trim $pcdata]]} {
04702     return {}
04703     }
04704 
04705     if {![regexp ^[cl $::sgml::Char]*\$ $pcdata]} {
04706     upvar \#0 $options(-statevariable) state
04707     uplevel \#0 $options(-errorcommand) [list illegalcharacters "illegal, non-Unicode characters found in text \"$pcdata\" around line $state(line)"]
04708     }
04709 
04710     uplevel \#0 $options(-characterdatacommand) [list $pcdata]
04711 }
04712 
04713 }
04714 
04715 namespace ::sgml {
04716 
04717 ret  {::sgml::ParseDTD:EntityMode} (type opts , type modeVar , type replTextVar , type declVar , type valueVar , type textVar , type delimiter , type name , type param) {
04718 upvar 1 $modeVar mode
04719     upvar 1 $replTextVar replText
04720     upvar 1 $declVar decl
04721     upvar 1 $valueVar value
04722     upvar 1 $textVar text
04723     array set options $opts
04724 
04725     switch $mode {
04726     {} {
04727         # Pass through to normal processing section
04728     }
04729     entity {
04730         # Look for closing delimiter
04731         if {[regexp ([cl ^$delimiter]*)${delimiter}(.*) $decl discard val1 remainder]} {
04732         append replText <$val1
04733         DTD:ENTITY [array get options] $name [string trim $param] $delimiter$replText$delimiter
04734         set decl /
04735         set text $remainder\ $value>$text
04736         set value {}
04737         set mode {}
04738         } elseif {[regexp ([cl ^$delimiter]*)${delimiter}(.*) $value discard val2 remainder]} {
04739         append replText <$decl\ $val2
04740         DTD:ENTITY [array get options] $name [string trim $param] $delimiter$replText$delimiter
04741         set decl /
04742         set text $remainder>$text
04743         set value {}
04744         set mode {}
04745         } elseif {[regexp ([cl ^$delimiter]*)${delimiter}(.*) $text discard val3 remainder]} {
04746         append replText <$decl\ $value>$val3
04747         DTD:ENTITY [array get options] $name [string trim $param] $delimiter$replText$delimiter
04748         set decl /
04749         set text $remainder
04750         set value {}
04751         set mode {}
04752         } else {
04753 
04754         # Remain in entity mode
04755         append replText <$decl\ $value>$text
04756         return -code continue
04757 
04758         }
04759     }
04760 
04761     ignore {
04762         upvar #0 $options(-statevariable) state
04763 
04764         if {[regexp {]](.*)$} $decl discard remainder]} {
04765         set state(condSections) [lreplace $state(condSections) end end]
04766         set decl $remainder
04767         set mode {}
04768         } elseif {[regexp {]](.*)$} $value discard remainder]} {
04769         set state(condSections) [lreplace $state(condSections) end end]
04770         regexp <[cl $::sgml::Wsp]*($::sgml::Name)(.*) $remainder discard decl value
04771         set mode {}
04772         } elseif {[regexp {]]>(.*)$} $text discard remainder]} {
04773         set state(condSections) [lreplace $state(condSections) end end]
04774         set decl /
04775         set value {}
04776         set text $remainder
04777         #regexp <[cl $::sgml::Wsp]*($::sgml::Name)([cl ^>]*)>(.*) $remainder discard decl value text
04778         set mode {}
04779         } else {
04780         set decl /
04781         }
04782 
04783     }
04784 
04785     comment {
04786         # Look for closing comment delimiter
04787 
04788         upvar #0 $options(-statevariable) state
04789 
04790         if {[regexp (.*?)--(.*)\$ $decl discard data1 remainder]} {
04791         } elseif {[regexp (.*?)--(.*)\$ $value discard data1 remainder]} {
04792         } elseif {[regexp (.*?)--(.*)\$ $text discard data1 remainder]} {
04793         } else {
04794         # comment continues
04795         append state(commentdata) <$decl\ $value>$text
04796         set decl /
04797         set value {}
04798         set text {}
04799         }
04800     }
04801 
04802     }
04803 
04804     return {}
04805 }
04806 
04807 }
04808 
04809 namespace ::sgml {
04810 
04811 ret  {::sgml::ParseDTD:External} (type opts , type dtd) {
04812 variable MarkupDeclExpr
04813     variable MarkupDeclSub
04814     variable declExpr
04815 
04816     array set options $opts
04817     upvar #0 $options(parameterentities) PEnts
04818     upvar #0 $options(externalparameterentities) ExtPEnts
04819     upvar #0 $options(-statevariable) state
04820 
04821     # As with the internal DTD subset, watch out for
04822     # entities with angle brackets
04823     set mode {} ;# normal
04824     set delimiter {}
04825     set name {}
04826     set param {}
04827 
04828     set oldState 0
04829     catch {set oldState $state(inInternalDTD)}
04830     set state(inInternalDTD) 0
04831 
04832     # Initialise conditional section stack
04833     if {![info exists state(condSections)]} {
04834     set state(condSections) {}
04835     }
04836     set startCondSectionDepth [llength $state(condSections)]
04837 
04838     while {[string length $dtd]} {
04839     set progress 0
04840     set PEref {}
04841     if {![string compare $mode "ignore"]} {
04842         set progress 1
04843         if {[regexp {]]>(.*)} $dtd discard dtd]} {
04844         set remainder {}
04845         set mode {} ;# normal
04846         set state(condSections) [lreplace $state(condSections) end end]
04847         continue
04848         } else {
04849         uplevel #0 $options(-errorcommand) [list missingdelimiter "IGNORE conditional section closing delimiter not found"]
04850         }
04851     } elseif {[regexp ^(.*?)%($::sgml::Name)\;(.*)\$ $dtd discard data PEref remainder]} {
04852         set progress 1
04853     } else {
04854         set data $dtd
04855         set dtd {}
04856         set remainder {}
04857     }
04858 
04859     # Tokenize the DTD (so far)
04860 
04861     # Protect Tcl special characters
04862     regsub -all {([{}\\])} $data {\\\1} dataP
04863 
04864     set n [regsub -all $MarkupDeclExpr $dataP $MarkupDeclSub dataP]
04865 
04866     if {$n} {
04867         set progress 1
04868         # All but the last markup declaration should have no text
04869         set dataP [lrange "{} {} \{$dataP\}" 3 end]
04870         if {[llength $dataP] > 3} {
04871         foreach {decl value text} [lrange $dataP 0 [expr [llength $dataP] - 4]] {
04872             ParseDTD:EntityMode [array get options] mode replText decl value text $delimiter $name $param
04873             ParseDTD:ProcessMarkupDecl [array get options] decl value delimiter name mode repltextVar text param
04874 
04875             if {[string length [string trim $text]]} {
04876             # check for conditional section close
04877             if {[regexp {]]>(.*)$} $text discard text]} {
04878                 if {[string length [string trim $text]]} {
04879                 uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$text\""]
04880                 }
04881                 if {![llength $state(condSections)]} {
04882                 uplevel #0 $options(-errorcommand) [list illegalsection "extraneous conditional section close"]
04883                 }
04884                 set state(condSections) [lreplace $state(condSections) end end]
04885                 if {![string compare $mode "ignore"]} {
04886                 set mode {} ;# normal
04887                 }
04888             } else {
04889                 uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$text\""]
04890             }
04891             }
04892         }
04893         }
04894         # Do the last declaration
04895         foreach {decl value text} [lrange $dataP [expr [llength $dataP] - 3] end] {
04896         ParseDTD:EntityMode [array get options] mode replText decl value text $delimiter $name $param
04897         ParseDTD:ProcessMarkupDecl [array get options] decl value delimiter name mode repltextVar text param
04898         }
04899     }
04900 
04901     # Now expand the PE reference, if any
04902     switch -glob $mode,[string length $PEref],$n {
04903         ignore,0,* {
04904         set dtd $text
04905         }
04906         ignore,*,* {
04907         set dtd $text$remainder
04908         }
04909         *,0,0 {
04910         set dtd $data
04911         }
04912         *,0,* {
04913         set dtd $text
04914         }
04915         *,*,0 {
04916         if {[catch {append data $PEnts($PEref)}]} {
04917             if {[info exists ExtPEnts($PEref)]} {
04918             set externalParser [$options(-cmd) entityparser]
04919             $externalParser parse $ExtPEnts($PEref) -dtdsubset external
04920             #$externalParser free
04921             } else {
04922             uplevel #0 $options(-errorcommand) [list entityundeclared "parameter entity \"$PEref\" not declared"]
04923             }
04924         }
04925         set dtd $data$remainder
04926         }
04927         default {
04928         if {[catch {append text $PEnts($PEref)}]} {
04929             if {[info exists ExtPEnts($PEref)]} {
04930             set externalParser [$options(-cmd) entityparser]
04931             $externalParser parse $ExtPEnts($PEref) -dtdsubset external
04932             #$externalParser free
04933             } else {
04934             uplevel #0 $options(-errorcommand) [list entityundeclared "parameter entity \"$PEref\" not declared"]
04935             }
04936         }
04937         set dtd $text$remainder
04938         }
04939     }
04940 
04941     # Check whether a conditional section has been terminated
04942     if {[regexp {^(.*?)]]>(.*)$} $dtd discard t1 t2]} {
04943         if {![regexp <.*> $t1]} {
04944         if {[string length [string trim $t1]]} {
04945             uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$t1\""]
04946         }
04947         if {![llength $state(condSections)]} {
04948             uplevel #0 $options(-errorcommand) [list illegalsection "extraneous conditional section close"]
04949         }
04950         set state(condSections) [lreplace $state(condSections) end end]
04951         if {![string compare $mode "ignore"]} {
04952             set mode {} ;# normal
04953         }
04954         set dtd $t2
04955         set progress 1
04956         }
04957     }
04958 
04959     if {!$progress} {
04960         # No parameter entity references were found and 
04961         # the text does not contain a well-formed markup declaration
04962         # Avoid going into an infinite loop
04963         upvar #0 $options(-errorcommand) [list syntaxerror "external entity does not contain well-formed markup declaration"]
04964         break
04965     }
04966     }
04967 
04968     set state(inInternalDTD) $oldState
04969 
04970     # Check that conditional sections have been closed properly
04971     if {[llength $state(condSections)] > $startCondSectionDepth} {
04972     uplevel #0 $options(-errorcommand) [list syntaxerror "[lindex $state(condSections) end] conditional section not closed"]
04973     }
04974     if {[llength $state(condSections)] < $startCondSectionDepth} {
04975     uplevel #0 $options(-errorcommand) [list syntaxerror "too many conditional section closures"]
04976     }
04977 
04978     return {}
04979 }
04980 
04981 }
04982 
04983 namespace ::sgml {
04984 
04985 ret  {::sgml::ParseDTD:Internal} (type opts , type dtd) {
04986 variable MarkupDeclExpr
04987     variable MarkupDeclSub
04988 
04989     array set options {}
04990     array set options $opts
04991 
04992     upvar #0 $options(-statevariable) state
04993     upvar #0 $options(parameterentities) PEnts
04994     upvar #0 $options(externalparameterentities) ExtPEnts
04995 
04996     # Bug 583947: remove comments before further processing
04997     regsub -all {<!--.*?-->} $dtd {} dtd
04998 
04999     # Tokenize the DTD
05000 
05001     # Protect Tcl special characters
05002     regsub -all {([{}\\])} $dtd {\\\1} dtd
05003 
05004     regsub -all $MarkupDeclExpr $dtd $MarkupDeclSub dtd
05005 
05006     # Entities may have angle brackets in their replacement
05007     # text, which breaks the RE processing.  So, we must
05008     # use a similar technique to processing doc instances
05009     # to rebuild the declarations from the pieces
05010 
05011     set mode {} ;# normal
05012     set delimiter {}
05013     set name {}
05014     set param {}
05015 
05016     set state(inInternalDTD) 1
05017 
05018     # Process the tokens
05019     foreach {decl value text} [lrange "{} {} \{$dtd\}" 3 end] {
05020 
05021     # Keep track of line numbers
05022     incr state(line) [regsub -all \n $text {} discard]
05023 
05024     ParseDTD:EntityMode [array get options] mode replText decl value text $delimiter $name $param
05025 
05026     ParseDTD:ProcessMarkupDecl [array get options] decl value delimiter name mode replText text param
05027 
05028     # There may be parameter entity references between markup decls
05029 
05030     if {[regexp {%.*;} $text]} {
05031 
05032         # Protect Tcl special characters
05033         regsub -all {([{}\\])} $text {\\\1} text
05034 
05035         regsub -all %($::sgml::Name)\; $text "\} {\\1} \{" text
05036 
05037         set PElist "\{$text\}"
05038         set PElist [lreplace $PElist end end]
05039         foreach {text entref} $PElist {
05040         if {[string length [string trim $text]]} {
05041             uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text in internal DTD subset around line $state(line)"]
05042         }
05043 
05044         # Expand parameter entity and recursively parse
05045         # BUG: no checks yet for recursive entity references
05046 
05047         if {[info exists PEnts($entref)]} {
05048             set externalParser [$options(-cmd) entityparser]
05049             $externalParser parse $PEnts($entref) -dtdsubset internal
05050         } elseif {[info exists ExtPEnts($entref)]} {
05051             set externalParser [$options(-cmd) entityparser]
05052             $externalParser parse $ExtPEnts($entref) -dtdsubset external
05053             #$externalParser free
05054         } else {
05055             uplevel #0 $options(-errorcommand) [list illegalreference "reference to undeclared parameter entity \"$entref\""]
05056         }
05057         }
05058 
05059     }
05060 
05061     }
05062 
05063     return {}
05064 }
05065 
05066 }
05067 
05068 namespace ::sgml {
05069 
05070 ret  {::sgml::ParseDTD:ProcessMarkupDecl} (type opts , type declVar , type valueVar , type delimiterVar , type nameVar , type modeVar , type replTextVar , type textVar , type paramVar) {
05071 upvar 1 $modeVar mode
05072     upvar 1 $replTextVar replText
05073     upvar 1 $textVar text
05074     upvar 1 $declVar decl
05075     upvar 1 $valueVar value
05076     upvar 1 $nameVar name
05077     upvar 1 $delimiterVar delimiter
05078     upvar 1 $paramVar param
05079 
05080     variable declExpr
05081     variable ExternalEntityExpr
05082 
05083     array set options $opts
05084     upvar #0 $options(-statevariable) state
05085 
05086     switch -glob -- $decl {
05087 
05088     / {
05089         # continuation from entity processing
05090     }
05091 
05092     !ELEMENT {
05093         # Element declaration
05094         if {[regexp $declExpr $value discard tag cmodel]} {
05095         DTD:ELEMENT [array get options] $tag $cmodel
05096         } else {
05097         uplevel #0 $options(-errorcommand) [list illegaldeclaration "malformed element declaration around line $state(line)"]
05098         }
05099     }
05100 
05101     !ATTLIST {
05102         # Attribute list declaration
05103         variable declExpr
05104         if {[regexp $declExpr $value discard tag attdefns]} {
05105         if {[catch {DTD:ATTLIST [array get options] $tag $attdefns} err]} {
05106             #puts stderr "Stack trace: $::errorInfo\n***\n"
05107             # Atttribute parsing has bugs at the moment
05108             #return -code error "$err around line $state(line)"
05109             return {}
05110         }
05111         } else {
05112         uplevel #0 $options(-errorcommand) [list illegaldeclaration "malformed attribute list declaration around line $state(line)"]
05113         }
05114     }
05115 
05116     !ENTITY {
05117         # Entity declaration
05118         variable EntityExpr
05119 
05120         if {[regexp $EntityExpr $value discard param name value]} {
05121 
05122         # Entity replacement text may have a '>' character.
05123         # In this case, the real delimiter will be in the following
05124         # text.  This is complicated by the possibility of there
05125         # being several '<','>' pairs in the replacement text.
05126         # At this point, we are searching for the matching quote delimiter.
05127 
05128         if {[regexp $ExternalEntityExpr $value]} {
05129             DTD:ENTITY [array get options] $name [string trim $param] $value
05130         } elseif {[regexp ("|')(.*?)\\1(.*) $value discard delimiter replText value]} {
05131 
05132             if {[string length [string trim $value]]} {
05133             uplevel #0 $options(-errorcommand) [list illegaldeclaration "malformed entity declaration around line $state(line)"]
05134             } else {
05135             DTD:ENTITY [array get options] $name [string trim $param] $delimiter$replText$delimiter
05136             }
05137         } elseif {[regexp ("|')(.*) $value discard delimiter replText]} {
05138             append replText >$text
05139             set text {}
05140             set mode entity
05141         } else {
05142             uplevel #0 $options(-errorcommand) [list illegaldeclaration "no delimiter for entity declaration around line $state(line)"]
05143         }
05144 
05145         } else {
05146         uplevel #0 $options(-errorcommand) [list illegaldeclaration "malformed entity declaration around line $state(line)"]
05147         }
05148     }
05149 
05150     !NOTATION {
05151         # Notation declaration
05152         if {[regexp $declExpr param discard tag notation]} {
05153         DTD:ENTITY [array get options] $tag $notation
05154         } else {
05155         uplevel #0 $options(-errorcommand) [list illegaldeclaration "malformed entity declaration around line $state(line)"]
05156         }
05157     }
05158 
05159     !--* {
05160         # Start of a comment
05161 
05162         if {[regexp !--(.*?)--\$ $decl discard data]} {
05163         if {[string length [string trim $value]]} {
05164             uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$value\""]
05165         }
05166         uplevel #0 $options(-commentcommand) [list $data]
05167         set decl /
05168         set value {}
05169         } elseif {[regexp -- ^(.*?)--\$ $value discard data2]} {
05170         regexp !--(.*)\$ $decl discard data1
05171         uplevel #0 $options(-commentcommand) [list $data1\ $data2]
05172         set decl /
05173         set value {}
05174         } elseif {[regexp (.*?)-->(.*)\$ $text discard data3 remainder]} {
05175         regexp !--(.*)\$ $decl discard data1
05176         uplevel #0 $options(-commentcommand) [list $data1\ $value>$data3]
05177         set decl /
05178         set value {}
05179         set text $remainder
05180         } else {
05181         regexp !--(.*)\$ $decl discard data1
05182         set state(commentdata) $data1\ $value>$text
05183         set decl /
05184         set value {}
05185         set text {}
05186         set mode comment
05187         }
05188     }
05189 
05190     !*INCLUDE* -
05191     !*IGNORE* {
05192         if {$state(inInternalDTD)} {
05193         uplevel #0 $options(-errorcommand) [list illegalsection "conditional section not permitted in internal DTD subset around line $state(line)"]
05194         }
05195 
05196         if {[regexp {^!\[INCLUDE\[(.*)} $decl discard remainder]} {
05197         # Push conditional section stack, popped by ]]> sequence
05198 
05199         if {[regexp {(.*?)]]$} $remainder discard r2]} {
05200             # section closed immediately
05201             if {[string length [string trim $r2]]} {
05202             uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$r2\" in conditional section"]
05203             }
05204         } elseif {[regexp {(.*?)]](.*)} $value discard r2 r3]} {
05205             # section closed immediately
05206             if {[string length [string trim $r2]]} {
05207             uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$r2\" in conditional section"]
05208             }
05209             if {[string length [string trim $r3]]} {
05210             uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$r3\" in conditional section"]
05211             }
05212         } else {
05213 
05214             lappend state(condSections) INCLUDE
05215 
05216             set parser [$options(-cmd) entityparser]
05217             $parser parse $remainder\ $value> -dtdsubset external
05218             #$parser free
05219 
05220             if {[regexp {(.*?)]]>(.*)} $text discard t1 t2]} {
05221             if {[string length [string trim $t1]]} {
05222                 uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$t1\""]
05223             }
05224             if {![llength $state(condSections)]} {
05225                 uplevel #0 $options(-errorcommand) [list illegalsection "extraneous conditional section close"]
05226             }
05227             set state(condSections) [lreplace $state(condSections) end end]
05228             set text $t2
05229             }
05230 
05231         }
05232         } elseif {[regexp {^!\[IGNORE\[(.*)} $decl discard remainder]} {
05233         # Set ignore mode.  Still need a stack
05234         set mode ignore
05235 
05236         if {[regexp {(.*?)]]$} $remainder discard r2]} {
05237             # section closed immediately
05238             if {[string length [string trim $r2]]} {
05239             uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$r2\" in conditional section"]
05240             }
05241         } elseif {[regexp {(.*?)]](.*)} $value discard r2 r3]} {
05242             # section closed immediately
05243             if {[string length [string trim $r2]]} {
05244             uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$r2\" in conditional section"]
05245             }
05246             if {[string length [string trim $r3]]} {
05247             uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$r3\" in conditional section"]
05248             }
05249         } else {
05250             
05251             lappend state(condSections) IGNORE
05252 
05253             if {[regexp {(.*?)]]>(.*)} $text discard t1 t2]} {
05254             if {[string length [string trim $t1]]} {
05255                 uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$t1\""]
05256             }
05257             if {![llength $state(condSections)]} {
05258                 uplevel #0 $options(-errorcommand) [list illegalsection "extraneous conditional section close"]
05259             }
05260             set state(condSections) [lreplace $state(condSections) end end]
05261             set text $t2
05262             }
05263 
05264         }
05265         } else {
05266         uplevel #0 $options(-errorcommand) [list illegaldeclaration "illegal markup declaration \"$decl\" around line $state(line)"]
05267         }
05268 
05269     }
05270 
05271     default {
05272         if {[regexp {^\?(.*)} $decl discard target]} {
05273         # Processing instruction
05274         } else {
05275         uplevel #0 $options(-errorcommand) [list illegaldeclaration "illegal markup declaration \"$decl\""]
05276         }
05277     }
05278     }
05279 
05280     return {}
05281 }
05282 
05283 }
05284 
05285 namespace ::sgml {
05286 
05287 ret  {::sgml::ParseEvent:DocTypeDecl} (type opts , type docEl , type pubId , type sysId , type intSSet) {
05288 array set options {}
05289     array set options $opts
05290 
05291     set code [catch {uplevel #0 $options(-doctypecommand) [list $docEl $pubId $sysId $intSSet]} err]
05292     switch $code {
05293     3 {
05294         # break
05295         return {}
05296     }
05297     0 -
05298     4 {
05299         # continue
05300     }
05301     default {
05302         return -code $code $err
05303     }
05304     }
05305 
05306     # Otherwise we'll parse the DTD and report it piecemeal
05307 
05308     # The internal DTD subset is processed first (XML 2.8)
05309     # During this stage, parameter entities are only allowed
05310     # between markup declarations
05311 
05312     ParseDTD:Internal [array get options] $intSSet
05313 
05314     # The external DTD subset is processed last (XML 2.8)
05315     # During this stage, parameter entities may occur anywhere
05316 
05317     # We must resolve the external identifier to obtain the
05318     # DTD data.  The application may supply its own resolver.
05319 
05320     if {[string length $pubId] || [string length $sysId]} {
05321     uplevel #0 $options(-externalentitycommand) [list $options(-cmd) $options(-baseuri) $sysId $pubId]
05322     }
05323 
05324     return {}
05325 }
05326 
05327 }
05328 
05329 namespace ::sgml {
05330 
05331 ret  {::sgml::ParseEvent:ElementClose} (type tag , type opts , type args) {
05332 array set options $opts
05333     upvar #0 $options(-statevariable) state
05334     array set cfg {-empty 0}
05335     array set cfg $args
05336 
05337     # WF check
05338     if {[string compare $tag [lindex $state(stack) end]]} {
05339     uplevel #0 $options(-errorcommand) [list illegalendtag "end tag \"$tag\" does not match open element \"[lindex $state(stack) end]\" around line $state(line)"]
05340     return
05341     }
05342 
05343     # Check whether this element has an expanded name
05344     upvar #0 $options(namespaces) namespaces
05345     set ns {}
05346     if {[regexp {([^:]+):(.*)$} $tag discard prefix tag]} {
05347     set nsuri $namespaces([lindex [lsort -dictionary -decreasing [array names namespaces $prefix,*]] 0])
05348     set ns [list -namespace $nsuri]
05349     } elseif {[llength $state(defaultNSURI)]} {
05350     set ns [list -namespace [lindex $state(defaultNSURI) end]]
05351     }
05352 
05353     # Pop namespace stacks, if any
05354     if {[llength $state(defaultNS)]} {
05355     if {[llength $state(stack)] == [lindex $state(defaultNS) end]} {
05356         set state(defaultNS) [lreplace $state(defaultNS) end end]
05357     }
05358     }
05359     foreach nsspec [array names namespaces *,[llength $state(stack)]] {
05360     unset namespaces($nsspec)
05361     }
05362 
05363     # Update state
05364     set state(stack) [lreplace $state(stack) end end]
05365 
05366     set empty {}
05367     if {$cfg(-empty) && $options(-reportempty)} {
05368     set empty {-empty 1}
05369     }
05370 
05371     # Invoke callback
05372     # Mats: Shall be same as sgml::ParseEvent:ElementOpen to handle exceptions in callback.
05373     set code [catch {uplevel #0 $options(-elementendcommand) [list $tag] $empty $ns} msg]
05374     return -code $code -errorinfo $::errorInfo $msg
05375 }
05376 
05377 }
05378 
05379 namespace ::sgml {
05380 
05381 ret  {::sgml::ParseEvent:ElementOpen} (type tag , type attr , type opts , type args) {
05382 variable Name
05383     variable Wsp
05384 
05385     array set options $opts
05386     upvar #0 $options(-statevariable) state
05387     array set cfg {-empty 0}
05388     array set cfg $args
05389     set handleEmpty 0
05390 
05391     if {$options(-normalize)} {
05392     set tag [string toupper $tag]
05393     }
05394 
05395     # Update state
05396     lappend state(stack) $tag
05397 
05398     # Parse attribute list into a key-value representation
05399     if {[string compare $options(-parseattributelistcommand) {}]} {
05400     if {[catch {uplevel #0 $options(-parseattributelistcommand) [list $opts $attr]} attr]} {
05401         if {[string compare [lindex $attr 0] "unterminated attribute value"]} {
05402         uplevel #0 $options(-errorcommand) [list unterminatedattribute "$attr around line $state(line)"]
05403         set attr {}
05404         } else {
05405 
05406         # It is most likely that a ">" character was in an attribute value.
05407         # This manifests itself by ">" appearing in the element's text.
05408         # In this case the callback should return a three element list;
05409         # the message "unterminated attribute value", the attribute list it
05410         # did manage to parse and the remainder of the attribute list.
05411 
05412         foreach {msg attlist brokenattr} $attr break
05413 
05414         upvar text elemText
05415         if {[string first > $elemText] >= 0} {
05416 
05417             # Now piece the attribute list back together
05418             regexp [cl $Wsp]*($Name)[cl $Wsp]*=[cl $Wsp]*("|')(.*) $brokenattr discard attname delimiter attvalue
05419             regexp (.*)>([cl ^>]*)\$ $elemText discard remattlist elemText
05420             regexp ([cl ^$delimiter]*)${delimiter}(.*) $remattlist discard remattvalue remattlist
05421 
05422             # Gotcha: watch out for empty element syntax
05423             if {[string match */ [string trimright $remattlist]]} {
05424             set remattlist [string range $remattlist 0 end-1]
05425             set handleEmpty 1
05426             set cfg(-empty) 1
05427             }
05428 
05429             append attvalue >$remattvalue
05430             lappend attlist $attname $attvalue
05431 
05432             # Complete parsing the attribute list
05433             if {[catch {uplevel #0 $options(-parseattributelistcommand) [list $options(-statevariable) $remattlist]} attr]} {
05434             uplevel #0 $options(-errorcommand) [list unterminatedattribute "$attr around line $state(line)"]
05435             set attr {}
05436             set attlist {}
05437             } else {
05438             eval lappend attlist $attr
05439             }
05440 
05441             set attr $attlist
05442 
05443         } else {
05444             uplevel #0 $options(-errorcommand) [list unterminatedattribute "$attr around line $state(line)"]
05445             set attr {}
05446         }
05447         }
05448     }
05449     }
05450 
05451     set empty {}
05452     if {$cfg(-empty) && $options(-reportempty)} {
05453     set empty {-empty 1}
05454     }
05455 
05456     # Check for namespace declarations
05457     upvar #0 $options(namespaces) namespaces
05458     set nsdecls {}
05459     if {[llength $attr]} {
05460     array set attrlist $attr
05461     foreach {attrName attrValue} [array get attrlist xmlns*] {
05462         unset attrlist($attrName)
05463         set colon [set prefix {}]
05464         if {[regexp {^xmlns(:(.+))?$} $attrName discard colon prefix]} {
05465         switch -glob [string length $colon],[string length $prefix] {
05466             0,0 {
05467             # default NS declaration
05468             lappend state(defaultNSURI) $attrValue
05469             lappend state(defaultNS) [llength $state(stack)]
05470             lappend nsdecls $attrValue {}
05471             }
05472             0,* {
05473             # Huh?
05474             }
05475             *,0 {
05476             # Error
05477             uplevel #0 $state(-warningcommand) "no prefix specified for namespace URI \"$attrValue\" in element \"$tag\""
05478             }
05479             default {
05480             set namespaces($prefix,[llength $state(stack)]) $attrValue
05481             lappend nsdecls $attrValue $prefix
05482             }
05483         }
05484         }
05485     }
05486     if {[llength $nsdecls]} {
05487         set nsdecls [list -namespacedecls $nsdecls]
05488     }
05489     set attr [array get attrlist]
05490     }
05491 
05492     # Check whether this element has an expanded name
05493     set ns {}
05494     if {[regexp {([^:]+):(.*)$} $tag discard prefix tag]} {
05495     set nsspec [lsort -dictionary -decreasing [array names namespaces $prefix,*]]
05496     if {[llength $nsspec]} {
05497         set nsuri $namespaces([lindex $nsspec 0])
05498         set ns [list -namespace $nsuri]
05499     } else {
05500         uplevel #0 $options(-errorcommand) [list namespaceundeclared "no namespace declared for prefix \"$prefix\" in element $tag"]
05501     }
05502     } elseif {[llength $state(defaultNSURI)]} {
05503     set ns [list -namespace [lindex $state(defaultNSURI) end]]
05504     }
05505 
05506     # Invoke callback
05507     set code [catch {uplevel #0 $options(-elementstartcommand) [list $tag $attr] $empty $ns $nsdecls} msg]
05508 
05509     # Sometimes empty elements must be handled here (see above)
05510     if {$code == 0 && $handleEmpty} {
05511     ParseEvent:ElementClose $tag $opts -empty 1
05512     }
05513 
05514     return -code $code -errorinfo $::errorInfo $msg
05515 }
05516 
05517 }
05518 
05519 namespace ::sgml {
05520 
05521 ret  {::sgml::ParserDelete} (type var) {
05522 upvar #0 $var state
05523 
05524     if {![info exists state]} {
05525     return -code error "unknown parser"
05526     }
05527 
05528     catch {unset $state(entities)}
05529     catch {unset $state(parameterentities)}
05530     catch {unset $state(elementdecls)}
05531     catch {unset $state(attlistdecls)}
05532     catch {unset $state(notationdecls)}
05533     catch {unset $state(namespaces)}
05534 
05535     unset state
05536 
05537     return {}
05538 }
05539 
05540 }
05541 
05542 namespace ::sgml {
05543 
05544 ret  {::sgml::ResolveEntity} (type cmd , type base , type sysId , type pubId) {
05545 variable ParseEventNum
05546 
05547     if {[catch {uri::resolve $base $sysId} url]} {
05548     return -code error "unable to resolve system identifier \"$sysId\""
05549     }
05550     if {[catch {uri::geturl $url} token]} {
05551     return -code error "unable to retrieve external entity \"$url\" for system identifier \"$sysId\""
05552     }
05553 
05554     upvar #0 $token data
05555 
05556     set parser [uplevel #0 $cmd entityparser]
05557 
05558     set body {}
05559     catch {set body $data(body)}
05560     catch {set body $data(data)}
05561     if {[string length $body]} {
05562     uplevel #0 $parser parse [list $body] -dtdsubset external
05563     }
05564     $parser free
05565 
05566     return {}
05567 }
05568 
05569 }
05570 
05571 namespace ::sgml {
05572 
05573 ret  {::sgml::TraverseDepth1st} (type state , type t , type leaf , type nonTerm) {
05574 upvar #0 $state var
05575 
05576     set nullable {}
05577     set firstpos {}
05578     set lastpos {}
05579 
05580     switch -- [lindex [lindex $t 1] 0] {
05581     :seq -
05582     :choice {
05583         set rep [lindex $t 0]
05584         set cs [lindex [lindex $t 1] 0]
05585 
05586         foreach child [lrange [lindex $t 1] 1 end] {
05587         foreach {childNullable childFirstpos childLastpos}  [TraverseDepth1st $state $child $leaf $nonTerm] break
05588         lappend nullable $childNullable
05589         lappend firstpos $childFirstpos
05590         lappend lastpos  $childLastpos
05591         }
05592 
05593         eval $nonTerm
05594     }
05595     default {
05596         incr var(number)
05597         set rep [lindex [lindex $t 0] 0]
05598         set name [lindex [lindex $t 1] 0]
05599         eval $leaf
05600     }
05601     }
05602 
05603     return [list $nullable $firstpos $lastpos]
05604 }
05605 
05606 }
05607 
05608 namespace ::sgml {
05609 
05610 ret  {::sgml::cl} (type x) {
05611 return "\[$x\]"
05612 }
05613 
05614 }
05615 
05616 namespace ::sgml {
05617 
05618 ret  {::sgml::firstpos} (type cs , type firstpos , type nullable) {
05619 switch -- $cs {
05620     :seq {
05621         set result [lindex [lindex $firstpos 0] 1]
05622         for {set i 0} {$i < [llength $nullable]} {incr i} {
05623             if {[lindex [lindex $nullable $i] 1]} {
05624                 eval lappend result [lindex [lindex $firstpos [expr $i + 1]] 1]
05625         } else {
05626             break
05627         }
05628         }
05629     }
05630     :choice {
05631         foreach child $firstpos {
05632         eval lappend result $child
05633         }
05634     }
05635     }
05636 
05637     return [list $firstpos [makeSet $result]]
05638 }
05639 
05640 }
05641 
05642 namespace ::sgml {
05643 
05644 ret  {::sgml::followpos} (type state , type st , type firstpos , type lastpos) {
05645 upvar #0 $state var
05646 
05647     switch -- [lindex [lindex $st 1] 0] {
05648     :seq {
05649         for {set i 1} {$i < [llength [lindex $st 1]]} {incr i} {
05650             followpos $state [lindex [lindex $st 1] $i]          [lindex [lindex $firstpos 0] [expr $i - 1]]     [lindex [lindex $lastpos 0] [expr $i - 1]]
05651             foreach pos [lindex [lindex [lindex $lastpos 0] [expr $i - 1]] 1] {
05652             eval lappend var($pos) [lindex [lindex [lindex $firstpos 0] $i] 1]
05653             set var($pos) [makeSet $var($pos)]
05654             }
05655         }
05656     }
05657     :choice {
05658         for {set i 1} {$i < [llength [lindex $st 1]]} {incr i} {
05659         followpos $state [lindex [lindex $st 1] $i]          [lindex [lindex $firstpos 0] [expr $i - 1]]     [lindex [lindex $lastpos 0] [expr $i - 1]]
05660         }
05661     }
05662     default {
05663         # No action at leaf nodes
05664     }
05665     }
05666 
05667     switch -- [lindex $st 0] {
05668     ? {
05669         # We having nothing to do here ! Doing the same as
05670         # for * effectively converts this qualifier into the other.
05671     }
05672     * {
05673         foreach pos [lindex $lastpos 1] {
05674         eval lappend var($pos) [lindex $firstpos 1]
05675         set var($pos) [makeSet $var($pos)]
05676         }
05677     }
05678     }
05679 }
05680 
05681 }
05682 
05683 namespace ::sgml {
05684 
05685 ret  {::sgml::identity} (type a) {
05686 return $a
05687 }
05688 
05689 }
05690 
05691 namespace ::sgml {
05692 
05693 ret  {::sgml::lastpos} (type cs , type lastpos , type nullable) {
05694 switch -- $cs {
05695     :seq {
05696         set result [lindex [lindex $lastpos end] 1]
05697         for {set i [expr [llength $nullable] - 1]} {$i >= 0} {incr i -1} {
05698         if {[lindex [lindex $nullable $i] 1]} {
05699             eval lappend result [lindex [lindex $lastpos $i] 1]
05700         } else {
05701             break
05702         }
05703         }
05704     }
05705     :choice {
05706         foreach child $lastpos {
05707         eval lappend result $child
05708         }
05709     }
05710     }
05711 
05712     return [list $lastpos [makeSet $result]]
05713 }
05714 
05715 }
05716 
05717 namespace ::sgml {
05718 
05719 ret  {::sgml::makeSet} (type s) {
05720 foreach r $s {
05721     if {[llength $r]} {
05722         set unique($r) {}
05723     }
05724     }
05725     return [array names unique]
05726 }
05727 
05728 }
05729 
05730 namespace ::sgml {
05731 
05732 ret  {::sgml::noop} (type args) {
05733 return 0
05734 }
05735 
05736 }
05737 
05738 namespace ::sgml {
05739 
05740 ret  {::sgml::nullable} (type nodeType , type rep , type name , optional subtree ={)} {
05741 switch -glob -- $rep:$nodeType {
05742     :leaf -
05743     +:leaf {
05744         return [list {} 0]
05745     }
05746     \\*:leaf -
05747     \\?:leaf {
05748         return [list {} 1]
05749     }
05750     \\*:nonterm -
05751     \\?:nonterm {
05752         return [list $subtree 1]
05753     }
05754     :nonterm -
05755     +:nonterm {
05756         switch -- $name {
05757         :choice {
05758             set result 0
05759             foreach child $subtree {
05760             set result [expr $result || [lindex $child 1]]
05761             }
05762         }
05763         :seq {
05764             set result 1
05765             foreach child $subtree {
05766             set result [expr $result && [lindex $child 1]]
05767             }
05768         }
05769         }
05770         return [list $subtree $result]
05771     }
05772     }
05773 }
05774 
05775 }
05776 
05777 namespace ::sgml {
05778 
05779 ret  {::sgml::parseEvent} (type sgml , type args) {
05780 variable Wsp
05781     variable noWsp
05782     variable Nmtoken
05783     variable Name
05784     variable ParseEventNum
05785     variable StdOptions
05786 
05787     array set options [array get StdOptions]
05788     catch {array set options $args}
05789 
05790     # Mats:
05791     # If the data is not final then there must be a variable to persistently store the parse state.
05792     if {!$options(-final) && ![info exists options(-statevariable)]} {
05793     return -code error {option "-statevariable" required if not final}
05794     }
05795     
05796     foreach {opt value} [array get options *command] {
05797     if {[string compare $opt "-externalentitycommand"] && ![string length $value]} {
05798         set options($opt) [namespace current]::noop
05799     }
05800     }
05801 
05802     if {![info exists options(-statevariable)]} {
05803     set options(-statevariable) [namespace current]::ParseEvent[incr ParseEventNum]
05804     }
05805     if {![info exists options(entities)]} {
05806     set options(entities) [namespace current]::Entities$ParseEventNum
05807     array set $options(entities) [array get [namespace current]::EntityPredef]
05808     }
05809     if {![info exists options(extentities)]} {
05810     set options(extentities) [namespace current]::ExtEntities$ParseEventNum
05811     }
05812     if {![info exists options(parameterentities)]} {
05813     set options(parameterentities) [namespace current]::ParamEntities$ParseEventNum
05814     }
05815     if {![info exists options(externalparameterentities)]} {
05816     set options(externalparameterentities) [namespace current]::ExtParamEntities$ParseEventNum
05817     }
05818     if {![info exists options(elementdecls)]} {
05819     set options(elementdecls) [namespace current]::ElementDecls$ParseEventNum
05820     }
05821     if {![info exists options(attlistdecls)]} {
05822     set options(attlistdecls) [namespace current]::AttListDecls$ParseEventNum
05823     }
05824     if {![info exists options(notationdecls)]} {
05825     set options(notationdecls) [namespace current]::NotationDecls$ParseEventNum
05826     }
05827     if {![info exists options(namespaces)]} {
05828     set options(namespaces) [namespace current]::Namespaces$ParseEventNum
05829     }
05830 
05831     # For backward-compatibility
05832     catch {set options(-baseuri) $options(-baseurl)}
05833 
05834     # Choose an external entity resolver
05835 
05836     if {![string length $options(-externalentitycommand)]} {
05837     if {$options(-validate)} {
05838         set options(-externalentitycommand) [namespace code ResolveEntity]
05839     } else {
05840         set options(-externalentitycommand) [namespace code noop]
05841     }
05842     }
05843 
05844     upvar #0 $options(-statevariable) state
05845     upvar #0 $options(entities) entities
05846 
05847     # Mats:
05848     # The problem is that the state is not maintained when -final 0 !
05849     # I've switched back to an older version here. 
05850     
05851     if {![info exists state(line)]} {
05852     # Initialise the state variable
05853     array set state {
05854         mode normal
05855         haveXMLDecl 0
05856         haveDocElement 0
05857         inDTD 0
05858         context {}
05859         stack {}
05860         line 0
05861         defaultNS {}
05862         defaultNSURI {}
05863     }
05864     }
05865 
05866     foreach {tag close param text} $sgml {
05867 
05868     # Keep track of lines in the input
05869     incr state(line) [regsub -all \n $param {} discard]
05870     incr state(line) [regsub -all \n $text {} discard]
05871 
05872     # If the current mode is cdata or comment then we must undo what the
05873     # regsub has done to reconstitute the data
05874 
05875     set empty {}
05876     switch $state(mode) {
05877         comment {
05878         # This had "[string length $param] && " as a guard -
05879         # can't remember why :-(
05880         if {[regexp ([cl ^-]*)--\$ $tag discard comm1]} {
05881             # end of comment (in tag)
05882             set tag {}
05883             set close {}
05884             set state(mode) normal
05885             DeProtect1 $options(-commentcommand) $state(commentdata)<$comm1
05886             unset state(commentdata)
05887         } elseif {[regexp ([cl ^-]*)--\$ $param discard comm1]} {
05888             # end of comment (in attributes)
05889             DeProtect1 $options(-commentcommand) $state(commentdata)<$close$tag>$comm1
05890             unset state(commentdata)
05891             set tag {}
05892             set param {}
05893             set close {}
05894             set state(mode) normal
05895         } elseif {[regexp ([cl ^-]*)-->(.*) $text discard comm1 text]} {
05896             # end of comment (in text)
05897             DeProtect1 $options(-commentcommand) $state(commentdata)<$close$tag$param>$comm1
05898             unset state(commentdata)
05899             set tag {}
05900             set param {}
05901             set close {}
05902             set state(mode) normal
05903         } else {
05904             # comment continues
05905             append state(commentdata) <$close$tag$param>$text
05906             continue
05907         }
05908         }
05909         cdata {
05910         if {[string length $param] && [regexp ([cl ^\]]*)\]\][cl $Wsp]*\$ $tag discard cdata1]} {
05911             # end of CDATA (in tag)
05912             PCDATA [array get options] $state(cdata)<[subst -nocommand -novariable $close$cdata1]
05913             set text [subst -novariable -nocommand $text]
05914             set tag {}
05915             unset state(cdata)
05916             set state(mode) normal
05917         } elseif {[regexp ([cl ^\]]*)\]\][cl $Wsp]*\$ $param discard cdata1]} {
05918             # end of CDATA (in attributes)
05919             PCDATA [array get options] $state(cdata)<[subst -nocommand -novariable $close$tag$cdata1]
05920             set text [subst -novariable -nocommand $text]
05921             set tag {}
05922             set param {}
05923             unset state(cdata)
05924             set state(mode) normal
05925         } elseif {[regexp (.*)\]\][cl $Wsp]*>(.*) $text discard cdata1 text]} {
05926             # end of CDATA (in text)
05927             PCDATA [array get options] $state(cdata)<[subst -nocommand -novariable $close$tag$param>$cdata1]
05928             set text [subst -novariable -nocommand $text]
05929             set tag {}
05930             set param {}
05931             set close {}
05932             unset state(cdata)
05933             set state(mode) normal
05934         } else {
05935             # CDATA continues
05936             append state(cdata) [subst -nocommand -novariable <$close$tag$param>$text]
05937             continue
05938         }
05939         }
05940         continue {
05941         # We're skipping elements looking for the close tag
05942         switch -glob -- [string length $tag],[regexp {^\?|!.*} $tag],$close {
05943             0,* {
05944             continue
05945             }
05946             *,0, {
05947             if {![string compare $tag $state(continue:tag)]} {
05948                 set empty [uplevel #0 $options(-emptyelement) [list $tag $param $empty]]
05949                 if {![string length $empty]} {
05950                 incr state(continue:level)
05951                 }
05952             }
05953             continue
05954             }
05955             *,0,/ {
05956             if {![string compare $tag $state(continue:tag)]} {
05957                 incr state(continue:level) -1
05958             }
05959             if {!$state(continue:level)} {
05960                 unset state(continue:tag)
05961                 unset state(continue:level)
05962                 set state(mode) {}
05963             }
05964             }
05965             default {
05966             continue
05967             }
05968         }
05969         }
05970         default {
05971         # The trailing slash on empty elements can't be automatically separated out
05972         # in the RE, so we must do it here.
05973         regexp (.*)(/)[cl $Wsp]*$ $param discard param empty
05974         }
05975     }
05976 
05977     # default: normal mode
05978 
05979     # Bug: if the attribute list has a right angle bracket then the empty
05980     # element marker will not be seen
05981 
05982     set empty [uplevel #0 $options(-emptyelement) [list $tag $param $empty]]
05983 
05984     switch -glob -- [string length $tag],[regexp {^\?|!.*} $tag],$close,$empty {
05985 
05986         0,0,, {
05987         # Ignore empty tag - dealt with non-normal mode above
05988         }
05989         *,0,, {
05990 
05991         # Start tag for an element.
05992 
05993         # Check if the internal DTD entity is in an attribute value
05994         regsub -all &xml:intdtd\; $param \[$options(-internaldtd)\] param
05995 
05996         set code [catch {ParseEvent:ElementOpen $tag $param [array get options]} msg]
05997         set state(haveDocElement) 1
05998         switch $code {
05999             0 {# OK}
06000             3 {
06001             # break
06002             return {}
06003             }
06004             4 {
06005             # continue
06006             # Remember this tag and look for its close
06007             set state(continue:tag) $tag
06008             set state(continue:level) 1
06009             set state(mode) continue
06010             continue
06011             }
06012             default {
06013             return -code $code -errorinfo $::errorInfo $msg
06014             }
06015         }
06016 
06017         }
06018 
06019         *,0,/, {
06020 
06021         # End tag for an element.
06022 
06023         set code [catch {ParseEvent:ElementClose $tag [array get options]} msg]
06024         switch $code {
06025             0 {# OK}
06026             3 {
06027             # break
06028             return {}
06029             }
06030             4 {
06031             # continue
06032             # skip sibling nodes
06033             set state(continue:tag) [lindex $state(stack) end]
06034             set state(continue:level) 1
06035             set state(mode) continue
06036             continue
06037             }
06038             default {
06039             return -code $code -errorinfo $::errorInfo $msg
06040             }
06041         }
06042 
06043         }
06044 
06045         *,0,,/ {
06046 
06047         # Empty element
06048 
06049         # The trailing slash sneaks through into the param variable
06050         regsub -all /[cl $::sgml::Wsp]*\$ $param {} param
06051 
06052         set code [catch {ParseEvent:ElementOpen $tag $param [array get options] -empty 1} msg]
06053         set state(haveDocElement) 1
06054         switch $code {
06055             0 {# OK}
06056             3 {
06057             # break
06058             return {}
06059             }
06060             4 {
06061             # continue
06062             # Pretty useless since it closes straightaway
06063             }
06064             default {
06065             return -code $code -errorinfo $::errorInfo $msg
06066             }
06067         }
06068         set code [catch {ParseEvent:ElementClose $tag [array get options] -empty 1} msg]
06069         switch $code {
06070             0 {# OK}
06071             3 {
06072             # break
06073             return {}
06074             }
06075             4 {
06076             # continue
06077             # skip sibling nodes
06078             set state(continue:tag) [lindex $state(stack) end]
06079             set state(continue:level) 1
06080             set state(mode) continue
06081             continue
06082             }
06083             default {
06084             return -code $code -errorinfo $::errorInfo $msg
06085             }
06086         }
06087 
06088         }
06089 
06090         *,1,* {
06091         # Processing instructions or XML declaration
06092         switch -glob -- $tag {
06093 
06094             {\?xml} {
06095             # XML Declaration
06096             if {$state(haveXMLDecl)} {
06097                 uplevel #0 $options(-errorcommand) [list illegalcharacter "unexpected characters \"<$tag\" around line $state(line)"]
06098             } elseif {![regexp {\?$} $param]} {
06099                 uplevel #0 $options(-errorcommand) [list missingcharacters "XML Declaration missing characters \"?>\" around line $state(line)"]
06100             } else {
06101 
06102                 # We can do the parsing in one step with Tcl 8.1 RE's
06103                 # This has the benefit of performing better WF checking
06104 
06105                 set adv_re [format {^[%s]*version[%s]*=[%s]*("|')(-+|[a-zA-Z0-9_.:]+)\1([%s]+encoding[%s]*=[%s]*("|')([A-Za-z][-A-Za-z0-9._]*)\4)?([%s]*standalone[%s]*=[%s]*("|')(yes|no)\7)?[%s]*\?$} $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp]
06106 
06107                 if {[catch {regexp $adv_re $param discard delimiter version discard delimiter encoding discard delimiter standalone} matches]} {
06108                 # Otherwise we must fallback to 8.0.
06109                 # This won't detect certain well-formedness errors
06110 
06111                 # Get the version number
06112                 if {[regexp [format {[%s]*version[%s]*=[%s]*"(-+|[a-zA-Z0-9_.:]+)"[%s]*} $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp] $param discard version] || [regexp [format {[%s]*version[%s]*=[%s]*'(-+|[a-zA-Z0-9_.:]+)'[%s]*} $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp] $param discard version]} {
06113                     if {[string compare $version "1.0"]} {
06114                     # Should we support future versions?
06115                     # At least 1.X?
06116                     uplevel #0 $options(-errorcommand) [list versionincompatibility "document XML version \"$version\" is incompatible with XML version 1.0"]
06117                     }
06118                 } else {
06119                     uplevel #0 $options(-errorcommand) [list missingversion "XML Declaration missing version information around line $state(line)"]
06120                 }
06121 
06122                 # Get the encoding declaration
06123                 set encoding {}
06124                 regexp [format {[%s]*encoding[%s]*=[%s]*"([A-Za-z]([A-Za-z0-9._]|-)*)"[%s]*} $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp] $param discard encoding
06125                 regexp [format {[%s]*encoding[%s]*=[%s]*'([A-Za-z]([A-Za-z0-9._]|-)*)'[%s]*} $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp] $param discard encoding
06126 
06127                 # Get the standalone declaration
06128                 set standalone {}
06129                 regexp [format {[%s]*standalone[%s]*=[%s]*"(yes|no)"[%s]*} $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp] $param discard standalone
06130                 regexp [format {[%s]*standalone[%s]*=[%s]*'(yes|no)'[%s]*} $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp $::sgml::Wsp] $param discard standalone
06131 
06132                 # Invoke the callback
06133                 uplevel #0 $options(-xmldeclcommand) [list $version $encoding $standalone]
06134 
06135                 } elseif {$matches == 0} {
06136                 uplevel #0 $options(-errorcommand) [list illformeddeclaration "XML Declaration not well-formed around line $state(line)"]
06137                 } else {
06138 
06139                 # Invoke the callback
06140                 uplevel #0 $options(-xmldeclcommand) [list $version $encoding $standalone]
06141 
06142                 }
06143 
06144             }
06145 
06146             }
06147 
06148             {\?*} {
06149             # Processing instruction
06150             set tag [string range $tag 1 end]
06151             if {[regsub {\?$} $tag {} tag]} {
06152                 if {[string length [string trim $param]]} {
06153                 uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$param\" in processing instruction around line $state(line)"]
06154                 }
06155             } elseif {![regexp ^$Name\$ $tag]} {
06156                 uplevel #0 $options(-errorcommand) [list illegalcharacter "illegal character in processing instruction target \"$tag\""]
06157             } elseif {[regexp {[xX][mM][lL]} $tag]} {
06158                 uplevel #0 $options(-errorcommand) [list illegalcharacters "characters \"xml\" not permitted in processing instruction target \"$tag\""]
06159             } elseif {![regsub {\?$} $param {} param]} {
06160                 uplevel #0 $options(-errorcommand) [list missingquestion "PI: expected '?' character around line $state(line)"]
06161             }
06162             set code [catch {uplevel #0 $options(-processinginstructioncommand) [list $tag [string trimleft $param]]} msg]
06163             switch $code {
06164                 0 {# OK}
06165                 3 {
06166                 # break
06167                 return {}
06168                 }
06169                 4 {
06170                 # continue
06171                 # skip sibling nodes
06172                 set state(continue:tag) [lindex $state(stack) end]
06173                 set state(continue:level) 1
06174                 set state(mode) continue
06175                 continue
06176                 }
06177                 default {
06178                 return -code $code -errorinfo $::errorInfo $msg
06179                 }
06180             }
06181             }
06182 
06183             !DOCTYPE {
06184             # External entity reference
06185             # This should move into xml.tcl
06186             # Parse the params supplied.  Looking for Name, ExternalID and MarkupDecl
06187             set matched [regexp ^[cl $Wsp]*($Name)[cl $Wsp]*(.*) $param x state(doc_name) param]
06188             set state(doc_name) [Normalize $state(doc_name) $options(-normalize)]
06189             set externalID {}
06190             set pubidlit {}
06191             set systemlit {}
06192             set externalID {}
06193             if {[regexp -nocase ^[cl $Wsp]*(SYSTEM|PUBLIC)(.*) $param x id param]} {
06194                 switch [string toupper $id] {
06195                 SYSTEM {
06196                     if {[regexp ^[cl $Wsp]+"([cl ^"]*)"(.*) $param x systemlit param] || [regexp ^[cl $Wsp]+'([cl ^']*)'(.*) $param x systemlit param]} {
06197                     set externalID [list SYSTEM $systemlit] ;# "
06198                     } else {
06199                     uplevel #0 $options(-errorcommand) {syntaxerror {syntax error: SYSTEM identifier not followed by literal}}
06200                     }
06201                 }
06202                 PUBLIC {
06203                     if {[regexp ^[cl $Wsp]+"([cl ^"]*)"(.*) $param x pubidlit param] || [regexp ^[cl $Wsp]+'([cl ^']*)'(.*) $param x pubidlit param]} {
06204                     if {[regexp ^[cl $Wsp]+"([cl ^"]*)"(.*) $param x systemlit param] || [regexp ^[cl $Wsp]+'([cl ^']*)'(.*) $param x systemlit param]} {
06205                         set externalID [list PUBLIC $pubidlit $systemlit]
06206                     } else {
06207                         uplevel #0 $options(-errorcommand) [list syntaxerror "syntax error: PUBLIC identifier not followed by system literal around line $state(line)"]
06208                     }
06209                     } else {
06210                     uplevel #0 $options(-errorcommand) [list syntaxerror "syntax error: PUBLIC identifier not followed by literal around line $state(line)"]
06211                     }
06212                 }
06213                 }
06214                 if {[regexp -nocase ^[cl $Wsp]+NDATA[cl $Wsp]+($Name)(.*) $param x notation param]} {
06215                 lappend externalID $notation
06216                 }
06217             }
06218 
06219             set state(inDTD) 1
06220 
06221             ParseEvent:DocTypeDecl [array get options] $state(doc_name) $pubidlit $systemlit $options(-internaldtd)
06222 
06223             set state(inDTD) 0
06224 
06225             }
06226 
06227             !--* {
06228 
06229             # Start of a comment
06230             # See if it ends in the same tag, otherwise change the
06231             # parsing mode
06232 
06233             regexp {!--(.*)} $tag discard comm1
06234             if {[regexp ([cl ^-]*)--[cl $Wsp]*\$ $comm1 discard comm1_1]} {
06235                 # processed comment (end in tag)
06236                 uplevel #0 $options(-commentcommand) [list $comm1_1]
06237             } elseif {[regexp ([cl ^-]*)--[cl $Wsp]*\$ $param discard comm2]} {
06238                 # processed comment (end in attributes)
06239                 uplevel #0 $options(-commentcommand) [list $comm1$comm2]
06240             } elseif {[regexp ([cl ^-]*)-->(.*) $text discard comm2 text]} {
06241                 # processed comment (end in text)
06242                 uplevel #0 $options(-commentcommand) [list $comm1$param$empty>$comm2]
06243             } else {
06244                 # start of comment
06245                 set state(mode) comment
06246                 set state(commentdata) "$comm1$param$empty>$text"
06247                 continue
06248             }
06249             }
06250 
06251             {!\[CDATA\[*} {
06252 
06253             regexp {!\[CDATA\[(.*)} $tag discard cdata1
06254             if {[regexp {(.*)]]$} $cdata1 discard cdata2]} {
06255                 # processed CDATA (end in tag)
06256                 PCDATA [array get options] [subst -novariable -nocommand $cdata2]
06257                 set text [subst -novariable -nocommand $text]
06258             } elseif {[regexp {(.*)]]$} $param discard cdata2]} {
06259                 # processed CDATA (end in attribute)
06260                 # Backslashes in param are quoted at this stage
06261                 PCDATA [array get options] $cdata1[subst -novariable -nocommand $cdata2]
06262                 set text [subst -novariable -nocommand $text]
06263             } elseif {[regexp {(.*)]]>(.*)} $text discard cdata2 text]} {
06264                 # processed CDATA (end in text)
06265                 # Backslashes in param and text are quoted at this stage
06266                 PCDATA [array get options] $cdata1[subst -novariable -nocommand $param]$empty>[subst -novariable -nocommand $cdata2]
06267                 set text [subst -novariable -nocommand $text]
06268             } else {
06269                 # start CDATA
06270                 set state(cdata) "$cdata1$param>$text"
06271                 set state(mode) cdata
06272                 continue
06273             }
06274 
06275             }
06276 
06277             !ELEMENT -
06278             !ATTLIST -
06279             !ENTITY -
06280             !NOTATION {
06281             uplevel #0 $options(-errorcommand) [list illegaldeclaration "[string range $tag 1 end] declaration not expected in document instance around line $state(line)"]
06282             }
06283 
06284             default {
06285             uplevel #0 $options(-errorcommand) [list unknowninstruction "unknown processing instruction \"<$tag>\" around line $state(line)"]
06286             }
06287         }
06288         }
06289         *,1,* -
06290         *,0,/,/ {
06291         # Syntax error
06292             uplevel #0 $options(-errorcommand) [list syntaxerror "syntax error: closed/empty tag: tag $tag param $param empty $empty close $close around line $state(line)"]
06293         }
06294     }
06295 
06296     # Process character data
06297 
06298     if {$state(haveDocElement) && [llength $state(stack)]} {
06299 
06300         # Check if the internal DTD entity is in the text
06301         regsub -all &xml:intdtd\; $text \[$options(-internaldtd)\] text
06302 
06303         # Look for entity references
06304         if {([array size entities] ||  [string length $options(-entityreferencecommand)]) &&  $options(-defaultexpandinternalentities) &&  [regexp {&[^;]+;} $text]} {
06305 
06306         # protect Tcl specials
06307         # NB. braces and backslashes may already be protected
06308         regsub -all {\\({|}|\\)} $text {\1} text
06309         regsub -all {([][$\\{}])} $text {\\\1} text
06310 
06311         # Mark entity references
06312         regsub -all {&([^;]+);} $text [format {%s; %s {\1} ; %s %s} \}\} [namespace code [list Entity [array get options] $options(-entityreferencecommand) [namespace code [list PCDATA [array get options]]] $options(entities)]] [namespace code [list DeProtect [namespace code [list PCDATA [array get options]]]]] \{\{] text
06313         set text "uplevel #0 [namespace code [list DeProtect1 [namespace code [list PCDATA [array get options]]]]] {{$text}}"
06314         eval $text
06315         } else {
06316 
06317         # Restore protected special characters
06318         regsub -all {\\([][{}\\])} $text {\1} text
06319         PCDATA [array get options] $text
06320         }
06321     } elseif {[string length [string trim $text]]} {
06322         uplevel #0 $options(-errorcommand) [list unexpectedtext "unexpected text \"$text\" in document prolog around line $state(line)"]
06323     }
06324 
06325     }
06326 
06327     # If this is the end of the document, close all open containers
06328     if {$options(-final) && [llength $state(stack)]} {
06329     eval $options(-errorcommand) [list unclosedelement "element [lindex $state(stack) end] remains unclosed around line $state(line)"]
06330     }
06331 
06332     return {}
06333 }
06334 
06335 }
06336 
06337 namespace ::sgml {
06338 
06339 ret  {::sgml::tokenise} (type sgml , type elemExpr , type elemSub , type args) {
06340 array set options {-final 1}
06341     array set options $args
06342     set options(-final) [Boolean $options(-final)]
06343 
06344     # If the data is not final then there must be a variable to store
06345     # unused data.
06346     if {!$options(-final) && ![info exists options(-statevariable)]} {
06347     return -code error {option "-statevariable" required if not final}
06348     }
06349 
06350     # Pre-process stage
06351     #
06352     # Extract the internal DTD subset, if any
06353 
06354     catch {upvar #0 $options(-internaldtdvariable) dtd}
06355     if {[regexp {<!DOCTYPE[^[<]+\[([^]]+)\]} $sgml discard dtd]} {
06356     regsub {(<!DOCTYPE[^[<]+)(\[[^]]+\])} $sgml {\1\&xml:intdtd;} sgml
06357     }
06358 
06359     # Protect Tcl special characters
06360     regsub -all {([{}\\])} $sgml {\\\1} sgml
06361 
06362     # Do the translation
06363 
06364     if {[info exists options(-statevariable)]} {
06365     # Mats: Several rewrites here to handle -final 0 option.
06366     # If any cached unparsed xml (state(leftover)), prepend it.
06367     upvar #0 $options(-statevariable) state
06368     if {[string length $state(leftover)]} {
06369         regsub -all $elemExpr $state(leftover)$sgml $elemSub sgml
06370         set state(leftover) {}
06371     } else {
06372         regsub -all $elemExpr $sgml $elemSub sgml
06373     }
06374     set sgml "{} {} {} \{$sgml\}"
06375 
06376     # Performance note (Tcl 8.0):
06377     #   Use of lindex, lreplace will cause parsing to list object
06378 
06379     # This RE only fixes chopped inside tags, not chopped text.
06380     if {[regexp {^([^<]*)(<[^>]*$)} [lindex $sgml end] x text rest]} {
06381         set sgml [lreplace $sgml end end $text]
06382         # Mats: unmatched stuff means that it is chopped off. Cache it for next round.
06383         set state(leftover) $rest
06384     }
06385 
06386     # Patch from bug report #596959, Marshall Rose
06387     if {[string compare [lindex $sgml 4] ""]} {
06388         set sgml [linsert $sgml 0 {} {} {} {} {}]
06389     }
06390 
06391     } else {
06392 
06393     # Performance note (Tcl 8.0):
06394     #   In this case, no conversion to list object is performed
06395 
06396     # Mats: This fails if not -final and $sgml is chopped off right in a tag.   
06397     regsub -all $elemExpr $sgml $elemSub sgml
06398     set sgml "{} {} {} \{$sgml\}"
06399     }
06400 
06401     return $sgml
06402 }
06403 
06404 }
06405 
06406 namespace ::sgml {
06407 
06408 ret  {::sgml::zapWhite} (type data) {
06409 regsub -all "\[ \t\r\n\]+" $data { } data
06410     return $data
06411 }
06412 
06413 }
06414 
06415 namespace ::uri {
06416 
06417 ret  {::uri::GetHostPort} (type urlvar) {
06418 # @c Parse host and port out of the url stored in variable <a urlvar>.
06419     # @d Side effect: The extracted information is removed from the given url.
06420     # @r List containing the extracted information in a format suitable for
06421     # @r 'array set'.
06422     # @a urlvar: Name of the variable containing the url to parse.
06423 
06424     upvar #0 [namespace current]::basic::hostname   hostname
06425     upvar #0 [namespace current]::basic::hostnumber hostnumber
06426     upvar #0 [namespace current]::basic::port       port
06427 
06428     upvar $urlvar url
06429 
06430     set pattern "^(${hostname}|${hostnumber})(:(${port}))?"
06431 
06432     if {[regexp -indices -- $pattern $url match host c d e f g h thePort]} {
06433     set fromHost    [lindex $host 0]
06434     set toHost  [lindex $host 1]
06435 
06436     set fromPort    [lindex $thePort 0]
06437     set toPort  [lindex $thePort 1]
06438 
06439     set parts(host) [string range $url $fromHost $toHost]
06440     set parts(port) [string range $url $fromPort $toPort]
06441 
06442     set  matchEnd   [lindex $match 1]
06443     incr matchEnd
06444 
06445     set url [string range $url $matchEnd end]
06446     }
06447 
06448     return [array get parts]
06449 }
06450 
06451 }
06452 
06453 namespace ::uri {
06454 
06455 ret  {::uri::GetUPHP} (type urlvar) {
06456 # @c Parse user, password host and port out of the url stored in
06457     # @c variable <a urlvar>.
06458     # @d Side effect: The extracted information is removed from the given url.
06459     # @r List containing the extracted information in a format suitable for
06460     # @r 'array set'.
06461     # @a urlvar: Name of the variable containing the url to parse.
06462 
06463     upvar \#0 [namespace current]::basic::user      user
06464     upvar \#0 [namespace current]::basic::password  password
06465     upvar \#0 [namespace current]::basic::hostname  hostname
06466     upvar \#0 [namespace current]::basic::hostnumber    hostnumber
06467     upvar \#0 [namespace current]::basic::port      port
06468 
06469     upvar $urlvar url
06470 
06471     array set parts {user {} pwd {} host {} port {}}
06472 
06473     # syntax
06474     # "//" [ <user> [":" <password> ] "@"] <host> [":" <port>] "/"
06475     # "//" already cut off by caller
06476 
06477     set upPattern "^(${user})(:(${password}))?@"
06478 
06479     if {[regexp -indices -- $upPattern $url match theUser c d thePassword]} {
06480     set fu  [lindex $theUser 0]
06481     set tu  [lindex $theUser 1]
06482 
06483     set fp  [lindex $thePassword 0]
06484     set tp  [lindex $thePassword 1]
06485 
06486     set parts(user) [string range $url $fu $tu]
06487     set parts(pwd)  [string range $url $fp $tp]
06488 
06489     set  matchEnd   [lindex $match 1]
06490     incr matchEnd
06491 
06492     set url [string range $url $matchEnd end]
06493     }
06494 
06495     set hpPattern "^($hostname|$hostnumber)(:($port))?"
06496 
06497     if {[regexp -indices -- $hpPattern $url match theHost c d e f g h thePort]} {
06498     set fh  [lindex $theHost 0]
06499     set th  [lindex $theHost 1]
06500 
06501     set fp  [lindex $thePort 0]
06502     set tp  [lindex $thePort 1]
06503 
06504     set parts(host) [string range $url $fh $th]
06505     set parts(port) [string range $url $fp $tp]
06506 
06507     set  matchEnd   [lindex $match 1]
06508     incr matchEnd
06509 
06510     set url [string range $url $matchEnd end]
06511     }
06512 
06513     return [array get parts]
06514 }
06515 
06516 }
06517 
06518 namespace ::uri {
06519 
06520 ret  {::uri::JoinFile} (type args) {
06521 array set components {
06522     host {} port {} path {}
06523     }
06524     array set components $args
06525 
06526     switch -exact -- $::tcl_platform(platform) {
06527     windows {
06528         if {[string length $components(host)]} {
06529         return file://$components(host):$components(path)
06530         } else {
06531         return file://$components(path)
06532         }
06533     }
06534     default {
06535         return file://$components(host)$components(path)
06536     }
06537     }
06538 }
06539 
06540 }
06541 
06542 namespace ::uri {
06543 
06544 ret  {::uri::JoinFtp} (type args) {
06545 array set components {
06546     user {} pwd {} host {} port {}
06547     path {} type {}
06548     }
06549     array set components $args
06550 
06551     set userPwd {}
06552     if {[string length $components(user)] || [string length $components(pwd)]} {
06553     set userPwd $components(user)[expr {[string length $components(pwd)] ? ":$components(pwd)" : {}}]@
06554     }
06555 
06556     set port {}
06557     if {[string length $components(port)]} {
06558     set port :$components(port)
06559     }
06560 
06561     set type {}
06562     if {[string length $components(type)]} {
06563     set type \;type=$components(type)
06564     }
06565 
06566     return ftp://${userPwd}$components(host)${port}/[string trimleft $components(path) /]$type
06567 }
06568 
06569 }
06570 
06571 namespace ::uri {
06572 
06573 ret  {::uri::JoinHttp} (type args) {
06574 return [eval [linsert $args 0 ::uri::JoinHttpInner http 80]]
06575 }
06576 
06577 }
06578 
06579 namespace ::uri {
06580 
06581 ret  {::uri::JoinHttpInner} (type scheme , type defport , type args) {
06582 array set components {host {} path {} query {}}
06583     set       components(port) $defport
06584     array set components $args
06585 
06586     set port {}
06587     if {[string length $components(port)] && $components(port) != $defport} {
06588     set port :$components(port)
06589     }
06590 
06591     set query {}
06592     if {[string length $components(query)]} {
06593     set query ?$components(query)
06594     }
06595 
06596     regsub -- {^/} $components(path) {} components(path)
06597 
06598     if { [info exists components(fragment)] && $components(fragment) != "" } {
06599     set components(fragment) "#$components(fragment)"
06600     } else {
06601     set components(fragment) ""
06602     }
06603 
06604     return $scheme://$components(host)$port/$components(path)$components(fragment)$query
06605 }
06606 
06607 }
06608 
06609 namespace ::uri {
06610 
06611 ret  {::uri::JoinHttps} (type args) {
06612 return [eval [linsert $args 0 ::uri::JoinHttpInner https 443]]
06613 }
06614 
06615 }
06616 
06617 namespace ::uri {
06618 
06619 ret  {::uri::JoinLdap} (type args) {
06620 return [eval [linsert $args 0 ::uri::JoinLdapInner ldap 389]]
06621 }
06622 
06623 }
06624 
06625 namespace ::uri {
06626 
06627 ret  {::uri::JoinLdapInner} (type scheme , type defport , type args) {
06628 array set components {host {} port {} dn {} attrs {} scope {} filter {} extensions {}}
06629     set       components(port) $defport
06630     array set components $args
06631 
06632     set port {}
06633     if {[string length $components(port)] && $components(port) != $defport} {
06634     set port :$components(port)
06635     }
06636 
06637     set url "$scheme://$components(host)$port"
06638 
06639     set components(attrs) [::join $components(attrs) ","]
06640 
06641     set s ""
06642     foreach c {dn attrs scope filter extensions} {
06643     if {[string equal $c "dn"]} then {
06644         append s "/"
06645     } else {
06646         append s "?"
06647     }
06648     if {! [string equal $components($c) ""]} then {
06649         append url "${s}$components($c)"
06650         set s ""
06651     }
06652     }
06653 
06654     return $url
06655 }
06656 
06657 }
06658 
06659 namespace ::uri {
06660 
06661 ret  {::uri::JoinLdaps} (type args) {
06662 return [eval [linsert $args 0 ::uri::JoinLdapInner ldaps 636]]
06663 }
06664 
06665 }
06666 
06667 namespace ::uri {
06668 
06669 ret  {::uri::JoinMailto} (type args) {
06670 array set components {
06671     user {} host {}
06672     }
06673     array set components $args
06674 
06675     return mailto:$components(user)@$components(host)
06676 }
06677 
06678 }
06679 
06680 namespace ::uri {
06681 
06682 ret  {::uri::JoinNews} (type args) {
06683 array set components {
06684     message-id {} newsgroup-name {}
06685     }
06686     array set components $args
06687     return news:$components(message-id)$components(newsgroup-name)
06688 }
06689 
06690 }
06691 
06692 namespace ::uri {
06693 
06694 ret  {::uri::SplitFile} (type url) {
06695 # @c Splits the given file-<a url> into its constituents.
06696     # @a url: The url to split, without! scheme specification.
06697     # @r List containing the constituents, suitable for 'array set'.
06698 
06699     upvar #0 [namespace current]::basic::hostname   hostname
06700     upvar #0 [namespace current]::basic::hostnumber hostnumber
06701 
06702     if {[string match "//*" $url]} {
06703     set url [string range $url 2 end]
06704 
06705     set hostPattern "^($hostname|$hostnumber)"
06706     switch -exact -- $::tcl_platform(platform) {
06707         windows {
06708         # Catch drive letter
06709         append hostPattern :?
06710         }
06711         default {
06712         # Proceed as usual
06713         }
06714     }
06715 
06716     if {[regexp -indices -- $hostPattern $url match host]} {
06717         set fh  [lindex $host 0]
06718         set th  [lindex $host 1]
06719 
06720         set parts(host) [string range $url $fh $th]
06721 
06722         set  matchEnd   [lindex $match 1]
06723         incr matchEnd
06724 
06725         set url [string range $url $matchEnd end]
06726     }
06727     }
06728 
06729     set parts(path) $url
06730 
06731     return [array get parts]
06732 }
06733 
06734 }
06735 
06736 namespace ::uri {
06737 
06738 ret  {::uri::SplitFtp} (type url) {
06739 # @c Splits the given ftp-<a url> into its constituents.
06740     # @a url: The url to split, without! scheme specification.
06741     # @r List containing the constituents, suitable for 'array set'.
06742 
06743     # general syntax:
06744     # //<user>:<password>@<host>:<port>/<cwd1>/.../<cwdN>/<name>;type=<typecode>
06745     #
06746     # additional rules:
06747     #
06748     # <user>:<password> are optional, detectable by presence of @.
06749     # <password> is optional too.
06750     #
06751     # "//" [ <user> [":" <password> ] "@"] <host> [":" <port>] "/"
06752     #   <cwd1> "/" ..."/" <cwdN> "/" <name> [";type=" <typecode>]
06753 
06754     upvar \#0 [namespace current]::ftp::typepart ftptype
06755 
06756     array set parts {user {} pwd {} host {} port {} path {} type {}}
06757 
06758     # slash off possible type specification
06759 
06760     if {[regexp -indices -- "${ftptype}$" $url dummy ftype]} {
06761 
06762     set from    [lindex $ftype 0]
06763     set to      [lindex $ftype 1]
06764 
06765     set parts(type) [string range   $url $from $to]
06766 
06767     set from    [lindex $dummy 0]
06768     set url     [string replace $url $from end]
06769     }
06770 
06771     # Handle user, password, host and port
06772 
06773     if {[string match "//*" $url]} {
06774     set url [string range $url 2 end]
06775 
06776     array set parts [GetUPHP url]
06777     }
06778 
06779     set parts(path) [string trimleft $url /]
06780 
06781     return [array get parts]
06782 }
06783 
06784 }
06785 
06786 namespace ::uri {
06787 
06788 ret  {::uri::SplitHttp} (type url) {
06789 # @c Splits the given http-<a url> into its constituents.
06790     # @a url: The url to split, without! scheme specification.
06791     # @r List containing the constituents, suitable for 'array set'.
06792 
06793     # general syntax:
06794     # //<host>:<port>/<path>?<searchpart>
06795     #
06796     #   where <host> and <port> are as described in Section 3.1. If :<port>
06797     #   is omitted, the port defaults to 80.  No user name or password is
06798     #   allowed.  <path> is an HTTP selector, and <searchpart> is a query
06799     #   string. The <path> is optional, as is the <searchpart> and its
06800     #   preceding "?". If neither <path> nor <searchpart> is present, the "/"
06801     #   may also be omitted.
06802     #
06803     #   Within the <path> and <searchpart> components, "/", ";", "?" are
06804     #   reserved.  The "/" character may be used within HTTP to designate a
06805     #   hierarchical structure.
06806     #
06807     # path == <cwd1> "/" ..."/" <cwdN> "/" <name> ["#" <fragment>]
06808 
06809     upvar #0 [namespace current]::http::search  search
06810     upvar #0 [namespace current]::http::segment segment
06811 
06812     array set parts {host {} port {} path {} query {}}
06813 
06814     set searchPattern   "\\?(${search})\$"
06815     set fragmentPattern "#(${segment})\$"
06816 
06817     # slash off possible query. the 'search' regexp, while official,
06818     # is not good enough. We have apparently lots of urls in the wild
06819     # which contain unquoted urls with queries in a query. The RE
06820     # finds the embedded query, not the actual one. Using string first
06821     # now instead of a RE
06822 
06823     if {[set pos [string first ? $url]] >= 0} {
06824     incr pos
06825     set parts(query) [string range   $url $pos end]
06826     incr pos -1
06827     set url          [string replace $url $pos end]
06828     }
06829 
06830     # slash off possible fragment
06831 
06832     if {[regexp -indices -- $fragmentPattern $url match fragment]} {
06833     set from [lindex $fragment 0]
06834     set to   [lindex $fragment 1]
06835 
06836     set parts(fragment) [string range $url $from $to]
06837 
06838     set url [string replace $url [lindex $match 0] end]
06839     }
06840 
06841     if {[string match "//*" $url]} {
06842     set url [string range $url 2 end]
06843 
06844     array set parts [GetUPHP url]
06845     }
06846 
06847     set parts(path) [string trimleft $url /]
06848 
06849     return [array get parts]
06850 }
06851 
06852 }
06853 
06854 namespace ::uri {
06855 
06856 ret  {::uri::SplitHttps} (type url) {
06857 return [SplitHttp $url]
06858 }
06859 
06860 }
06861 
06862 namespace ::uri {
06863 
06864 ret  {::uri::SplitLdap} (type url) {
06865 # @c Splits the given Ldap-<a url> into its constituents.
06866     # @a url: The url to split, without! scheme specification.
06867     # @r List containing the constituents, suitable for 'array set'.
06868 
06869     # general syntax:
06870     # //<host>:<port>/<dn>?<attrs>?<scope>?<filter>?<extensions>
06871     #
06872     #   where <host> and <port> are as described in Section 5 of RFC 1738.
06873     #   No user name or password is allowed.
06874     #   If omitted, the port defaults to 389 for ldap, 636 for ldaps
06875     #   <dn> is the base DN for the search
06876     #   <attrs> is a comma separated list of attributes description
06877     #   <scope> is either "base", "one" or "sub".
06878     #   <filter> is a RFC 2254 filter specification
06879     #   <extensions> are documented in RFC 2255
06880     #
06881 
06882     array set parts {host {} port {} dn {} attrs {} scope {} filter {} extensions {}}
06883 
06884     #          host        port           dn          attrs       scope               filter     extns
06885     set re {//([^:?/]+)(?::([0-9]+))?(?:/([^?]+)(?:\?([^?]*)(?:\?(base|one|sub)?(?:\?([^?]*)(?:\?(.*))?)?)?)?)?}
06886 
06887     if {! [regexp $re $url match parts(host) parts(port)  parts(dn) parts(attrs) parts(scope) parts(filter)  parts(extensions)]} then {
06888     return -code error "unable to match URL \"$url\""
06889     }
06890 
06891     set parts(attrs) [::split $parts(attrs) ","]
06892 
06893     return [array get parts]
06894 }
06895 
06896 }
06897 
06898 namespace ::uri {
06899 
06900 ret  {::uri::SplitLdaps} (type url) {
06901 ::uri::SplitLdap $url
06902 }
06903 
06904 }
06905 
06906 namespace ::uri {
06907 
06908 ret  {::uri::SplitMailto} (type url) {
06909 # @c Splits the given mailto-<a url> into its constituents.
06910     # @a url: The url to split, without! scheme specification.
06911     # @r List containing the constituents, suitable for 'array set'.
06912 
06913     if {[string match "*@*" $url]} {
06914     set url [::split $url @]
06915     return [list user [lindex $url 0] host [lindex $url 1]]
06916     } else {
06917     return [list user $url]
06918     }
06919 }
06920 
06921 }
06922 
06923 namespace ::uri {
06924 
06925 ret  {::uri::SplitNews} (type url) {
06926 if { [string first @ $url] >= 0 } {
06927     return [list message-id $url]
06928     } else {
06929     return [list newsgroup-name $url]
06930     }
06931 }
06932 
06933 }
06934 
06935 namespace ::uri {
06936 
06937 ret  {::uri::canonicalize} (type uri) {
06938 # Make uri canonical with respect to dots (path changing commands)
06939     #
06940     # Remove single dots (.)  => pwd not changing
06941     # Remove double dots (..) => gobble previous segment of path
06942     #
06943     # Fixes for this command:
06944     #
06945     # * Ignore any url which cannot be split into components by this
06946     #   module. Just assume that such urls do not have a path to
06947     #   canonicalize.
06948     #
06949     # * Ignore any url which could be split into components, but does
06950     #   not have a path component.
06951     #
06952     # In the text above 'ignore' means
06953     # 'return the url unchanged to the caller'.
06954 
06955     if {[catch {array set u [::uri::split $uri]}]} {
06956     return $uri
06957     }
06958     if {![info exists u(path)]} {
06959     return $uri
06960     }
06961 
06962     set uri $u(path)
06963 
06964     # Remove leading "./" "../" "/.." (and "/../")
06965     regsub -all -- {^(\./)+}    $uri {}  uri
06966     regsub -all -- {^/(\.\./)+} $uri {/} uri
06967     regsub -all -- {^(\.\./)+}  $uri {}  uri
06968 
06969     # Remove inner /./ and /../
06970     while {[regsub -all -- {/\./}         $uri {/} uri]} {}
06971     while {[regsub -all -- {/[^/]+/\.\./} $uri {/} uri]} {}
06972     while {[regsub -all -- {^[^/]+/\.\./} $uri {}  uri]} {}
06973     # Munge trailing /..
06974     while {[regsub -all -- {/[^/]+/\.\.} $uri {/} uri]} {}
06975     if { $uri == ".." } { set uri "/" }
06976 
06977     set u(path) $uri
06978     set uri [eval [linsert [array get u] 0 ::uri::join]]
06979 
06980     return $uri
06981 }
06982 
06983 }
06984 
06985 namespace ::uri {
06986 
06987 ret  {::uri::file_geturl} (type url , type args) {
06988 variable file:counter
06989 
06990     set var [namespace current]::file[incr file:counter]
06991     upvar #0 $var state
06992     array set state {data {}}
06993 
06994     array set parts [split $url]
06995 
06996     set ch [open $parts(path)]
06997     # Could determine text/binary from file extension,
06998     # except on Macintosh
06999     # fconfigure $ch -translation binary
07000     set state(data) [read $ch]
07001     close $ch
07002 
07003     return $var
07004 }
07005 
07006 }
07007 
07008 namespace ::uri {
07009 
07010 ret  {::uri::geturl} (type url , type args) {
07011 array set urlparts [split $url]
07012 
07013     switch -- $urlparts(scheme) {
07014     file {
07015         return [eval [linsert $args 0 file_geturl $url]]
07016     }
07017     default {
07018         # Load a geturl package for the scheme first and only if
07019         # that fails the scheme package itself. This prevents
07020         # cyclic dependencies between packages.
07021         if {[catch {package require $urlparts(scheme)::geturl}]} {
07022         package require $urlparts(scheme)
07023         }
07024         return [eval [linsert $args 0 $urlparts(scheme)::geturl $url]]
07025     }
07026     }
07027 }
07028 
07029 }
07030 
07031 namespace ::uri {
07032 
07033 ret  {::uri::isrelative} (type url) {
07034 return [expr {![regexp -- {^[a-z0-9+-.][a-z0-9+-.]*:} $url]}]
07035 }
07036 
07037 }
07038 
07039 namespace ::uri {
07040 
07041 ret  {::uri::join} (type args) {
07042 array set components $args
07043 
07044     return [eval [linsert $args 0 Join[string totitle $components(scheme)]]]
07045 }
07046 
07047 }
07048 
07049 namespace ::uri {
07050 
07051 ret  {::uri::register} (type schemeList , type script) {
07052 variable schemes
07053     variable schemePattern
07054     variable url
07055     variable url2part
07056 
07057     # Check scheme and its aliases for existence.
07058     foreach scheme $schemeList {
07059     if {[lsearch -exact $schemes $scheme] >= 0} {
07060         return -code error  "trying to register scheme (\"$scheme\") which is already known"
07061     }
07062     }
07063 
07064     # Get the main scheme
07065     set scheme  [lindex $schemeList 0]
07066 
07067     if {[catch {namespace eval $scheme $script} msg]} {
07068     catch {namespace delete $scheme}
07069     return -code error  "error while evaluating scheme script: $msg"
07070     }
07071 
07072     if {![info exists ${scheme}::schemepart]} {
07073     namespace delete $scheme
07074     return -code error  "Variable \"schemepart\" is missing."
07075     }
07076 
07077     # Now we can extend the variables which keep track of the registered schemes.
07078 
07079     eval [linsert $schemeList 0 lappend schemes]
07080     set schemePattern   "([::join $schemes |]):"
07081 
07082     foreach s $schemeList {
07083     # FRINK: nocheck
07084     set url2part($s) "${s}:[set ${scheme}::schemepart]"
07085     # FRINK: nocheck
07086     append url "(${s}:[set ${scheme}::schemepart])|"
07087     }
07088     set url [string trimright $url |]
07089     return
07090 }
07091 
07092 }
07093 
07094 namespace ::uri {
07095 
07096 ret  {::uri::resolve} (type base , type url) {
07097 if {[string length $url]} {
07098     if {[isrelative $url]} {
07099 
07100         array set baseparts [split $base]
07101 
07102         switch -- $baseparts(scheme) {
07103         http -
07104         https -
07105         ftp -
07106         file {
07107             array set relparts [split $url]
07108             if { [string match /* $url] } {
07109             catch { set baseparts(path) $relparts(path) }
07110             } elseif { [string match */ $baseparts(path)] } {
07111             set baseparts(path) "$baseparts(path)$relparts(path)"
07112             } else {
07113             if { [string length $relparts(path)] > 0 } {
07114                 set path [lreplace [::split $baseparts(path) /] end end]
07115                 set baseparts(path) "[::join $path /]/$relparts(path)"
07116             }
07117             }
07118             catch { set baseparts(query) $relparts(query) }
07119             catch { set baseparts(fragment) $relparts(fragment) }
07120             return [eval [linsert [array get baseparts] 0 join]]
07121         }
07122         default {
07123             return -code error "unable to resolve relative URL \"$url\""
07124         }
07125         }
07126 
07127     } else {
07128         return $url
07129     }
07130     } else {
07131     return $base
07132     }
07133 }
07134 
07135 }
07136 
07137 namespace ::uri {
07138 
07139 ret  {::uri::split} (type url , optional defaultscheme =http) {
07140 set url [string trim $url]
07141     set scheme {}
07142 
07143     # RFC 1738: scheme = 1*[ lowalpha | digit | "+" | "-" | "." ]
07144     regexp -- {^([a-z0-9+.-][a-z0-9+.-]*):} $url dummy scheme
07145 
07146     if {$scheme == {}} {
07147     set scheme $defaultscheme
07148     }
07149 
07150     # ease maintenance: dynamic dispatch, able to handle all schemes
07151     # added in future!
07152 
07153     if {[::info procs Split[string totitle $scheme]] == {}} {
07154     error "unknown scheme '$scheme' in '$url'"
07155     }
07156 
07157     regsub -- "^${scheme}:" $url {} url
07158 
07159     set       parts(scheme) $scheme
07160     array set parts [Split[string totitle $scheme] $url]
07161 
07162     # should decode all encoded characters!
07163 
07164     return [array get parts]
07165 }
07166 
07167 }
07168 
07169 namespace ::xml {
07170 
07171 ret  {::xml::cl} (type x) {
07172 return "\[$x\]"
07173 }
07174 
07175 }
07176 
07177 namespace ::xml {
07178 
07179 ret  {::xml::qnamesplit} (type qname) {
07180 variable NCName
07181     variable Name
07182 
07183     set prefix {}
07184     set localname $qname
07185     if {[regexp : $qname]} {
07186     if {![regexp ^($NCName)?:($NCName)\$ $qname discard prefix localname]} {
07187         return -code error "name \"$qname\" is not a valid QName"
07188     }
07189     } elseif {![regexp ^$Name\$ $qname]} {
07190     return -code error "name \"$qname\" is not a valid Name"
07191     }
07192 
07193     return [list $prefix $localname]
07194 }
07195 
07196 }
07197 
07198 namespace ::xml::tclparser {
07199 
07200 ret  {::xml::tclparser::NormalizeAttValue} (type opts , type value) {
07201 # sgmlparser already has backslashes protected
07202     # Protect Tcl specials
07203     regsub -all {([][$])} $value {\\\1} value
07204 
07205     # Deal with white space
07206     regsub -all "\[$::xml::Wsp\]" $value { } value
07207 
07208     # Find entity refs
07209     regsub -all {&([^;]+);} $value {[NormalizeAttValue:DeRef $opts {\1}]} value
07210 
07211     return [subst $value]
07212 }
07213 
07214 }
07215 
07216 namespace ::xml::tclparser {
07217 
07218 ret  {::xml::tclparser::NormalizeAttValue:DeRef} (type opts , type ref) {
07219 switch -glob -- $ref {
07220     #x* {
07221         scan [string range $ref 2 end] %x value
07222         set char [format %c $value]
07223         # Check that the char is legal for XML
07224         if {[regexp [format {^[%s]$} $::xml::Char] $char]} {
07225         return $char
07226         } else {
07227         return -code error "illegal character"
07228         }
07229     }
07230     #* {
07231         scan [string range $ref 1 end] %d value
07232         set char [format %c $value]
07233         # Check that the char is legal for XML
07234         if {[regexp [format {^[%s]$} $::xml::Char] $char]} {
07235         return $char
07236         } else {
07237         return -code error "illegal character"
07238         }
07239     }
07240     lt -
07241     gt -
07242     amp -
07243     quot -
07244     apos {
07245         array set map {lt < gt > amp & quot \" apos '}
07246         return $map($ref)
07247     }
07248     default {
07249         # A general entity.  Must resolve to a text value - no element structure.
07250 
07251         array set options $opts
07252         upvar #0 $options(entities) map
07253 
07254         if {[info exists map($ref)]} {
07255 
07256         if {[regexp < $map($ref)]} {
07257             return -code error "illegal character \"<\" in attribute value"
07258         }
07259 
07260         if {![regexp & $map($ref)]} {
07261             # Simple text replacement
07262             return $map($ref)
07263         }
07264 
07265         # There are entity references in the replacement text.
07266         # Can't use child entity parser since must catch element structures
07267 
07268         return [NormalizeAttValue $opts $map($ref)]
07269 
07270         } elseif {[string compare $options(-entityreferencecommand) "::sgml::noop"]} {
07271 
07272         set result [uplevel #0 $options(-entityreferencecommand) [list $ref]]
07273 
07274         return $result
07275 
07276         } else {
07277         return -code error "unable to resolve entity reference \"$ref\""
07278         }
07279     }
07280     }
07281 }
07282 
07283 }
07284 
07285 namespace ::xml::tclparser {
07286 
07287 ret  {::xml::tclparser::ParseAttrs} (type opts , type attrs) {
07288 set result {}
07289 
07290     while {[string length [string trim $attrs]]} {
07291     if {[regexp [::sgml::cl $::xml::Wsp]*($::xml::Name)[::sgml::cl $::xml::Wsp]*=[::sgml::cl $::xml::Wsp]*("|')([::sgml::cl ^<]*?)\\2(.*) $attrs discard attrName delimiter value attrs]} {
07292         lappend result $attrName [NormalizeAttValue $opts $value]
07293     } elseif {[regexp [::sgml::cl $::xml::Wsp]*$::xml::Name[::sgml::cl $::xml::Wsp]*=[::sgml::cl $::xml::Wsp]*("|')[::sgml::cl ^<]*\$ $attrs]} {
07294         return -code error [list {unterminated attribute value} $result $attrs]
07295     } else {
07296         return -code error "invalid attribute list"
07297     }
07298     }
07299 
07300     return $result
07301 }
07302 
07303 }
07304 
07305 namespace ::xml::tclparser {
07306 
07307 ret  {::xml::tclparser::ParseEmpty} (type tag , type attr , type e) {
07308 switch -glob [string length $e],[regexp "/[::xml::cl $::xml::Wsp]*$" $attr] {
07309     0,0 {
07310         return {}
07311     }
07312     0,* {
07313         return /
07314     }
07315     default {
07316         return $e
07317     }
07318     }
07319 }
07320 
07321 }
07322 
07323 namespace ::xml::tclparser {
07324 
07325 ret  {::xml::tclparser::ParseEntity} (type data) {
07326 set data [string trim $data]
07327     if {[regexp $::sgml::ExternalEntityExpr $data discard type delimiter1 id1 discard delimiter2 id2 optNDATA ndata]} {
07328     switch $type {
07329         PUBLIC {
07330         return [list external $id2 $id1 $ndata]
07331         }
07332         SYSTEM {
07333         return [list external $id1 {} $ndata]
07334         }
07335     }
07336     } elseif {[regexp {^("|')(.*?)\1$} $data discard delimiter value]} {
07337     return [list internal $value]
07338     } else {
07339     return -code error "badly formed entity declaration"
07340     }
07341 }
07342 
07343 }
07344 
07345 namespace ::xml::tclparser {
07346 
07347 ret  {::xml::tclparser::configure} (type name , type args) {
07348 upvar \#0 [namespace current]::$name parser
07349 
07350     # BUG: very crude, no checks for illegal args
07351     # Mats: Should be synced with sgmlparser.tcl
07352     set options {-elementstartcommand -elementendcommand  -characterdatacommand -processinginstructioncommand  -externalentitycommand -xmldeclcommand  -doctypecommand -commentcommand  -entitydeclcommand -unparsedentitydeclcommand  -parameterentitydeclcommand -notationdeclcommand  -elementdeclcommand -attlistdeclcommand  -paramentityparsing -defaultexpandinternalentities  -startdoctypedeclcommand -enddoctypedeclcommand  -entityreferencecommand -warningcommand  -defaultcommand -unknownencodingcommand -notstandalonecommand  -startcdatasectioncommand -endcdatasectioncommand  -errorcommand -final  -validate -baseuri -baseurl  -name -cmd -emptyelement  -parseattributelistcommand -parseentitydeclcommand  -normalize -internaldtd -dtdsubset  -reportempty -ignorewhitespace  -reportempty  }
07353     set usage [join $options ", "]
07354     regsub -all -- - $options {} options
07355     set pat ^-([join $options |])$
07356     foreach {flag value} $args {
07357     if {[regexp $pat $flag]} {
07358         # Validate numbers
07359         if {[info exists parser($flag)] &&  [string is integer -strict $parser($flag)] &&  ![string is integer -strict $value]} {
07360         return -code error "Bad value for $flag ($value), must be integer"
07361         }
07362         set parser($flag) $value
07363     } else {
07364         return -code error "Unknown option $flag, can be: $usage"
07365     }
07366     }
07367 
07368     # Backward-compatibility: -baseuri is a synonym for -baseurl
07369     catch {set parser(-baseuri) $parser(-baseurl)}
07370 
07371     return {}
07372 }
07373 
07374 }
07375 
07376 namespace ::xml::tclparser {
07377 
07378 ret  {::xml::tclparser::create} (type name) {
07379 # Initialise state variable
07380     upvar \#0 [namespace current]::$name parser
07381     array set parser [list -name $name           -cmd [uplevel 3 namespace current]::$name   -final 1                    -validate 0                     -statevariable [namespace current]::$name   -baseuri {}                     internaldtd {}                  entities [namespace current]::Entities$name     extentities [namespace current]::ExtEntities$name   parameterentities [namespace current]::PEntities$name   externalparameterentities [namespace current]::ExtPEntities$name    elementdecls [namespace current]::ElDecls$name  attlistdecls [namespace current]::AttlistDecls$name     notationdecls [namespace current]::NotDecls$name    depth 0                         leftover {}                                      ]
07382 
07383     # Initialise entities with predefined set
07384     array set [namespace current]::Entities$name [array get ::sgml::EntityPredef]
07385 
07386     return $parser(-cmd)
07387 }
07388 
07389 }
07390 
07391 namespace ::xml::tclparser {
07392 
07393 ret  {::xml::tclparser::createentityparser} (type parent , type name) {
07394 upvar #0 [namespace current]::$parent p
07395 
07396     # Initialise state variable
07397     upvar \#0 [namespace current]::$name external
07398     array set external [array get p]
07399 
07400     regsub $parent $p(-cmd) {} parentns
07401 
07402     array set external [list -name $name         -cmd $parentns$name                 -statevariable [namespace current]::$name   internaldtd {}                  line 0                      ]
07403     incr external(depth)
07404 
07405     return $external(-cmd)
07406 }
07407 
07408 }
07409 
07410 namespace ::xml::tclparser {
07411 
07412 ret  {::xml::tclparser::delete} (type name) {
07413 upvar \#0 [namespace current]::$name parser
07414     catch {::sgml::ParserDelete $parser(-statevariable)}
07415     catch {unset parser}
07416     return {}
07417 }
07418 
07419 }
07420 
07421 namespace ::xml::tclparser {
07422 
07423 ret  {::xml::tclparser::get} (type name , type method , type args) {
07424 upvar #0 [namespace current]::$name parser
07425 
07426     switch -- $method {
07427 
07428     elementdecl {
07429         switch [llength $args] {
07430 
07431         0 {
07432             # Return all element declarations
07433             upvar #0 $parser(elementdecls) elements
07434             return [array get elements]
07435         }
07436 
07437         1 {
07438             # Return specific element declaration
07439             upvar #0 $parser(elementdecls) elements
07440             if {[info exists elements([lindex $args 0])]} {
07441             return [array get elements [lindex $args 0]]
07442             } else {
07443             return -code error "element \"[lindex $args 0]\" not declared"
07444             }
07445         }
07446 
07447         default {
07448             return -code error "wrong number of arguments: should be \"elementdecl ?element?\""
07449         }
07450         }
07451     }
07452 
07453     attlist {
07454         if {[llength $args] != 1} {
07455         return -code error "wrong number of arguments: should be \"get attlist element\""
07456         }
07457 
07458         upvar #0 $parser(attlistdecls)
07459 
07460         return {}
07461     }
07462 
07463     entitydecl {
07464     }
07465 
07466     parameterentitydecl {
07467     }
07468 
07469     notationdecl {
07470     }
07471 
07472     default {
07473         return -code error "unknown method \"$method\""
07474     }
07475     }
07476 
07477     return {}
07478 }
07479 
07480 }
07481 
07482 namespace ::xml::tclparser {
07483 
07484 ret  {::xml::tclparser::parse} (type name , type xml , type args) {
07485 array set options $args
07486     upvar \#0 [namespace current]::$name parser
07487     variable tokExpr
07488     variable substExpr
07489 
07490     # Mats:
07491     if {[llength $args]} {
07492     eval {configure $name} $args
07493     }
07494 
07495     set parseOptions [list  -emptyelement [namespace code ParseEmpty]  -parseattributelistcommand [namespace code ParseAttrs]  -parseentitydeclcommand [namespace code ParseEntity]  -normalize 0]
07496     eval lappend parseOptions  [array get parser -*command]  [array get parser -reportempty]  [array get parser -ignorewhitespace]  [array get parser -name]  [array get parser -cmd]  [array get parser -baseuri]  [array get parser -validate]  [array get parser -final]  [array get parser -defaultexpandinternalentities]  [array get parser entities]  [array get parser extentities]  [array get parser parameterentities]  [array get parser externalparameterentities]  [array get parser elementdecls]  [array get parser attlistdecls]  [array get parser notationdecls]
07497 
07498     # Mats:
07499     # If -final 0 we also need to maintain the state with a -statevariable !
07500     if {!$parser(-final)} {
07501     eval lappend parseOptions [array get parser -statevariable]
07502     }
07503 
07504     set dtdsubset no
07505     catch {set dtdsubset $options(-dtdsubset)}
07506     switch -- $dtdsubset {
07507     internal {
07508         # Bypass normal parsing
07509         lappend parseOptions -statevariable $parser(-statevariable)
07510         array set intOptions [array get ::sgml::StdOptions]
07511         array set intOptions $parseOptions
07512         ::sgml::ParseDTD:Internal [array get intOptions] $xml
07513         return {}
07514     }
07515     external {
07516         # Bypass normal parsing
07517         lappend parseOptions -statevariable $parser(-statevariable)
07518         array set intOptions [array get ::sgml::StdOptions]
07519         array set intOptions $parseOptions
07520         ::sgml::ParseDTD:External [array get intOptions] $xml
07521         return {}
07522     }
07523     default {
07524         # Pass through to normal processing
07525     }
07526     }
07527 
07528     lappend tokenOptions   -internaldtdvariable [namespace current]::${name}(internaldtd)
07529     
07530     # Mats: If -final 0 we also need to maintain the state with a -statevariable !
07531     if {!$parser(-final)} {
07532     eval lappend tokenOptions [array get parser -statevariable]  [array get parser -final]
07533     }
07534     
07535     # Mats:
07536     # Why not the first four? Just padding? Lrange undos \n interp.
07537     # It is necessary to have the first four as well if chopped off in
07538     # middle of pcdata.
07539     set tokenised [lrange  [eval {::sgml::tokenise $xml $tokExpr $substExpr} $tokenOptions]  0 end]
07540 
07541     lappend parseOptions -internaldtd [list $parser(internaldtd)]
07542     eval ::sgml::parseEvent [list $tokenised] $parseOptions
07543 
07544     return {}
07545 }
07546 
07547 }
07548 
07549 namespace ::xml::tclparser {
07550 
07551 ret  {::xml::tclparser::reset} (type name) {
07552 upvar \#0 [namespace current]::$name parser
07553 
07554     # Has this parser object been properly initialised?
07555     if {![info exists parser] ||  ![info exists parser(-name)]} {
07556     return [create $name]
07557     }
07558 
07559     array set parser {
07560     -final 1
07561     depth 0
07562     leftover {}
07563     }
07564 
07565     foreach var {Entities ExtEntities PEntities ExtPEntities ElDecls AttlistDecls NotDecls} {
07566     catch {unset [namespace current]::${var}$name}
07567     }
07568 
07569     # Initialise entities with predefined set
07570     array set [namespace current]::Entities$name [array get ::sgml::EntityPredef]
07571 
07572     return {}
07573 }
07574 
07575 }
07576 
07577 namespace ::xml {
07578 
07579 ret  {::xml::zapWhite} (type data) {
07580 regsub -all "\[ \t\r\n\]+" $data { } data
07581     return $data
07582 }
07583 
07584 }
07585 
07586 ret  {Draw_comm} (optional unitd =all) {
07587 # Raffiche les connexions d'un composant (entrantes ou sortantes)
07588 # ou toutes les connexions (unitd == "all")
07589 
07590     #** global variables
07591     global liste_cnx_in  dico_cnx_out  dico_caract_cnx dico_units
07592     global viewcommcheck
07593     #** Variables specifiques pour le dessin
07594     global plot
07595     global coord_points_X coord_points_Y
07596 
07597     global coordunit
07598     global DRAW
07599     global zoomfactor
07600 
07601     set w $DRAW(window)
07602     set info .gui.pr.tools.infolabel
07603     set ray $DRAW(ray)
07604 
07605 #================================================================
07606 #=== destruction des communications
07607 #================================================================
07608 
07609     if {$unitd == "all"} {
07610         $w delete COMM_tag(all)
07611         $w dtag COMM_tag(all)
07612     } else {
07613         $w delete COMM_tag($unitd)
07614         $w dtag COMM_tag($unitd)
07615     }
07616     # S'il ne faut pas dessiner les connexions, c'est fini
07617     if {$viewcommcheck == 0} {return}
07618 
07619 #================================================================
07620 #===  Etablissement de la liste des couples de composants
07621 #===  et établissement de la liste des communications de chaque couple
07622 #================================================================
07623 
07624     set liste_couples {}
07625 
07626     foreach clef_cnx $liste_cnx_in {
07627         set appli_source $dico_caract_cnx($clef_cnx.app_source)
07628         set appli_cible  $dico_caract_cnx($clef_cnx.app_cible)
07629         set comp_source $dico_caract_cnx($clef_cnx.comp_source)
07630         set comp_cible  $dico_caract_cnx($clef_cnx.comp_cible)
07631 
07632         set clef_comp_source $appli_source.$comp_source
07633         set clef_comp_cible  $appli_cible.$comp_cible
07634 
07635         if {$unitd != "all"} {if {$unitd != $clef_comp_source && $unitd != $clef_comp_cible } {continue}}
07636 
07637         # Si on n'a pas encore vu ce couple de composants
07638         if { ! [info exists nbcom_parcouple_unite($clef_comp_source.$clef_comp_cible)] } {
07639             # Ajoute le couple à la liste
07640             lappend liste_couples $clef_comp_source.$clef_comp_cible
07641             # Initialise le nb de comm total
07642             set nbcom_parcouple_unite($clef_comp_source.$clef_comp_cible) 0
07643         }
07644         # Ajoute la connexion à la liste pour ce couple
07645         lappend table_liste_cnx($clef_comp_source.$clef_comp_cible) $clef_cnx
07646     }
07647 
07648     # Pour tous les couples d'unités
07649     foreach couple $liste_couples {
07650         # Compte le nombre total de connexions
07651         set nbcom_parcouple_unite($couple) [llength table_liste_cnx($couple)]
07652     }
07653 
07654 #================================================================
07655 #===  établissement de la liste des paires de composants
07656 #===  et établissement de la liste des communications de chaque paire dans chaque sens (montant ou descendant)
07657 #================================================================
07658 
07659     # Un couple : (a,b) est différent de (b,a)
07660     # Une paire : (a,b) similaire à (b,a)
07661     set liste_paires {}
07662 
07663     # Pour tous les couples de composants
07664     foreach couple $liste_couples {
07665         # Si le couple n'a pas déjà été traité
07666         if { [info exists table_liste_cnx($couple)] } {
07667             # Enregistre la paire dans la liste
07668             lappend liste_paires $couple
07669             
07670             # Crée la liste des connexions pour cette paire : cnx dans un sens et dans l'autre
07671             # ---------------------------------------------
07672             
07673             # Regarde la première connexion
07674             set clef_cnx [lindex $table_liste_cnx($couple) 0]
07675             set appli_source $dico_caract_cnx($clef_cnx.app_source)
07676             set appli_cible  $dico_caract_cnx($clef_cnx.app_cible)
07677             set comp_source $dico_caract_cnx($clef_cnx.comp_source)
07678             set comp_cible  $dico_caract_cnx($clef_cnx.comp_cible)
07679             set clef_comp_source $appli_source.$comp_source
07680             set clef_comp_cible  $appli_cible.$comp_cible
07681     
07682             # Regarde quelle unité est le plus bas dans le graphe
07683             set y_source $dico_units($clef_comp_source.coor_y)
07684             set y_cible  $dico_units($clef_comp_cible.coor_y)
07685     
07686             # Si l'unité source est la plus haute
07687             if { $y_source < $y_cible } {
07688                 # Mémorise le sens
07689                 set sens descend
07690                 set sens_inverse monte
07691             } else {
07692                 set sens monte
07693                 set sens_inverse descend
07694             }
07695             # Mémorise la liste des cnx dans une nouvelle table
07696             set liste_cnx($clef_comp_source.$clef_comp_cible.$sens) $table_liste_cnx($clef_comp_source.$clef_comp_cible)
07697             # Elimine la liste des cnx dans ce sens
07698             unset table_liste_cnx($clef_comp_source.$clef_comp_cible)
07699                         
07700             # S'il existe des connexions dans l'autre sens
07701             if { [info exists table_liste_cnx($clef_comp_cible.$clef_comp_source)] } {                
07702                 # Mémorise la liste des cnx dans une nouvelle table
07703                 set liste_cnx($clef_comp_source.$clef_comp_cible.$sens_inverse) $table_liste_cnx($clef_comp_cible.$clef_comp_source)
07704                 # Elimine la liste des cnx dans le sens inverse
07705                 unset table_liste_cnx($clef_comp_cible.$clef_comp_source)
07706             } else {
07707                 # Aucune connexion dans l'autre sens
07708                 set liste_cnx($clef_comp_source.$clef_comp_cible.$sens_inverse) {}
07709             }
07710         }
07711     }
07712     array unset table_liste_cnx
07713     
07714 #================================================================
07715 # Determine le point milieu de chaque fil de communication
07716 #================================================================
07717 
07718     # Pour toutes les paires d'unités
07719     foreach paire $liste_paires {
07720         # 1ère étape : étudier l'espace total occcupé par tous les points milieu
07721         # ----------------------------------------------------------------------
07722         
07723         # Détermine le nombre max de pictogrammes de transformations dans chaque sens
07724         set nbmax_transfo(descend) 0
07725         set nbmax_transfo(monte) 0
07726 
07727         # Pour les sens montant et descendant
07728         foreach sens {monte descend} {
07729             # Nombre de connexions dans ce sens entre les deux composants de la paire
07730             set nb_cnx($sens) [llength $liste_cnx($paire.$sens)]
07731             
07732             # Pour toutes les connexions dans ce sens entre les deux composants de la paire
07733             foreach clef_cnx $liste_cnx($paire.$sens) {
07734                 # Transformations de la connexions
07735                 set src_time_op  $dico_caract_cnx($clef_cnx.src_time_op)
07736                 set src_add_val  $dico_caract_cnx($clef_cnx.src_add_val)
07737                 set src_mult_val $dico_caract_cnx($clef_cnx.src_mult_val)
07738                 set tgt_time_op  $dico_caract_cnx($clef_cnx.tgt_time_op)
07739                 set tgt_add_val  $dico_caract_cnx($clef_cnx.tgt_add_val)
07740                 set tgt_mult_val $dico_caract_cnx($clef_cnx.tgt_mult_val)
07741                 set remail       $dico_caract_cnx($clef_cnx.remail)
07742     
07743                 # Détermine la liste des pictogrammes de transformations 
07744                 # dans l'ordre où elles sont effectivement appliquées :
07745                 #    -1- source local transf.  -2- middle transf. -3- target local transf.
07746                 
07747                 set liste_transfo($clef_cnx) {}
07748                 if { $src_add_val != 0 } { lappend liste_transfo($clef_cnx) add }
07749                 if { $src_mult_val != 1 } { lappend liste_transfo($clef_cnx) multiply }
07750                 switch $src_time_op {
07751                     taverage {lappend liste_transfo($clef_cnx) average}
07752                     accumul {lappend liste_transfo($clef_cnx) accumulate}
07753                 }
07754                 if { $remail != "" } { lappend liste_transfo($clef_cnx) remaillage }
07755                 if { $tgt_time_op == "time_linear"} { lappend liste_transfo($clef_cnx) interpol }
07756                 if { $tgt_add_val != 0 } { lappend liste_transfo($clef_cnx) add }
07757                 if { $tgt_mult_val != 1 } { lappend liste_transfo($clef_cnx) multiply }
07758                 # Calcule le nombre total de pictogrammes de transformations
07759                 set nb_transfo($clef_cnx) [llength $liste_transfo($clef_cnx)]
07760             
07761                 # Détermine le nombre max de pictogrammes de transformations dans chaque sens
07762                 if { $nb_transfo($clef_cnx) > $nbmax_transfo($sens) } {
07763                     set nbmax_transfo($sens) $nb_transfo($clef_cnx)
07764                 }
07765             }
07766         }
07767             
07768         # Les pictogrammes de transformations des cnx descendantes sont alignés horizontalement
07769         # Ceux des connexions montantes sont alignés verticalement
07770         
07771         # Hauteur totale de la zone des points milieu : 
07772         #   hauteur des pictogrm alignés verticalement plus nbre de lignes
07773         set hauteur [expr ($nbmax_transfo(monte) + $nb_cnx(descend)) * 30]
07774         # Largeur totale de la zone des points milieu
07775         #   largeur des pictogrm alignés horizontalement plus nbre de colonnes
07776         set largeur [expr ($nbmax_transfo(descend) + $nb_cnx(monte)) * 25] 
07777         # 30 et 25 pixels  sont les dimensions d'un pictogramme
07778 
07779         # 2ème étape : définir la position de tous les points milieu
07780         # ----------------------------------------------------------
07781 
07782         # Indicateur d'initialisation
07783         set initial 1
07784 
07785         # Pour toutes les connexions descendantes entre les deux composants de la paire
07786         foreach clef_cnx $liste_cnx($paire.descend) {
07787 
07788             # Si première cnx de la liste
07789             if  { $initial } {
07790                 set initial 0
07791 
07792                 # Lit les données de la connexion
07793                 set appli_source $dico_caract_cnx($clef_cnx.app_source)
07794                 set appli_cible  $dico_caract_cnx($clef_cnx.app_cible)
07795                 set comp_source $dico_caract_cnx($clef_cnx.comp_source)
07796                 set comp_cible  $dico_caract_cnx($clef_cnx.comp_cible)
07797                 set clef_comp_source $appli_source.$comp_source
07798                 set clef_comp_cible  $appli_cible.$comp_cible
07799     
07800                 # Si un des deux composants est rétréci
07801                 if {   $dico_units($clef_comp_source.expand) == 0
07802                     || $dico_units($clef_comp_cible.expand) == 0} {
07803                     # Rien à calculer car on ne dessinera pas les connexions une à une
07804                     break;
07805                 }    
07806                 # Détermine le milieu du composant source
07807                 set mil_source_x [expr $dico_units($clef_comp_source.coor_x) + $dico_units($clef_comp_source.largeur) / 2]
07808                 set mil_source_y [expr $dico_units($clef_comp_source.coor_y) + $DRAW(heigthunit) / 2]
07809                 # Détermine le milieu du composant cible
07810                 set mil_cible_x [expr $dico_units($clef_comp_cible.coor_x) + $dico_units($clef_comp_cible.largeur) / 2]
07811                 set mil_cible_y [expr $dico_units($clef_comp_cible.coor_y) + $DRAW(heigthunit) / 2]
07812 
07813                 # Détermine le milieu géométrique entre les deux composants
07814                 set milieu_comp_x [expr ($mil_source_x + $mil_cible_x) / 2]
07815                 set milieu_comp_y [expr ($mil_source_y + $mil_cible_y) / 2]
07816                 
07817                 # Ordonnée du point milieu pour la 1ère connexion : tout en haut
07818                 #  mais en dessous des pictogrammes des connexions montantes
07819                 set point_milieu_y [expr round( $milieu_comp_y - ($hauteur - 30) / 2 + 30 * $nbmax_transfo(monte) )]
07820                 # Abscisse du point milieu pour toutes les connexions descendantes : à gauche des connexions montantes
07821                 set point_milieu_x_general [expr round( $milieu_comp_x + ($largeur - 25) / 2 - 25 * $nb_cnx(monte) )]
07822             }
07823             
07824             # Abscisse du point milieu pour cette connexion : à gauche du point milieu general
07825             set point_milieu_x [expr round( $point_milieu_x_general - 25 * $nb_transfo($clef_cnx) / 2 )]
07826             
07827             # Mémorise les coordonnées du point milieu de la cnx
07828             set table_points_milieu($clef_cnx.x) $point_milieu_x
07829             set table_points_milieu($clef_cnx.y) $point_milieu_y
07830 
07831             # Calcule l'ordonnée du point milieu pour la connexion suivante
07832             incr point_milieu_y 30
07833         }
07834 
07835         # Indicateur d'initialisation
07836         set initial 1
07837 
07838         # Pour toutes les connexions montantes entre les deux composants de la paire
07839         foreach clef_cnx $liste_cnx($paire.monte) {
07840 
07841             # Si première cnx de la liste
07842             if  { $initial } {
07843                 set initial 0
07844 
07845                 # Lit les données de la connexion
07846                 set appli_source $dico_caract_cnx($clef_cnx.app_source)
07847                 set appli_cible  $dico_caract_cnx($clef_cnx.app_cible)
07848                 set comp_source $dico_caract_cnx($clef_cnx.comp_source)
07849                 set comp_cible  $dico_caract_cnx($clef_cnx.comp_cible)
07850                 set clef_comp_source $appli_source.$comp_source
07851                 set clef_comp_cible  $appli_cible.$comp_cible
07852     
07853                 # Si un des deux composants est rétréci
07854                 if {   $dico_units($clef_comp_source.expand) == 0
07855                     || $dico_units($clef_comp_cible.expand) == 0} {
07856                     # Rien à calculer car on ne dessinera pas les connexions une à une
07857                     break;
07858                 }    
07859                 # Détermine le milieu du composant source
07860                 set mil_source_x [expr $dico_units($clef_comp_source.coor_x) + $dico_units($clef_comp_source.largeur) / 2]
07861                 set mil_source_y [expr $dico_units($clef_comp_source.coor_y) + $DRAW(heigthunit) / 2]
07862                 # Détermine le milieu du composant cible
07863                 set mil_cible_x [expr $dico_units($clef_comp_cible.coor_x) + $dico_units($clef_comp_cible.largeur) / 2]
07864                 set mil_cible_y [expr $dico_units($clef_comp_cible.coor_y) + $DRAW(heigthunit) / 2]
07865 
07866                 # Détermine le milieu géométrique entre les deux composants
07867                 set milieu_comp_x [expr ($mil_source_x + $mil_cible_x) / 2]
07868                 set milieu_comp_y [expr ($mil_source_y + $mil_cible_y) / 2]
07869                 
07870                 # Abscisse du point milieu pour la 1ère connexion : tout à fait à gauche
07871                 #  mais à droite des pictogrammes des connexions descendantes
07872                 set point_milieu_x [expr round( $milieu_comp_x - ($largeur - 25) / 2 + 25 * $nbmax_transfo(descend))]
07873                 # Ordonnée du point milieu pour toutes les connexions montantes : au dessus des connexions descendantes
07874                 set point_milieu_y_general [expr round( $milieu_comp_y + ($hauteur - 30) / 2 - 30 * $nb_cnx(descend) )]
07875             }
07876             
07877             # Ordonnée du point milieu pour cette connexion : au dessous du point milieu general
07878             set point_milieu_y [expr round( $point_milieu_y_general - 30 * $nb_transfo($clef_cnx) / 2 )]
07879             
07880             # Mémorise les coordonnées du point milieu de la cnx
07881             set table_points_milieu($clef_cnx.x) $point_milieu_x
07882             set table_points_milieu($clef_cnx.y) $point_milieu_y
07883 
07884             # Calcule l'abscisse du point milieu pour la connexion suivante
07885             incr point_milieu_x 25
07886         }
07887 
07888     }
07889 
07890 #================================================================
07891 #=== affichage des communications
07892 #================================================================
07893 
07894     # Pour toutes les paires de composants
07895     foreach paire $liste_paires {
07896 
07897         # Pour les sens montant et descendant
07898         foreach sens {monte descend} {
07899             # Pour toutes les connexions entre les deux composants de la paire
07900             foreach clef_cnx $liste_cnx($paire.$sens) {
07901     
07902                 set appli_source $dico_caract_cnx($clef_cnx.app_source)
07903                 set appli_cible  $dico_caract_cnx($clef_cnx.app_cible)
07904                 set comp_source $dico_caract_cnx($clef_cnx.comp_source)
07905                 set comp_cible  $dico_caract_cnx($clef_cnx.comp_cible)
07906                 set clef_comp_source $appli_source.$comp_source
07907                 set clef_comp_cible  $appli_cible.$comp_cible
07908     
07909                 # Determine la clef des points de couplage source et cible
07910                 set champ_source $dico_caract_cnx($clef_cnx.champ_source)
07911                 set champ_cible  $dico_caract_cnx($clef_cnx.champ_cible)
07912                 set clef_point_source $clef_comp_source.$champ_source.o
07913                 set clef_point_cible  $clef_comp_cible.$champ_cible.i
07914     
07915                 # Si un des deux composants est rétréci
07916                 if {   $dico_units($clef_comp_source.expand) == 0
07917                     || $dico_units($clef_comp_cible.expand) == 0} {
07918     
07919                     # Si les deux composants sont rétrécis
07920                     if {   $dico_units($clef_comp_source.expand) == 0
07921                         && $dico_units($clef_comp_cible.expand) == 0} {
07922     
07923                         set x1 [expr $coord_points_X($clef_point_source)+$dico_units($clef_comp_source.coor_x)]
07924                         set y1 [expr $coord_points_Y($clef_point_source)+$ray+$dico_units($clef_comp_source.coor_y)]
07925                         set x2 [expr $coord_points_X($clef_point_cible)+$dico_units($clef_comp_cible.coor_x)]
07926                         set y2 [expr $coord_points_Y($clef_point_cible)-$ray+$dico_units($clef_comp_cible.coor_y)]
07927                         if {$y1 < $y2 } {
07928                             set x3 [expr $x1+($x2-$x1)/10 ]
07929                             set x4 [expr $x2-($x2-$x1)/10 ]
07930                             set y3 [expr $y1+($y2-$y1)/10 +12]
07931                             set y4 [expr $y2-($y2-$y1)/10 -12]
07932                             set midleX [expr $x3+($x4-$x3)/3]
07933                             set midleY [expr $y3+($y4-$y3)/3]
07934                         } else {
07935                             set midleX [expr ($x1+$x2)/2]
07936                             set midleY [expr ($y1+$y2)/2]
07937                         }
07938                         if {[distming $x1 $y1 $x2 $y2]} {
07939                             set itemcom [$w create line [chemin $x1 $y1 $x2 $y2] -smooth 1 -fill grey80 -width 3]
07940                             $w addtag COMM_tag(all) withtag $itemcom
07941                             $w addtag COMM_tag($clef_comp_source) withtag $itemcom
07942                             $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
07943                             set itemcom [$w create rectangle [expr $midleX-8] [expr $midleY-6] [expr $midleX+8] [expr $midleY+5] -fill white  -outline white ]
07944                             $w addtag COMM_tag(all) withtag $itemcom
07945                             $w addtag COMM_tag($clef_comp_source) withtag $itemcom
07946                             $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
07947                             set itemcom [$w create text $midleX $midleY -text "$nbcom_parcouple_unite($clef_comp_source.$clef_comp_cible)" -font {helvetica -10 bold} -fill black  ]
07948                             $w addtag COMM_tag(all) withtag $itemcom
07949                             $w addtag COMM_tag($clef_comp_source) withtag $itemcom
07950                             $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
07951                         }
07952                     } else {
07953     
07954                         # Un seul des deux composants est rétréci
07955                         # --> on n'affiche aucun fil de connexion
07956                     }
07957                     # Ne traite pas les autres cnx de la paire
07958                     break
07959     
07960                 } else {
07961                     # Aucun des deux composants n'est rétréci
07962                     #=====affichage liaison
07963     
07964                     #===type de trait
07965                     set dash {2 2}
07966                     #===epaisseur
07967                     set wid 1
07968                     #====couleur
07969                     set colorcom [color_rgb 128 80 0]
07970     
07971                     # Point de départ de la liaison
07972                     set x1 [expr $coord_points_X($clef_point_source)+$dico_units($clef_comp_source.coor_x)]
07973                     set y1 [expr $coord_points_Y($clef_point_source)+$ray+$dico_units($clef_comp_source.coor_y)]
07974     
07975                     # Point d'arrivée
07976                     set x2 [expr $coord_points_X($clef_point_cible)+$dico_units($clef_comp_cible.coor_x)]
07977                     set y2 [expr $coord_points_Y($clef_point_cible)-$ray+$dico_units($clef_comp_cible.coor_y)]
07978     
07979                     # Si liaison entre deux points du même composant
07980                     if {$clef_comp_source == $clef_comp_cible} {
07981                         set itemcom [$w create line $x1 [expr $y1-2*$ray] $x2 [expr $y2+2*$ray] -fill  $colorcom -width $wid -dash $dash]
07982                         $w addtag COMM_tag(all) withtag $itemcom
07983                         $w addtag COMM_tag($clef_comp_source) withtag $itemcom
07984                         $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
07985                         $w addtag tag_connexions($clef_cnx) withtag $itemcom
07986                         $w addtag tag_fils_connexions($clef_cnx) withtag $itemcom
07987     
07988                     } else {
07989     
07990                         # Si la distance entre le départ et l'arrivée n'est pas trop grande
07991                         if {[distmin $x1 $y1 $x2 $y2]} {
07992 
07993                             # Si la cnx a des transformations
07994                             if { $nb_transfo($clef_cnx) != "0" } {
07995                                 # Affiche la liaison en deux morceaux :
07996                                 #    1 :   du composant source au point milieu
07997                                 #    2 :   du point milieu au composant cible
07998         
07999                                 # Si on va de source dessus à cible dessous
08000                                 # if  $y2 > $y1 
08001                                 if { $dico_units($clef_comp_source.coor_y) < $dico_units($clef_comp_cible.coor_y) } {
08002                                     # On traverse le point milieu par l'horizontale
08003                                     # demi-largeur de l'ensemble des pictogrammes des transformations
08004                                     set demi_largeur [expr 25 * $nb_transfo($clef_cnx) / 2]
08005                                     set agauche_milieu_x [expr $table_points_milieu($clef_cnx.x) - $demi_largeur]
08006                                     set adroite_milieu_x [expr $table_points_milieu($clef_cnx.x) + $demi_largeur]
08007                                     # Si on va de gauche à droite
08008                                     if { $x1 < $x2 } {
08009                                         set cote_approche gauche
08010                                         set avant_milieu_x $agauche_milieu_x
08011                                         set apres_milieu_x $adroite_milieu_x
08012                                     } else {
08013                                         set cote_approche droit
08014                                         set avant_milieu_x $adroite_milieu_x
08015                                         set apres_milieu_x $agauche_milieu_x
08016                                     }
08017                                     set avant_milieu_y $table_points_milieu($clef_cnx.y)
08018                                     set apres_milieu_y $table_points_milieu($clef_cnx.y)
08019                                 } else {
08020                                     set cote_approche bas
08021                                     # On traverse le point milieu par la verticale (et par dessous)                            
08022                                     # demi-hauteur de l'ensemble des pictogrammes des transformations
08023                                     set demi_hauteur [expr 30 * $nb_transfo($clef_cnx) / 2]
08024                                     set avant_milieu_y [expr $table_points_milieu($clef_cnx.y) + $demi_hauteur]
08025                                     set apres_milieu_y [expr $table_points_milieu($clef_cnx.y) - $demi_hauteur]
08026                                     
08027                                     set avant_milieu_x $table_points_milieu($clef_cnx.x)
08028                                     set apres_milieu_x $table_points_milieu($clef_cnx.x)
08029                                 }
08030         
08031                                 # 1er morceau  :   du composant source au point milieu
08032                                 # ----------------------------------------------------
08033         
08034                                 set demi_chemin [demi_chemin_1 $x1 $y1 $avant_milieu_x $avant_milieu_y $cote_approche $clef_comp_source]
08035                                 set itemcom [$w create line $demi_chemin -smooth 1 -fill $colorcom -width $wid -dash $dash]
08036                                 $w addtag COMM_tag(all) withtag $itemcom
08037                                 $w addtag COMM_tag($clef_comp_source) withtag $itemcom
08038                                 $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
08039                                 $w addtag tag_connexions($clef_cnx) withtag $itemcom
08040                                 $w addtag tag_fils_connexions($clef_cnx) withtag $itemcom
08041         
08042                                 # 2ème morceau  :   du point milieu au composant cible
08043                                 # ----------------------------------------------------
08044         
08045                                 set demi_chemin [demi_chemin_2 $apres_milieu_x $apres_milieu_y $x2 $y2 $cote_approche $clef_comp_cible]
08046                                 set itemcom [$w create line $demi_chemin -smooth 1 -fill $colorcom -width $wid -dash $dash]
08047                                 $w addtag COMM_tag(all) withtag $itemcom
08048                                 $w addtag COMM_tag($clef_comp_source) withtag $itemcom
08049                                 $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
08050                                 $w addtag tag_connexions($clef_cnx) withtag $itemcom
08051                                 $w addtag tag_fils_connexions($clef_cnx) withtag $itemcom
08052         
08053                                 # 3eme : Affiche les symboles (pictogrammes) des transformations
08054                                 # --------------------------------------------------------------
08055                                 
08056                                 # Si la connexion est "montante"
08057                                 if { $cote_approche == "bas" } {
08058                                     # Dispose les pictogrammes sur une colonne en partant du bas
08059                                     set offset_1ere_tranfo_y [expr 30 * ($nb_transfo($clef_cnx) - 1) / 2]
08060                                     set pos_tranfo_y [expr $table_points_milieu($clef_cnx.y) + $offset_1ere_tranfo_y]
08061                                     set increment_y -30
08062                                     set pos_tranfo_x $table_points_milieu($clef_cnx.x)
08063                                     set increment_x 0
08064                                 } else {
08065                                     # Dispose les pictogrammes sur une ligne
08066                                     set offset_1ere_tranfo_x [expr 25 * ($nb_transfo($clef_cnx) - 1) / 2]
08067                                     # Si on va de source à gauche à cible à droite
08068                                     if { $x1 < $x2 } {
08069                                         # Dispose les pictogrammes sur une ligne en partant de la gauche
08070                                         set pos_tranfo_x [expr $table_points_milieu($clef_cnx.x) - $offset_1ere_tranfo_x]
08071                                         set increment_x 25
08072                                     } else {
08073                                         # Dispose les pictogrammes sur une ligne en partant de la droite
08074                                         set pos_tranfo_x [expr $table_points_milieu($clef_cnx.x) + $offset_1ere_tranfo_x]
08075                                         set increment_x -25
08076                                     }
08077                                     set pos_tranfo_y $table_points_milieu($clef_cnx.y)
08078                                     set increment_y 0
08079                                 }
08080                                 
08081                                 # Pour toutes les tranformations existantes
08082                                 foreach transfo $liste_transfo($clef_cnx) {
08083                                     # Met un pictogramme dans le graphe
08084                                     set itemcom [$w create image $pos_tranfo_x $pos_tranfo_y -image img_$transfo]
08085                                     $w addtag COMM_tag(all) withtag $itemcom
08086                                     $w addtag COMM_tag($clef_comp_source) withtag $itemcom
08087                                     $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
08088                                     $w addtag tag_connexions($clef_cnx) withtag $itemcom
08089                                     # Calcule les coordonnées de la transfo suivante
08090                                     incr pos_tranfo_x $increment_x
08091                                     incr pos_tranfo_y $increment_y
08092                                 }
08093                             } else {
08094                                 # S'il y a au moins un pictogramme de transformation dans ce sens
08095                                 if { $nbmax_transfo($sens) > 0 } {
08096                                     # Affiche la liaison en un seul morceau forcé de passer par le point milieu
08097                                     set milieu_x $table_points_milieu($clef_cnx.x)
08098                                     set milieu_y $table_points_milieu($clef_cnx.y)
08099                                     set itemcom [$w create line [chemin_force $x1 $y1 $x2 $y2 $milieu_x $milieu_y]  -smooth 1 -fill  $colorcom -width $wid -dash $dash]
08100                                 } else {
08101                                     # Affiche la liaison en un seul morceau
08102                                     set itemcom [$w create line [chemin $x1 $y1 $x2 $y2]  -smooth 1 -fill  $colorcom -width $wid -dash $dash]
08103                                 }
08104                                 $w addtag COMM_tag(all) withtag $itemcom
08105                                 $w addtag COMM_tag($clef_comp_source) withtag $itemcom
08106                                 $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
08107                                 $w addtag tag_connexions($clef_cnx) withtag $itemcom
08108                                 $w addtag tag_fils_connexions($clef_cnx) withtag $itemcom
08109                             }
08110                         
08111                         } else {
08112                             # Les deux composants sont trop éloignés l'un de l'autre
08113                             # On trace juste le départ et l'arrivée de la liaison : le milieu est caché
08114 
08115                             set itemcom [$w create line $x1 $y1 $x1 [expr $y1+20]  -fill  $colorcom -width $wid -dash $dash]
08116                             $w addtag COMM_tag(all) withtag $itemcom
08117                             $w addtag COMM_tag($clef_comp_source) withtag $itemcom
08118                             $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
08119                             $w addtag tag_connexions($clef_cnx) withtag $itemcom
08120                             $w addtag tag_fils_connexions($clef_cnx) withtag $itemcom
08121                             set itemcom [$w create line  $x2 $y2 $x2 [expr $y2-20]  -fill  $colorcom -width $wid -dash $dash]
08122                             $w addtag COMM_tag(all) withtag $itemcom
08123                             $w addtag COMM_tag($clef_comp_source) withtag $itemcom
08124                             $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
08125                             $w addtag tag_connexions($clef_cnx) withtag $itemcom
08126                             $w addtag tag_fils_connexions($clef_cnx) withtag $itemcom
08127                             # Le fil central qui relie les deux extrémités est caché
08128                             # Il n'est révélé que lorsque la souris survole l'une des extrémité
08129                             set itemcom [$w create line  $x1  [expr $y1+20]  $x2 [expr $y2-20]  -fill  $colorcom -width $wid -dash $dash -state hidden]
08130                             $w addtag COMM_tag(all) withtag $itemcom
08131                             $w addtag COMM_tag($clef_comp_source) withtag $itemcom
08132                             $w addtag COMM_tag($clef_comp_cible) withtag $itemcom
08133                             $w addtag tag_fils_connexions($clef_cnx) withtag $itemcom
08134                             $w addtag liaison($clef_cnx) withtag $itemcom
08135                         }
08136                     }
08137                     #=====bind evenements comm
08138                     $w bind tag_connexions($clef_cnx) <Any-Enter> " $w itemconfigure liaison($clef_cnx) -state normal ;help_communication %X %Y $clef_cnx
08139                                             $info configure -text {Double click to edit the connection}"
08140                     $w bind tag_connexions($clef_cnx) <Any-Leave> " $w itemconfigure liaison($clef_cnx) -state hidden ;help_object_hide
08141                                             $info configure -text {}"
08142                     $w bind tag_connexions($clef_cnx) <Button-1>  " minimenu_comm $clef_cnx"
08143                     $w bind tag_connexions($clef_cnx) <Double-Button-1>  "control_edit_entity"
08144                     $w bind tag_connexions($clef_cnx) <Key-Delete>  "control_delete_entity"
08145                 }
08146             }
08147         }
08148     }
08149 
08150 #-------------------------------------
08151 #** zoom
08152 #-------------------------------------
08153     if {$unitd == "all"} {
08154         $w scale COMM_tag(all) 0 0 $zoomfactor $zoomfactor
08155     } else {
08156         $w scale COMM_tag($unitd) 0 0 $zoomfactor $zoomfactor
08157     }
08158 }
08159 
08160 ret  {Draw_drawunit} () {
08161 #
08162     global dico_applis liste_fichiers
08163     #** Variables specifiques pour le dessin
08164     global DRAW
08165 
08166     # Efface toutes les unités affichées
08167     set w $DRAW(window)
08168     $w delete UNIT_tag(all)
08169     $w dtag UNIT_tag(all)
08170     
08171     # Détermine la liste des applications chargées en mémoire
08172     set liste_applis ""
08173     catch {set liste_applis $dico_applis(lapplis)}
08174     # Pour toutes les applications chargées en mémoire
08175     foreach nom_appli $liste_applis {
08176         # Pour tous les composants de cette application
08177         foreach nom_composant $dico_applis(app.$nom_appli.lcomp) {
08178             Draw_one_unit $nom_appli.$nom_composant
08179         }
08180     }
08181     
08182     # Pour tous les fichiers
08183     foreach id_fichier $liste_fichiers {
08184         Draw_one_unit ///file///.$id_fichier
08185     }
08186     
08187     return
08188 }
08189 
08190 ret  {Draw_one_unit} (type clef_, type unit) {
08191 # affiche une unité dans le canvas
08192 # clef_unit : 
08193 #  - si c'est un composant d'application : clef du composant, composée ainsi <nom_application>.<nom_composant>
08194 #  - si c'est un fichier NetCDF          : clef du fichier,   composée ainsi ///file///.<id-fichier>
08195 #
08196 # gere les procedures d'evenement
08197 
08198     global dico_applis dico_caract_fichiers
08199     global dico_units
08200     global liste_points_i liste_points_o
08201     global dico_caract_points
08202 
08203     global coord_points_X coord_points_Y
08204     global plot
08205 
08206     # Renomme le paramètre
08207     set i $clef_unit
08208 
08209     #** Variables specifiques pour le dessin
08210     global DRAW
08211     global zoomfactor
08212     set w $DRAW(window)
08213     set ray $DRAW(ray)
08214     set info .gui.pr.tools.infolabel
08215     
08216     $w delete UNIT_tag($i)
08217     $w dtag UNIT_tag($i)
08218     $w delete UNIT_texttag($i)
08219     $w dtag UNIT_texttag($i)
08220     $w delete UNIT_expand_tag($i)
08221     $w dtag UNIT_expand_tag($i)
08222 
08223 #================================================================
08224 #=== affichage des unites
08225 #================================================================
08226 #================================================================
08227     set id_unit $dico_units($i)
08228     # Extrait de la clef le nom de l'appli
08229     set len_unit [string length $id_unit]
08230     incr len_unit
08231     set nom_appli [string range $i 0 end-$len_unit]
08232     
08233     # S'il s'agit d'un fichier NetCDF
08234     if { $nom_appli == "///file///" } {
08235         set si_fichier 1
08236         set id_fichier $id_unit
08237         # Le nom du fichier
08238         set nom_unit $dico_caract_fichiers($id_fichier.name)
08239         set nom_long_unit ""
08240         
08241     # il s'agit d'un composant d'une application    
08242     } else {
08243         set si_fichier 0
08244         # Nom du composant
08245         set nom_unit $id_unit
08246         # Clef d'acces au composant dans le dictionnaire dico_applis
08247         set clef_comp_2 app.$nom_appli.comp.$nom_unit
08248         # Nom long du composant
08249         set nom_long_unit ""
08250         catch {set nom_long_unit $dico_applis($clef_comp_2.long_name)}
08251     }
08252     
08253     # Titres de l'unité à afficher
08254     set text1 $nom_unit
08255     if { [string length $text1] > 20 } {
08256         set text1 [string range $text1 0 20]
08257         set text1 "$text1..."
08258     }
08259     set text2 $nom_long_unit
08260     if { [string length $text2] > 30 } {
08261         set text2 [string range $text2 0 30]
08262         set text2 "$text2..."
08263     }
08264 
08265     set coor_x $dico_units($i.coor_x)
08266     set coor_y $dico_units($i.coor_y)
08267 
08268     # Nombre de points en entrée et en sortie de l'unité
08269     set liste_clefs_points_i [lsearch -glob -all -inline $liste_points_i "$i.*"]
08270     set liste_clefs_points_o [lsearch -glob -all -inline $liste_points_o "$i.*"]
08271     set nbget  [llength $liste_clefs_points_i]
08272     set nbput  [llength $liste_clefs_points_o]
08273 
08274 #== calcul largeur boite
08275     set nbb $nbput
08276     if {$nbget > $nbb} {set nbb $nbget}
08277     set widthtot [expr ($nbb + 2) * $DRAW(widthcombox)]
08278     set wrefp [expr $nbput * $DRAW(widthcombox)]
08279     set wrefg [expr $nbget * $DRAW(widthcombox)]
08280     if {$widthtot < $DRAW(widthminunit)} {set widthtot $DRAW(widthminunit)}
08281     set widthtex [expr [string length $text1] *8]
08282     if {$widthtot < $widthtex} {set widthtot $widthtex}
08283     set widthtex [expr [string length $text2] *6]
08284     if {$widthtot < $widthtex} {set widthtot $widthtex}
08285 
08286     # Mémorise la largeur de la boite
08287     set dico_units($i.largeur) $widthtot
08288     if { $si_fichier } {
08289         # Tient compte des deux bords verticaux de la boite
08290         set widthtot [expr $widthtot + 2 * $DRAW(hcont)]
08291     }
08292     set midle_x [expr $coor_x + $widthtot/2]
08293     
08294 #===couleurs de l'unité
08295     set colorunit $dico_units($i.colour)
08296     set colortext [color_white_or_black $colorunit]
08297     set pastel    [color_pastelise $colorunit]
08298     set fonce [color_fonce $colorunit]
08299 
08300 
08301 #===boite extérieure
08302 
08303     # Si c'est un fichier NetCDF
08304     if { $si_fichier } {
08305         # Dessine deux bandes verticales à gauche et à droite du rectangle principal
08306         set hauteur [expr $DRAW(heigthunit) + 2 * $DRAW(hcont)]
08307         set bord_gauche [$w create rectangle $coor_x $coor_y [expr $coor_x + $DRAW(hcont)]  [expr $coor_y + $hauteur] -fill $pastel -outline  $pastel]
08308         $w addtag unitpoint($i) withtag $bord_gauche
08309         $w addtag UNIT_tag($i) withtag $bord_gauche
08310         $w addtag UNIT_tag(all) withtag $bord_gauche
08311         set bord_droit [$w create rectangle [expr $coor_x + $widthtot - $DRAW(hcont)] $coor_y  [expr $coor_x + $widthtot]  [expr $coor_y + $hauteur] -fill $pastel -outline  $pastel]
08312         $w addtag unitpoint($i) withtag $bord_droit
08313         $w addtag UNIT_tag($i) withtag $bord_droit
08314         $w addtag UNIT_tag(all) withtag $bord_droit
08315     } else {
08316         # Dessine deux bandes horizontales au dessus et dessous du rectangle principal
08317         set containerput [$w create rectangle $coor_x  [expr $coor_y + $DRAW(heigthunit) + $DRAW(hcont)]   [expr $coor_x + $widthtot]  [expr $coor_y + $DRAW(heigthunit) + 2*$DRAW(hcont)] -fill $pastel -outline  $pastel]
08318         $w addtag unitpoint($i) withtag $containerput
08319         $w addtag UNIT_tag($i) withtag $containerput
08320         $w addtag UNIT_tag(all) withtag $containerput
08321         set containerget [$w create rectangle $coor_x $coor_y  [expr $coor_x + $widthtot]  [expr $coor_y + $DRAW(hcont)] -fill $pastel -outline  $pastel]
08322         $w addtag unitpoint($i) withtag $containerget
08323         $w addtag UNIT_tag($i) withtag $containerget
08324         $w addtag UNIT_tag(all) withtag $containerget
08325     }
08326     
08327 #===bouton expand
08328     set colorbout white
08329     if {$dico_units($i.expand) == 0} {set colorbout grey80}
08330     set bexp [$w create rectangle [expr $coor_x+$widthtot -12] [expr $coor_y + $DRAW(hcont) - 2]  [expr $coor_x + $widthtot-2]  [expr $coor_y + $DRAW(hcont) - 9] -fill $colorbout -outline  grey50]
08331     $w addtag UNIT_tag($i) withtag $bexp
08332     $w addtag UNIT_tag(all) withtag $bexp
08333     $w addtag UNIT_expand_tag($i) withtag $bexp
08334     if {$dico_units($i.expand) == 0} {
08335         $w bind UNIT_expand_tag($i) <1> "global dico_units; set dico_units($i.expand) 1; Draw_one_unit $i;Draw_comm $i;.gui configure -cursor {}"
08336         $w bind UNIT_expand_tag($i) <Any-Enter> ".gui configure -cursor sb_h_double_arrow"
08337         $w bind UNIT_expand_tag($i) <Any-Leave> ".gui configure -cursor {}"
08338     } else {
08339         $w bind UNIT_expand_tag($i) <1> "global dico_units; set dico_units($i.expand) 0; Draw_one_unit $i; Draw_comm $i;.gui configure -cursor {}"
08340         $w bind UNIT_expand_tag($i) <Any-Enter> ".gui configure -cursor sb_left_arrow"
08341         $w bind UNIT_expand_tag($i) <Any-Leave> ".gui configure -cursor {}"
08342     }
08343 
08344 #===boite intérieure
08345     
08346     # Si c'est un fichier NetCDF
08347     if { $si_fichier } {
08348         set itemunit [$w create rectangle [expr $coor_x + $DRAW(hcont)] $coor_y  [expr $coor_x + $widthtot - $DRAW(hcont)]  [expr $coor_y + 2 * $DRAW(hcont) + $DRAW(heigthunit)] -fill $colorunit -outline $colorunit]
08349     } else {
08350         set itemunit [$w create rectangle $coor_x  [expr $coor_y + $DRAW(hcont)]  [expr $coor_x + $widthtot]  [expr $coor_y + $DRAW(hcont) + $DRAW(heigthunit)] -fill $colorunit -outline $colorunit]
08351     }
08352     $w addtag UNIT_tag($i) withtag $itemunit
08353     $w addtag UNIT_tag(all) withtag $itemunit
08354     $w addtag unitpoint($i) withtag $itemunit
08355     $w addtag unitpoint_color($i) withtag $itemunit
08356     
08357     if { $text2 == "" } {
08358         # Un seul texte place au centre de la boite
08359         set pos_text1   12
08360     } else {
08361         set pos_text1   7
08362     }
08363     
08364     set itemunit [$w create text [expr $coor_x+$widthtot/2]  [expr $coor_y + $DRAW(hcont) + $pos_text1] -text $text1 -font "helvetica -[expr int(12*$zoomfactor)] bold" -fill $colortext]
08365     $w addtag unitpoint($i) withtag $itemunit
08366     $w addtag UNIT_tag($i) withtag $itemunit
08367     $w addtag UNIT_tag(all) withtag $itemunit
08368     $w addtag UNIT_texttag($i) withtag $itemunit
08369     if { $text2 != "" } {
08370         set itemunit [$w create text [expr $coor_x+$widthtot/2] [expr $coor_y + $DRAW(hcont) + 19] -text $text2 -font "helvetica -[expr int(10*$zoomfactor)] bold" -fill $colortext]
08371         $w addtag unitpoint($i) withtag $itemunit
08372         $w addtag UNIT_tag($i) withtag $itemunit
08373         $w addtag UNIT_tag(all) withtag $itemunit
08374         $w addtag UNIT_texttag($i) withtag $itemunit
08375     }
08376        
08377     # Si c'est un composant d'une application
08378     if { ! $si_fichier } {
08379         set itemunit [$w create text [expr $coor_x+$widthtot+12] [expr $coor_y + $DRAW(hcont) + 28] -text "0" -font "helvetica -[expr int(12*$zoomfactor)] bold" -fill white -anchor w -state hidden]
08380         $w addtag unitpoint($i) withtag $itemunit
08381         $w addtag UNIT_tag($i) withtag $itemunit
08382         $w addtag UNIT_tag(all) withtag $itemunit
08383     }
08384 #=======================================
08385 #==== gros rond pour les unites fermees
08386 #=======================================
08387     if {$dico_units($i.expand) == 0} {
08388         set x1 $midle_x
08389         set y1 [expr int([expr $coor_y + $DRAW(hcont)/2])]
08390         if {$nbget > 0} {
08391             set item [$w create  oval [expr $x1-2*$ray] [expr $y1 -$ray] [expr $x1+2*$ray] [expr $y1+$ray] -fill grey80]
08392             $w addtag UNIT_tag($i) withtag $item
08393             $w addtag UNIT_tag(all) withtag $item
08394         }
08395         foreach clef_point $liste_clefs_points_i {
08396             set coord_points_X($clef_point) [expr $x1 - $coor_x]
08397             set coord_points_Y($clef_point) [expr $y1 - $coor_y]
08398         }
08399         if {$nbput > 0} {
08400             set y1 [expr int([expr $coor_y+$DRAW(heigthunit) + 3*$DRAW(hcont)/2])]
08401             set item [$w create  oval [expr $x1-2*$ray] [expr $y1 -$ray] [expr $x1+2*$ray] [expr $y1+$ray] -fill grey80]
08402             $w addtag UNIT_tag($i) withtag $item
08403             $w addtag UNIT_tag(all) withtag $item
08404         }
08405         foreach clef_point $liste_clefs_points_o {
08406             set coord_points_X($clef_point)  [expr $x1 - $coor_x]
08407             set coord_points_Y($clef_point)  [expr $y1 - $coor_y]
08408         }
08409     } else {
08410 #=================================
08411 #====les petits ronds  du haut et du bas
08412 #================================
08413         Draw_points_de_couplage in $liste_clefs_points_i $nom_appli $id_unit $coor_x $coor_y [expr ($wrefg - $widthtot) / 2]
08414         Draw_points_de_couplage out $liste_clefs_points_o $nom_appli $id_unit $coor_x $coor_y [expr ($wrefp - $widthtot) / 2]
08415     }         
08416     set txt1 "{double click to edit the unit, left click to move the unit icon}"
08417 
08418 
08419 #==== bind evenement unit
08420     $w bind unitpoint($i) <Any-Enter> "$info configure -text $txt1 "
08421     $w bind unitpoint($i) <Any-Leave> "$info configure -text {}"
08422 
08423     $w bind unitpoint($i) <Button-1>  "minimenu_unit $i"
08424     $w bind unitpoint($i) <Button-1> "+ .gui configure -cursor fleur; unitplotdown $w %x %y $i"
08425     $w bind unitpoint($i) <ButtonRelease-1> ".gui configure -cursor {}; unitplotrelease $w %x %y $i"
08426 
08427     $w bind unitpoint($i) <Double-Button-1>  "unit_edit $i"
08428     $w bind unitpoint($i) <Key-Delete>  "unit_delete $i"
08429 
08430     $w scale UNIT_tag($i) 0 0 $zoomfactor $zoomfactor
08431     set plot(lastX)  0
08432     set plot(lastY)  0
08433     set plot(firstX) 0
08434     set plot(firstY) 0
08435 }
08436 
08437 ret  {Draw_points_de_couplage} (type direction , type liste_, type clefs_, type points , type nom_, type appli , type nom_, type unit , type coor_, type x , type coor_, type y , type offset) {
08438 # affiche une rangée de points de couplage sous forme de petits ronds
08439 #
08440 # Si points de couplage en entrée : il sont au sommet de la boite
08441 # Si points de couplage en sortie : il sont au fond   de la boite
08442 #
08443 # Paramètres d'entrée :
08444 #   - direction :           "in" ou "out"
08445 #   - liste_clefs_points :  liste des points de couplage a représenter 
08446 #   - nom_appli :           nom de l'application ou "///file///", si c'est un fichier
08447 #   - nom_unit :            nom du composant ou identifiant du fichier
08448 #   - coor_x coor_y :       coordonnées de l'unité (composant ou fichier) 
08449 #   - offset :              offset horizontal du premier petit rond par rapport au milieu de la boite
08450 #
08451     global DRAW
08452     global dico_applis dico_caract_fichiers dico_caract_points
08453     global coord_points_X coord_points_Y
08454 
08455     # wnd : le canevas
08456     set wnd $DRAW(window)
08457     set ray $DRAW(ray)
08458     set info .gui.pr.tools.infolabel
08459 
08460     if { $direction == "in" } {
08461         set y1 [expr int([expr $coor_y + $DRAW(hcont)/2])]
08462         # Un point pas connecté apparait plus haut
08463         set offset_point_pas_connecte -10
08464         set endpoint_type target
08465     } else {
08466         set y1 [expr int($coor_y + $DRAW(heigthunit) + 3*$DRAW(hcont)/2)]
08467         # Un point pas connecté apparait plus bas
08468         set offset_point_pas_connecte 10
08469         set endpoint_type source
08470     }
08471         
08472     # Pour tous les points de couplage
08473     set j 0
08474     foreach clef_point $liste_clefs_points {
08475         set x1 [expr int($coor_x+ ($j+.5)*$DRAW(widthcombox) - $offset)]
08476         set coord_points_X($clef_point) [expr $x1 - $coor_x]
08477         set coord_points_Y($clef_point) [expr $y1 - $coor_y]
08478                 
08479         # Si c'est un fichier NetCDF
08480         if { $nom_appli == "///file///" } {
08481             # Identifiant du fichier
08482             set id_fichier $nom_unit
08483             # Identifiant du champ
08484             set id_champ $dico_caract_points($clef_point.champ)
08485             # Nom du champ
08486             if { $direction == "in" } {
08487                 # C'est un fichier de sortie : normalement $id_champ vaut "field"
08488                 set nom_champ $dico_caract_fichiers($id_fichier.$id_champ.name)
08489             } else {
08490                 # C'est un fichier d'entrée (entrée de données pour un composant)
08491                 # L'identifiant est le nom
08492                 set nom_champ $id_champ
08493             }
08494             # info bulle
08495             set txt "{Field:    $nom_champ"
08496 
08497             # Pour tous les parametres du champ, ajoute à l'info bulle
08498             foreach {name param} {Datatype type Packing packing Scaling scaling Adding adding "Fill value" fill_value} {
08499                 if { [info exists dico_caract_fichiers($id_fichier.$id_champ.$param)] } {
08500                     set value $dico_caract_fichiers($id_fichier.$id_champ.$param)
08501                     if { $value != "" } {
08502                         set txt "$txt\n$name:   $value"
08503                     }
08504                 }
08505             }
08506             # Ajoute une parenthese fermante
08507             set txt "$txt}"
08508 
08509         # c'est un composant d'une application
08510         } else {
08511             # Nom du composant
08512             set nom_composant $nom_unit
08513             # Nom du champ
08514             set nom_champ $dico_caract_points($clef_point.champ)
08515             # Clef d'accès aux caractéristiques du champ
08516             set clef_champ "app.$nom_appli.comp.$nom_composant.champ.$nom_champ"
08517             # Nom long du champ, si présent
08518             set nom_long ""
08519             if { [info exists dico_applis($clef_champ.long_name)] } {
08520                 set nom_long $dico_applis($clef_champ.long_name)
08521             }
08522             # Type numérique
08523             set type_num $dico_applis($clef_champ.datatype)
08524             # Type dimensionel du champ (single ou vector)
08525             set type_dim $dico_applis($clef_champ.type)
08526             
08527             # info bulle
08528             set txt "{Field:    $nom_champ\nType:     $type_dim\nDatatype:   $type_num\nLong name:  $nom_long}"
08529         }
08530 
08531         # Liste des connexions à ce point de couplage
08532         set liste_cnx ""
08533         if { [info exists dico_caract_points($clef_point.lcnx)] } {
08534             set liste_cnx $dico_caract_points($clef_point.lcnx)
08535         }
08536         
08537         set coloro \#eeee55
08538         set bordcolor black
08539 
08540         # Si le point n'est pas connecté
08541         if { $liste_cnx == "" } {
08542             set y2 [expr $y1 + $offset_point_pas_connecte]
08543         } else {
08544             set y2 $y1
08545         }
08546         set item [$wnd create oval [expr $x1-$ray] [expr $y2 -$ray] [expr $x1+$ray] [expr $y2+$ray] -fill $coloro -outline $bordcolor]
08547         $wnd addtag UNIT_tag($nom_appli.$nom_unit) withtag $item
08548         $wnd addtag UNIT_tag(all) withtag $item
08549 
08550         $wnd bind  $item <Any-Enter> "help_object %W $txt label %X %Y \; $info configure -text {Click to set the $endpoint_type of a new connection }"
08551         $wnd bind  $item <Button-1> "comm_new $direction $clef_point"
08552         $wnd bind  $item <Any-Leave> "help_object_hide;$info configure -text {}"
08553         incr j 1
08554     }
08555 }
08556 
08557 ret  {Draw_set_init_variables} (optional default =no_default) {
08558 #================================================================
08559 #=== set des variables pour le dessin (valeurs par default)
08560 #================================================================
08561    global DRAW palm_param
08562 
08563    set DRAW(init) 1
08564    set DRAW(window) .gui.pr.cpd22.03
08565    set DRAW(widthminunit) 60
08566 
08567    if {$default == "default"} {
08568       set oasis_GUI_param(MAXIMUN_LINE_SIZE) 1000
08569       set oasis_GUI_param(GROUPED_MAXIMUN_LINE_SIZE) 1000
08570       set oasis_GUI_param(PLUGS_RADIUS) 4
08571       set oasis_GUI_param(HEIGHT_UNIT) 25
08572    }
08573    set DRAW(maxi_line_size) 1000
08574    set DRAW(grouped_maxi_line_size) 1000
08575    set DRAW(heigthunit)   25
08576    set DRAW(heightfile)   50
08577    set DRAW(ray)          4
08578 
08579 
08580    catch {set DRAW(maxi_line_size) $oasis_GUI_param(MAXIMUN_LINE_SIZE)}
08581    catch {set DRAW(grouped_maxi_line_size) $oasis_GUI_param(GROUPED_MAXIMUN_LINE_SIZE)}
08582    catch {set DRAW(heigthunit) $oasis_GUI_param(HEIGHT_UNIT)}
08583    catch {set DRAW(ray) $oasis_GUI_param(PLUGS_RADIUS)}
08584 
08585    set DRAW(widthcombox)  [expr $DRAW(ray)*2+1]
08586    set DRAW(hcont)        [expr $DRAW(ray)*2+2]
08587 }
08588 
08589 ret  {Draw_settings} () {
08590 # boite de dialogue pour gerer les parametres du dessin dans le
08591 # cannevas
08592 
08593 
08594     proc resize_draw {w} {
08595     global DRAW oasis_GUI_param
08596 
08597     set oasis_GUI_param(MAXIMUN_LINE_SIZE)         $DRAW(maxi_line_size)
08598     set oasis_GUI_param(GROUPED_MAXIMUN_LINE_SIZE) $DRAW(grouped_maxi_line_size)
08599     set oasis_GUI_param(PLUGS_RADIUS)             $DRAW(ray)
08600     set oasis_GUI_param(HEIGHT_UNIT)               $DRAW(heigthunit)
08601     set DRAW(widthcombox)  [expr $DRAW(ray)*2+1]
08602     set DRAW(hcont)        [expr $DRAW(ray)*2+2]
08603     drawall
08604     destroy $w
08605     }
08606 
08607     global DRAW
08608     global global_font
08609 
08610 
08611     set w .setting
08612     catch {destroy $w}
08613     toplevel $w
08614     wm title $w "Canvas settings"
08615     wm iconname $w "settings"
08616     window_position $w 200 200
08617 
08618 
08619 
08620     frame $w.fr1
08621     pack $w.fr1 -side bottom -fill x -pady 2m -padx 2m
08622     button $w.fr1.ok -font $global_font -text ok -command "resize_draw $w"
08623     pack $w.fr1.ok  -side left -expand 1
08624     button $w.fr1.defaut -font $global_font -text Default -command "global DRAW; catch {unset DRAW(init)};Draw_set_init_variables default; destroy $w; drawall"
08625     pack $w.fr1.defaut  -side left -expand 1
08626     frame $w.sep -width 100 -height 2 -borderwidth 1 -relief sunken
08627     pack $w.sep -side bottom
08628 
08629     frame $w.fr7 -borderwidth 1 -relief sunken
08630     pack $w.fr7  -side top -expand 1 -pady 10 -padx 10
08631 
08632     grid [label $w.fr7.v1 -font $global_font -text "Maximun line size for communications:"] -row 0 -column 0 -sticky w
08633     grid [scale $w.fr7.e1 -variable DRAW(maxi_line_size) -font $global_font -orient horizontal -from 0 -to 2000]   -row 0 -column 1 -sticky w
08634     grid [label $w.fr7.v5 -font $global_font -text "Maximun line size for grouped communications:"] -row 1 -column 0 -sticky w
08635     grid [scale $w.fr7.e5 -variable DRAW(grouped_maxi_line_size) -font $global_font -orient horizontal -from 0 -to 2000]   -row 1 -column 1 -sticky w
08636     grid [label $w.fr7.v2 -font $global_font -text "Radius of plugs:"] -row 2 -column 0 -sticky w
08637     grid [scale $w.fr7.e2 -variable DRAW(ray) -font $global_font -orient horizontal -from 2 -to 6]   -row 2 -column 1 -sticky w
08638     grid [label $w.fr7.v3 -font $global_font -text "Unit height:"] -row 3 -column 0 -sticky w
08639     grid [scale $w.fr7.e3 -variable DRAW(heigthunit) -font $global_font -orient horizontal -from 25 -to 60]   -row 3 -column 1 -sticky w
08640 }
08641 
08642 ret  {HOSTOS} () {
08643 # retourne l'operating systeme
08644     # sert pour le lancement de la bonne version de steplang
08645     # selon la machine sur laquelle tourne PrePALM
08646 
08647     set machine [string toupper [exec uname -s]]
08648     switch $machine {
08649         SunOS  {return "-sun"}
08650         IRIX64 {return "-sgi"}
08651         Linux  {return "-i386"}
08652     }
08653     puts  "Unknown architecture $machine"
08654     exit
08655 }
08656 
08657 ret  {Help_legend} () {
08658 # affiche la legende du canevas de Prepalm
08659     # contenue dan le fichier legend.gif
08660 
08661     if {![winfo exists .help_legend]} {
08662     set w .help_legend
08663     toplevel $w -background white
08664     wm title $w "Legend"
08665     wm iconname $w "Legend"
08666 
08667     frame $w.buttons
08668     pack $w.buttons -side bottom -fill x -pady 0
08669     button $w.buttons.dismiss -text Ok -command "destroy $w"
08670     pack $w.buttons.dismiss
08671     frame $w.sep -width 100 -height 2 -borderwidth 1 -relief sunken
08672     pack $w.sep -side bottom -fill x -pady 0
08673 
08674     set win $w.frsc
08675     frame $win
08676     scrollbar $win.sbar -command "$win.vport yview"
08677     pack $win.sbar -side right -fill y
08678     canvas $win.vport -yscrollcommand "$win.sbar set" -background white -width 590 -height 750 -scrollregion {0 0 0 750}
08679     pack $win.vport -side left -fill both -expand true
08680     pack $win -expand yes -fill both -side top
08681     } else {
08682     set w .help_legend
08683     wm deiconify $w
08684     raise $w
08685     set win $w.frsc
08686     $win.vport delete all
08687     }
08688     set font1 "helvetica 14 bold"
08689     set font2 "helvetica 11 bold"
08690     set font3 "helvetica 12"
08691     global env
08692     global OASIS_GUI_DIR
08693 
08694     # creation image et insertion dans le canvas
08695     set w $win.vport
08696 
08697 
08698     image create photo im1 -file [file join $OASIS_GUI_DIR IMAGES legend.gif]
08699     $w create image 295 375   -image im1
08700 }
08701 
08702 ret  {MOD} (type i , type j) {
08703 # retourne le modulo de deux entiers
08704 set mod [expr $i - $j*(int($i/$j))]
08705 return $mod
08706 }
08707 
08708 ret  {NetCDF_file_add_new} (optional filename ={)} {
08709 #
08710     global liste_fichiers dico_caract_fichiers
08711     global liste_points_i dico_caract_points
08712     global dico_units
08713 
08714     # Ferme la boite de dialogue précédente
08715     destroy .load_dir
08716     
08717     # Détermine un identifiant automatique pour ce nouveau fichier
08718     set file_id [NetCDF_file_cree_nouvel_id]
08719     set clef_unit "///file///.$file_id"
08720             
08721     # Choisit une couleur pour le composant
08722     set dico_units($clef_unit.colour) [color_rgb [expr int(rand()*254)] [expr int(rand()*254)] [expr int(rand()*254)]]
08723     # Permet la modification de ces attributs        
08724     NetCDF_file_edit $file_id $filename
08725     tkwait window .mod_file
08726 
08727     # Si l'utilisateur a validé la saisie
08728     if { [info exists dico_caract_fichiers($file_id.field.name)] } {
08729         /*  Met a jour la liste des points de couplage :*/
08730         /*  le fichier a un seul point de couplage qui est en entrée du fichier*/
08731          clef = _point $clef_unit.field.i
08732         lappend liste_points_i $clef_point
08733          dico = _caract_points($clef_point.appli) "///file///"
08734          dico = _caract_points($clef_point.comp) $file_id
08735          dico = _caract_points($clef_point.champ) field
08736                         
08737         /*  Tant qu'il n'est pas connecté, le champ a un type numérique inconnu*/
08738          dico = _caract_fichiers($file_id.field.type) undefined
08739         /*  Initialise la position du fichier sur le canvas*/
08740          dico = _units($clef_unit) $file_id
08741          dico = _units($clef_unit.coor_x) 80
08742          dico = _units($clef_unit.coor_y) 25
08743         /*  Met le composant dans l'état "connexions visibles"*/
08744          dico = _units($clef_unit.expand) 1
08745 
08746         /*  Dessine le fichier */
08747         Draw_one_unit $clef_unit
08748 
08749         /*  Affiche la liste des fichiers en sélectionnant le dernier créé*/
08750         /*  et annule la sélection des points de couplage*/
08751         minimenu_unit $clef_unit
08752     }
08753 }
08754 
08755 ret  {NetCDF_file_cree_nouvel_id} () {
08756 # Détermine automatiquement et retourne un identifiant 
08757 # pour un nouveau fichier NetCDF en output
08758     
08759     global liste_fichiers
08760     # Liste actuelle des identifiants de fichiers en output
08761     set liste_fic_output [lsearch -glob -all -inline $liste_fichiers "///outNetCDFfile///_*"]
08762     if { $liste_fic_output != "" } {
08763         # Numero du dernier identifiant
08764         set dernier [lindex $liste_fic_output end]
08765         set dernier_num [string range $dernier [string length ///outNetCDFfile///_] end]
08766         # Nouvel identifiant
08767         incr dernier_num
08768     } else {
08769         set dernier_num 1
08770     }
08771     return "///outNetCDFfile///_$dernier_num"
08772 }
08773 
08774 ret  {NetCDF_file_delete} (type clef_, type fichier) {
08775 # Suppression d'un fichier par appui sur la bouton <DELETE>
08776     global liste_fichiers dico_caract_fichiers dico_units
08777     global liste_points_i   liste_points_o  dico_caract_points
08778     global dico_cnx_out dico_caract_cnx
08779     global DRAW tagselected
08780 
08781     # Identifiant du fichier
08782     set file_id $dico_units($clef_fichier)
08783     set clef_fichier ///file///.$file_id
08784     
08785     # Supprime toutes les connexions vers ce fichier
08786     # ----------------------------------------------
08787      
08788     # Si le fichier est en "output"
08789     if { $dico_caract_fichiers($file_id.dir) == "output" } {
08790         # Point de couplage du champ unique du fichier   
08791         set clef_point $clef_fichier.field.i
08792         # Teste si le champ est connecté
08793         set cnx ""
08794         catch {set cnx $dico_caract_points($clef_point.lcnx)}
08795 
08796         # Si le champ est connecté
08797         if { $cnx != ""} {
08798             # Supprime la connexion
08799             comm_remove $clef_point
08800         }
08801         
08802     # Le fichier est en "input"    
08803     } else {
08804         # Pour tous les champs du fichier
08805         foreach field_id $dico_caract_fichiers($file_id.lchamps) {
08806             # Identifiant de ce point de couplage
08807             set clef_point $clef_fichier.$field_id
08808             # Teste si le champ est connecté
08809             set lcnx ""
08810             catch {set lcnx $dico_caract_points($clef_point.o.lcnx)}
08811             # Pour toutes les connexions sortant de ce point
08812             foreach num_cnx $lcnx {
08813                 # Clef de la cnx sortante
08814                 set clef_cnx_out $clef_point.$num_cnx
08815                 # Clef de la connexion : point de couplage cible de la connexion
08816                 set clef_cnx_in $dico_cnx_out($clef_cnx_out)
08817                 # Supprime la connexion
08818                 comm_remove $clef_cnx_in
08819             }
08820         }
08821     }
08822     
08823     # Supprime tous les points de couplage de ce fichier
08824     # --------------------------------------------------
08825     
08826     # Points en entrée
08827     set liste_points_i [lsearch -not -all -inline -glob $liste_points_i "$clef_fichier.*"]
08828     # Points en sortie
08829     set liste_points_o [lsearch -not -all -inline -glob $liste_points_o "$clef_fichier.*"]
08830     # Caractéristiques des points
08831     array unset dico_caract_points "$clef_fichier.*"
08832     
08833     # Supprime le fichier de la liste
08834     set liste_fichiers [lsearch -not -all -inline $liste_fichiers $file_id]
08835     # Supprime toutes ses caractéristiques
08836     array unset dico_caract_fichiers "$file_id.*"
08837 
08838     # Raffraichit l'écran
08839     # -------------------
08840     
08841     # Efface et redessine tous les composants
08842     Draw_drawunit
08843     # Efface et redessine toutes les connexions
08844     Draw_comm all
08845 
08846     # Affiche la liste des fichiers
08847     control_entity FILE
08848     
08849     # Si ce fichier était sélectionné (a priori OUI)
08850     if {$tagselected == "unitpoint_color($clef_fichier)"} {
08851         # Oublie l'objet sélectionné
08852         set tagselected ""
08853     }
08854 }
08855 
08856 ret  {NetCDF_file_edit} (type file_, type id , optional new_filename ={)} {
08857 #
08858     global liste_fichiers dico_caract_fichiers dico_units
08859 
08860     # Extrait les informations pertinentes
08861     # ------------------------------------
08862 
08863     # Si le fichier n'existe pas encore
08864     if { ! [info exists dico_caract_fichiers($file_id.dir)] } {
08865          io = _direction output
08866          access = _mode iosingle
08867          suffix =  no
08868          fieldname =  ""
08869         /*  On récupère le nom de fichier de la boite de dialogue précédente*/
08870          filename =  $new_filename
08871     
08872     } else {
08873          io = _direction $dico_caract_fichiers($file_id.dir)
08874          access = _mode ""
08875         catch { access = _mode $dico_caract_fichiers($file_id.io_mode)}
08876          suffix =  ""
08877         catch { suffix =  $dico_caract_fichiers($file_id.suffix) }
08878          fieldname =  ""
08879         catch { fieldname =  $dico_caract_fichiers($file_id.field.name)}
08880          filename =  $dico_caract_fichiers($file_id.name)
08881         
08882          time = _unit ""
08883         catch { time = _unit $dico_caract_fichiers($file_id.time_unit)}
08884          time = _values "unknown"
08885         catch { time = _values $dico_caract_fichiers($file_id.time_values)}
08886         if { $suffix == "true" } { suffix =  yes} else { suffix =  no}
08887     }
08888 
08889     /*  Cree une boite de dialogue*/
08890     /*  --------------------------*/
08891 
08892      u =  .mod_file
08893     catch {destroy $u}
08894     toplevel $u
08895     wm title $u "Edit NetCDF file"
08896     wm iconname $u "Mod_NetCDF"
08897     window_position $u 20 20
08898 
08899      w =  $u.menu
08900     frame $w
08901     grid $w -row 2 -column 0
08902     grid configure $w -columnspan 2 -padx 4 -pady 4
08903 
08904     frame $w.buttons
08905     pack $w.buttons -side bottom -fill x -pady 2m
08906 
08907     button $w.buttons.dismiss  -text Cancel -command "destroy $u"
08908     button $w.buttons.ok  -text Ok -command "NetCDF_file_update $u $file_id $io_direction"
08909 
08910     pack $w.buttons.dismiss -side left -expand 1 -padx 10
08911     pack $w.buttons.ok -side left -expand 1 -padx 10
08912 
08913     frame $w.sep1 -width 200 -height 2 -borderwidth 1 -relief sunken
08914     pack $w.sep1 -side bottom -fill x -pady 2m -expand 1
08915 
08916      w =  $u.char
08917     frame $w
08918     grid $w -row 0 -column 1
08919     grid configure $w -padx 4 -pady 4
08920      r =  1
08921 
08922 /* == name*/
08923     label $w.label1 -text "Name :"
08924     global filename_m
08925      filename = _m $filename
08926     entry $w.entry1 -textvariable filename_m -width 40 -background white
08927     grid  $w.label1 $w.entry1 -row $r -sticky w
08928     incr r
08929     /*  Ajoute une bulle d'aide pour 'name'*/
08930      help = _text {Enter here a file name, without suffix, if any.}
08931     entry_help_balloon $w.entry1 $help_text
08932 
08933 /* == I/O direction*/
08934     label $w.label12 -text "I/O direction :"
08935     label $w.label13 -text $io_direction -relief sunken
08936     grid  $w.label12 $w.label13 -row $r -sticky w
08937     incr r
08938 
08939 /* == access mode*/
08940     label $w.label2 -text "Access mode :"
08941     combobox_lim $w.cmb2 {iosingle distributed parallel} {} access_mode_m 30 $access_mode
08942     grid  $w.label2 $w.cmb2 -row $r -sticky w
08943     incr r
08944     /*  Ajoute une bulle d'aide pour 'access mode'*/
08945      help = _text {The mode iosingle means that the whole file is written or read only by the master ret ess;
08946 
08947 distributed means that each process writes or reads its part of the field to a different partial file. Note that if OASIS is linked against the parallel NetCDF library (7), the parallel mode will automatically be used; in this case each process writes its part of the field to one parallel file (see Oasis User Guide on page 26).}
08948     entry_help_balloon $w.cmb2 $help_text
08949 
08950 #== Suffix option
08951     label $w.label3 -text "Suffix :"
08952     combobox_lim $w.cmb3 (type yes , type no) {} suffix_m 30 $suffix
08953     grid  $w.label3 $w.cmb3 -row $r -sticky w
08954     incr r
08955     /*  Ajoute une bulle d'aide pour 'suffix'*/
08956      help = _text {If yes, a suffix is appended to file name.
08957     
08958 When the file is opened for writing, the suffix will be “ out.<job-startdate>.nc”, where <job-startdate> is the start date of the job.
08959     
08960 When the file is opened for reading, the suffix should be “ in.<start-date>.nc”, where <start-date> is the date of the first time stamp in that file.
08961 When reading an input from a file, OASIS will automatically match the requested date of the input with the appropiate file if it falls into the time interval covered by that file. The <job-startdate> and <start-date> must be written according to the ISO format yyyy-mm-ddTHH:MM:SS. The date/time string in the file name must have to format yyyy-mmddTHH.MM.SS since the colon is already used in other context for file systems.}
08962     entry_help_balloon $w.cmb3 $help_text
08963 
08964     grid [ frame $w.sep1 -width 430 -height 2 -borderwidth 1 -relief sunken] -row $r -column 0 -pady 5 -columnspan 2
08965     incr r
08966     
08967     /*  Si fichier en sortie*/
08968     if { $io_direction == "output" } {
08969     
08970     /* == field name*/
08971         label $w.label4 -text "Field name :" -fg /* ee3333*/
08972         global fieldname_m
08973          fieldname = _m $fieldname
08974         entry $w.entry4 -textvariable fieldname_m -width 40 -background white
08975         grid  $w.label4 $w.entry4 -row $r -sticky w
08976         incr r
08977         /*  Ajoute une bulle d'aide pour 'field name'*/
08978          help = _text {Enter here the name of NetCDF file variable.}
08979         entry_help_balloon $w.entry4 $help_text
08980     } else {
08981         /*  Fichier en entrée*/
08982         
08983     /* == Time axis*/
08984         label $w.label31 -text "Time values :"
08985         label $w.label32 -text "$time_values $time_unit" -relief sunken -wraplength 450 -justify left
08986         grid  $w.label31 $w.label32 -row $r -sticky w
08987         incr r    
08988     }
08989 
08990 /* == couleur*/
08991     global color_m
08992      clef = _comp ///file///.$file_id
08993      color = _m $dico_units($clef_comp.colour)
08994     label $w.label5 -text "Color :"
08995     button $w.entry5 -relief sunken -width 20 -text $color_m -background $color_m  -command {
08996         global color_m
08997          color = _m [tk_chooseColor -initialcolor $color_m]
08998         catch {.mod_file.char.entry5 configure -background $color_m}
08999         }
09000     grid  $w.label5 $w.entry5 -row $r -sticky w
09001     /*  Ajoute une bulle d'aide pour la couleur*/
09002     global no_help_balloon
09003     if { $no_help_balloon == 0 } {
09004          help = _text {Click here to change the color of the component.}
09005          help = _action "help_object %W [list $help_text] message"
09006         bind $w.entry5 <Any-Enter> $help_action 
09007         bind $w.entry5 <FocusIn>   $help_action
09008         bind $w.entry5 <Any-Leave>    "help_object_hide"
09009         bind $w.entry5 <FocusOut>     "help_object_hide"
09010         bind $w.entry5 <ButtonPress>  "help_object_hide"
09011     }
09012 }
09013 
09014 ret  {NetCDF_file_maj} (type file_, type list , type nom_, type dico_, type ajout , type option_, type redraw) {
09015 # Met à jour les données en mémoire avec les fichiers ajoutés : 
09016 #  en particulierm la liste des points de couplage
09017 # Ajoute les fichiers sur le graphe
09018 #
09019 # Tient compte du fait que des fichiers sont en fait chargés pour la deuxième fois en mémoire.
09020 # Les fichiers ne sont alors pas ajoutés mais remis à jour.
09021 # Dans ce cas, on supprime les points de couplage qui n'existent plus et les éventuelles connexions
09022 # qui leur sont attachées.
09023 #
09024 
09025     upvar 1 $nom_dico_ajout dico_ajout
09026     global liste_fichiers dico_caract_fichiers dico_units
09027     global liste_points_o dico_cnx_out dico_caract_points dico_caract_cnx
09028     
09029     # Position initiale du fichier ajouté sur le graphe : en haut à gauche
09030     set position_x 80
09031     set position_y 25
09032 
09033     foreach nom_fichier $file_list {
09034         set clef_comp ///file///.$nom_fichier
09035         
09036         # Si fichier en entrée dont l'attribut 'suffix' n'est pas encore défini
09037         if {$dico_ajout($nom_fichier.dir) == "input" && 
09038             ![info exists dico_ajout($nom_fichier.suffix)]} {
09039             
09040             # Si le nom du fichier comprend un suffixe _out<date>.nc
09041             if {[regexp -nocase {_in\.[1-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9][._][0-9][0-9][._][0-9][0-9].nc$} $nom_fichier]} {
09042                 set dico_ajout($nom_fichier.suffix) true
09043                 # Garde le nom sans le suffixe
09044                 set dico_ajout($nom_fichier.name) [string range $dico_ajout($nom_fichier.name) 0 end-26]
09045             } else {
09046                 set dico_ajout($nom_fichier.suffix) false
09047             }
09048         }
09049 
09050         # Si le fichier n'existait pas déja en mémoire (il a bien été ajouté)
09051         if { [lsearch $liste_fichiers $nom_fichier] == -1 } {
09052             
09053             # Ajoute le fichier a la liste
09054             lappend liste_fichiers $nom_fichier
09055             # Ajoute toutes ses infos au dictionnaire global des fichiers
09056             array set dico_caract_fichiers [array get dico_ajout "$nom_fichier.*"]
09057 
09058             # Nouvelle unité sur le graphe de couplage
09059             set dico_units($clef_comp) $nom_fichier
09060             # Détermine les coordonnées du fichier sur le graphe
09061             set dico_units($clef_comp.coor_x) $position_x
09062             set dico_units($clef_comp.coor_y) $position_y
09063             incr position_y 80
09064             # Met le composant dans l'état "connexions visibles"
09065             set dico_units($clef_comp.expand) 1
09066             # Choisit une couleur pour le composant
09067             set dico_units($clef_comp.colour) [color_rgb [expr int(rand()*254)] [expr int(rand()*254)] [expr int(rand()*254)]]
09068             
09069             # Pour toutes ses variables
09070             foreach nom_var $dico_ajout($nom_fichier.lchamps) {
09071                 # Ajoute un point de couplage
09072                 set clef_point $clef_comp.$nom_var.o
09073                 lappend liste_points_o $clef_point
09074                 
09075                 set dico_caract_points($clef_point.appli) ///file///
09076                 set dico_caract_points($clef_point.comp) $nom_fichier
09077                 set dico_caract_points($clef_point.champ) $nom_var
09078             }
09079             # Affiche le fichier sur le graphe
09080             Draw_one_unit $clef_comp
09081         
09082         } else {    # Le fichier a été rechargé avec des changements possibles
09083             
09084             # Mémorise l'axe des temps
09085             if {$dico_ajout($nom_fichier.time_name) != ""} {
09086                 array set dico_caract_fichiers [array get dico_ajout "$nom_fichier.time_*"]
09087             } else {
09088                 array unset dico_caract_fichiers $nom_fichier.time_unit
09089                 array unset dico_caract_fichiers $nom_fichier.time_values
09090             }
09091             
09092             # Vérifie la liste des champs et cherche des champs supprimés ou ajoutés
09093             # ----------------------------------------------------------------------
09094             
09095             # Pour toutes les variables du fichier chargé en mémoire
09096             foreach nom_var $dico_ajout($nom_fichier.lchamps) {
09097                 # Si la variable n'existait pas auparavant
09098                 if { [lsearch $dico_caract_fichiers($nom_fichier.lchamps) $nom_var] == -1} {
09099                     # Ajoute un point de couplage
09100                     set clef_point $clef_comp.$nom_var.o
09101                     lappend liste_points_o $clef_point
09102                     
09103                     set dico_caract_points($clef_point.appli) ///file///
09104                     set dico_caract_points($clef_point.comp) $nom_fichier
09105                     set dico_caract_points($clef_point.champ) $nom_var
09106                 }
09107                 # Met a jour en mémoire les attributs de cette var. du fichier
09108                 array set dico_caract_fichiers [array get dico_ajout "$nom_fichier.$nom_var.*"]
09109             }
09110             
09111             # Init liste des composants touchés par les conexions supprimées
09112             set liste_comp_touches {}
09113             # Pour toutes les variables qui existaient auparavant
09114             foreach nom_var $dico_caract_fichiers($nom_fichier.lchamps) {
09115                 # Si la variable n'existe plus
09116                 if { [lsearch $dico_ajout($nom_fichier.lchamps) $nom_var] == -1} {
09117                     # Identifiant de ce point de couplage
09118                     set clef_point $clef_comp.$nom_var
09119                     # Teste si le champ est connecté
09120                     set lcnx ""
09121                     catch {set lcnx $dico_caract_points($clef_point.o.lcnx)}
09122                     # Pour toutes les connexions sortant de ce point
09123                     foreach num_cnx $lcnx {
09124                         # Clef de la cnx sortante
09125                         set clef_cnx_out $clef_point.$num_cnx
09126                         # Clef de la connexion : point de couplage cible de la connexion
09127                         set clef_cnx_in $dico_cnx_out($clef_cnx_out)
09128                         # Memorise le composant cible de la connexion
09129                         set appli_cible $dico_caract_cnx($clef_cnx_in.app_cible)
09130                         set comp_cible  $dico_caract_cnx($clef_cnx_in.comp_cible)
09131                         lappend liste_comp_touches $appli_cible.$comp_cible
09132                         # Supprime la connexion
09133                         comm_remove $clef_cnx_in
09134                     }
09135 
09136                     # Met a jour la liste des points en sortie
09137                     set liste_points_o [lsearch -not -all -inline -glob $liste_points_o "$clef_point.o"]
09138                     # Supprime les caractéristiques du point
09139                     array unset dico_caract_points "$clef_point.o.*"
09140                     # Supprime les infos concernant ce champ
09141                     array unset dico_caract_fichiers "$nom_fichier.$nom_var.*"
09142                 }
09143             }
09144             
09145             # Met a jour en mémoire la liste des champs du fichier
09146             set dico_caract_fichiers($nom_fichier.lchamps) $dico_ajout($nom_fichier.lchamps)
09147 
09148             if {$option_redraw} {
09149                 # Pour tous les composants touchés
09150                 foreach composant $liste_comp_touches {
09151                     # Redessine le composant
09152                     Draw_one_unit $composant
09153                 }
09154     
09155                 # Affiche le fichier sur le graphe
09156                 Draw_one_unit $clef_comp
09157                 # Redessine les connexions du fichier
09158                 Draw_comm $clef_comp
09159             }
09160         }
09161     }
09162 }
09163 
09164 ret  {NetCDF_file_read} (type nom_, type dico_, type ajout , type nom_, type fichier) {
09165 # Lit les caracteristiques d'un fichier NetCDF
09166 # et les enregistre dans le dictionnaire "nom_dico_ajout"
09167 #
09168 # Retourne le nom court du fichier si ce fichier a été lu correctement
09169 #
09170     upvar 1 $nom_dico_ajout dico_ajout
09171 
09172     # Ecriture de l'en-tete du fichier netCDF dans un autre ficher
09173     exec ncdump -h $nom_fichier > ".dmp"
09174     
09175     
09176     # Lecture et stockage des informations de l'en-tete
09177     #--------------------------------------------------
09178     
09179     set status "rien"
09180     
09181     if { [file exists .dmp] } {
09182         set f [open .dmp r]
09183         
09184         # Mémorise le fichier sans son chemin
09185         set nom_fichier [file tail $nom_fichier]
09186         set dico_ajout($nom_fichier.dir) input
09187         # Le nom du fichier est editable : par défaut, c'est celui du fichier lu
09188         set dico_ajout($nom_fichier.name) $nom_fichier
09189         # Initialise certains attributs du fichier
09190         set dico_ajout($nom_fichier.io_mode) iosingle
09191 
09192         set time_dim_var ""
09193         set unlimited_dim ""
09194         while {![eof $f]} {
09195             gets $f line
09196             set line [string trim $line]
09197             # On regarde dans quelle partie declarative on se trouve
09198             if {$line == "dimensions:"} {
09199                 set status "on_dim"
09200                 continue
09201             }
09202             if {$line == "variables:"} {
09203                 set status "on_var"
09204                 continue
09205             }
09206             if {$line == "// global attributes:"} {
09207                 set status "on_ga"
09208                 continue
09209             }
09210             # On collecte les donnees en fonction du statut courant
09211             # GESTION DES DIMENSIONS
09212             if {$status == "on_dim"} {
09213                 set dim [lindex $line 0]
09214                 # Recherche la dimension "temps" : par son nom (time)
09215                 if {[string toupper $dim] == "TIME"} {
09216                     set time_dim_var $dim
09217                 }
09218                 # Recherche la dimension dont la taille n'est pas fixe
09219                 set valeur [lindex $line 2]
09220                 if {$valeur == "UNLIMITED"} {
09221                     # Memorise la variable
09222                     set unlimited_dim $dim
09223                 }
09224                 continue
09225             }    
09226             # GESTION DES VARIABLES
09227             if {$status == "on_var"} {
09228                 set item1 [lindex $line 0]
09229                 # gestion des attributs des variables 
09230                 if { $item1 == "double" || $item1 == "float" || $item1 == "int" || $item1 == "short" || $item1 == "char" || $item1 == "byte"} {
09231                     set type_var $item1
09232                     set debut_nom_var [string length $type_var]
09233                     incr debut_nom_var
09234                     set par_ouv [string first "(" $line]
09235                     set par_fer [string first ")" $line]
09236                     set nom_var [string range $line $debut_nom_var [expr $par_ouv -1 ]] 
09237                     set dim_var [string range $line [expr $par_ouv+1] [expr $par_fer-1]]
09238                     
09239                     # Si la variable porte le nom de ses dimensions, c'est que c'est une dimension
09240                     # On ne l'ajoutera pas à la liste
09241                     if { $nom_var != $dim_var } {
09242                         lappend dico_ajout($nom_fichier.lchamps) $nom_var
09243                         set dico_ajout($nom_fichier.$nom_var.type) $type_var
09244                     }
09245                 } else {
09246                     if { $item1 == "$nom_var:_FillValue" } {
09247                         set fill_value [lindex $line 2]
09248                         set dico_ajout($nom_fichier.$nom_var.fill_value) $fill_value
09249                     }
09250                     if { $item1 == "$nom_var:long_name" } {
09251                         set long_name [lindex $line 2]
09252                         set dico_ajout($nom_fichier.$nom_var.long_name) $long_name
09253                     }
09254                 }
09255                 continue
09256             }
09257             # GESTION DES ATTRIBUTS GLOBAUX
09258             if {$status == "on_ga"} {continue}
09259         }
09260 
09261         # On ferme le fichier .dump dont on a lu le nécessaire
09262         close $f
09263         file delete .dmp
09264         
09265         # Si la dimension 'temps' n'est pas connue
09266         if {$time_dim_var == ""} {
09267             # La dimension temps est a priori celle qui est de taille illimitée
09268             set time_dim_var $unlimited_dim
09269             # A ce stade, il se peut que la dimension temps soit encore inconue
09270         }
09271         set dico_ajout($nom_fichier.time_name) $time_dim_var
09272         
09273         return $nom_fichier
09274     } else {
09275         notice_show "'ncdump' not found !\nPlease, install NetCDF support utilities." error
09276         return ""
09277     }
09278 }
09279 
09280 ret  {NetCDF_file_read_time} (type nom_, type dico_, type ajout , type nom_, type fichier , type nom_, type axe_, type temps) {
09281 # Lit les caractéristiques de l'axe des temps d'un fichier NetCDF
09282 # et les enregistre dans le dictionnaire "nom_dico_ajout"
09283 #
09284     upvar 1 $nom_dico_ajout dico_ajout
09285 
09286     # Ecriture de l'en-tête du fichier netCDF suivie des données de la dimension temps
09287     # dans un autre ficher de type texte
09288     exec ncdump -v $nom_axe_temps $nom_fichier > ".dmp"
09289 
09290 
09291     # Lecture et stockage des informations du fichier texte
09292     #------------------------------------------------------
09293     
09294     set status "rien"
09295 
09296     if { [file exists .dmp] } {
09297         set f [open .dmp r]
09298         
09299         # Mémorise le fichier sans son chemin
09300         set nom_fichier [file tail $nom_fichier]
09301         # Lit toutes les lignes du fichier une à une
09302         while {![eof $f]} {
09303             gets $f line
09304             set line [string trim $line]
09305             # On regarde dans quelle partie declarative on se trouve
09306             if {$line == "dimensions:"} {
09307                 set status "on_dim"
09308                 continue
09309             }
09310             if {$line == "variables:"} {
09311                 set status "on_var"
09312                 continue
09313             }
09314             if {$line == "// global attributes:"} {
09315                 set status "on_ga"
09316                 continue
09317             }
09318             if {$line == "data:"} {
09319                 set status "on_data"
09320                 continue
09321             }
09322             # On collecte les donnees en fonction du statut courant
09323             # GESTION DES DIMENSIONS
09324             if {$status == "on_dim"} {continue}
09325             
09326             # GESTION DES VARIABLES
09327             if {$status == "on_var"} {
09328                 set item1 [lindex $line 0]
09329                 # si attribut 'units' de la dimension temps
09330                 if { $item1 == "$nom_axe_temps:units" } {
09331                     set units [lindex $line 2]
09332                     set dico_ajout($nom_fichier.time_unit) $units
09333                 }
09334                 continue
09335             }
09336             # GESTION DES ATTRIBUTS GLOBAUX
09337             if {$status == "on_ga"} {continue}
09338             
09339             # GESTION DES données de l'axe des temps : 1ère ligne
09340             if {$status == "on_data"} {
09341                 set item1 [lindex $line 0]
09342                 # Si première ligne des données de la dimension temps
09343                 if { $item1 == "$nom_axe_temps" } {
09344                     # Lit les premières données
09345                     set liste_valeurs [lrange $line 2 end]
09346                     # Si la ligne finit par un point-virgule
09347                     if {[lindex $liste_valeurs end] == ";"} {
09348                         set status "end"
09349                     } else {
09350                         set status "on_time"
09351                     }
09352                 }
09353                 continue
09354             }
09355             # GESTION DES données de l'axe des temps : lignes suivantes
09356             if {$status == "on_time"} {
09357                 # Lit les données
09358                 set liste_valeurs [concat $liste_valeurs $line]
09359                 # Si la ligne finit par un point-virgule
09360                 if {[lindex $liste_valeurs end] == ";"} {
09361                     set status "end"
09362                 }
09363                 continue
09364             }
09365         }
09366 
09367         # On ferme le fichier .dump dont on a lu le nécessaire
09368         close $f
09369         file delete .dmp
09370         
09371         # Memrise les attributs de l'axe des temps
09372         set dico_ajout($nom_fichier.time_values) [lrange $liste_valeurs 0 end-1]
09373     
09374     } else {
09375         notice_show "'ncdump' not found !\nPlease, install NetCDF support utilities." error
09376     }
09377 }
09378 
09379 ret  {NetCDF_file_read_files} (type file_, type list) {
09380 global tagselected oldcolor
09381     
09382     # Dictionnaire des fichiers ajoutés
09383     array set dico_ajout {}
09384     # Init de la liste des fichiers ajoutés
09385     set liste_fic_ajoutes {}
09386     
09387     foreach file $file_list {
09388         if {$file != ""} {
09389             set fic_ajoute [NetCDF_file_read dico_ajout $file]
09390             if {$fic_ajoute != ""} {
09391                 lappend liste_fic_ajoutes $fic_ajoute
09392                 set nom_fichier [file tail $file]
09393                 # Si le fichier comporte une dimension "temps" clairement reconnaissable
09394                 set nom_axe_temps $dico_ajout($nom_fichier.time_name)
09395                 if {$nom_axe_temps != ""} {
09396                     # Lit les caractéristiques de l'axe des temps
09397                     NetCDF_file_read_time dico_ajout $file $nom_axe_temps
09398                 }
09399             }
09400         }
09401     }
09402     # Met à jour les données et le graphe avec les fichiers ajoutés
09403     NetCDF_file_maj $liste_fic_ajoutes dico_ajout 1
09404 
09405     # Affiche la liste des fichiers
09406     control_entity "FILE"
09407     
09408     # S'il n'y a qu'un seul fichier ajouté
09409     if { [llength $file_list] == 1 } {
09410         set nom_fichier [lindex $file_list 0]
09411         # Permet la modification des attributs de ce fichier
09412         set file_id [file tail $nom_fichier]
09413         NetCDF_file_edit $file_id
09414         tkwait window .mod_file
09415     
09416         # Raffiche le fichier sur le graphe
09417         set clef ///file///.$file_id
09418         Draw_one_unit $clef
09419 
09420         # Si ce fichier était sélectionné
09421         if {$tagselected == "unitpoint_color($clef)"} {
09422             # Change la couleur de l'objet sélectionné
09423             set w .gui.pr.cpd22.03
09424             set oldcolor [$w itemcget $tagselected  -fill ]
09425             $w itemconfigure $tagselected -fill red
09426         }
09427     }
09428 }
09429 
09430 ret  {NetCDF_file_update} (type win , type file_, type id , type io_, type direction) {
09431 # Procedure appelee lors de la validation de la boite de dialogue de saisie des parametres
09432 # d'un fichier NetCDF.
09433 #
09434     # Variables globales
09435     global liste_fichiers dico_caract_fichiers dico_units
09436     # Varaibles globales des champs saisis
09437     global filename_m access_mode_m suffix_m fieldname_m
09438 
09439     # Validation du nom de fichier
09440     if { $filename_m == "" } {
09441         notice_show "File name not filled !" error
09442         focus .mod_file.char.entry1
09443         return error
09444     }
09445 
09446     # Si le fichier est un fichier en sortie
09447     if { $io_direction == "output" } {
09448         # Validation du nom de champ
09449         if { $fieldname_m == "" } {
09450             notice_show "Field name not filled !" error
09451             focus .mod_file.char.entry4
09452             return error
09453         }
09454     }
09455 
09456     # Réinterprète les booléens
09457     if {$suffix_m == "yes"} {set suffix_m true} else {set suffix_m false}
09458 
09459     # Si le fichier n'existe pas encore en mémoire
09460     if { ! [info exists dico_caract_fichiers($file_id.dir)] } {
09461         # Ajoute l'identifiant à la liste
09462         lappend liste_fichiers $file_id
09463         
09464         # Saisit les caracteristiques du nouveau fichier
09465         set dico_caract_fichiers($file_id.dir) output
09466         set dico_caract_fichiers($file_id.lchamps) field
09467     }   
09468 
09469     # Enregistre les attributs du fichier
09470     set dico_caract_fichiers($file_id.suffix) $suffix_m
09471     set dico_caract_fichiers($file_id.io_mode) $access_mode_m
09472     unset suffix_m access_mode_m
09473     # Enregistre le nom du fichier
09474     set dico_caract_fichiers($file_id.name) $filename_m
09475     unset filename_m
09476     
09477     # Si le fichier est un fichier en sortie
09478     if { $io_direction == "output"} {
09479         # Enregistre le nom du champ        
09480         set dico_caract_fichiers($file_id.field.name) $fieldname_m
09481         unset fieldname_m
09482         # Enregistre les attributs du champ NetCDF
09483         # set dico_caract_fichiers($filename_m.$fieldname_m.....) ....
09484     }
09485 
09486     # Met a jour la couleur
09487     global color_m
09488     set clef_comp ///file///.$file_id
09489     set dico_units($clef_comp.colour) $color_m
09490     unset color_m
09491     
09492     destroy $win
09493 }
09494 
09495 ret  {application_edit} (type nom_, type appli) {
09496 ##################################################
09497 # menu d'edition d'une application
09498 ##################################################
09499     global dico_applis
09500     
09501     # Extrait les informations concernant l'application
09502     global exe_name_m args_m redirect_m
09503     
09504     set exe_name_m ""
09505     catch { set exe_name_m $dico_applis(app.$nom_appli.executable_name) }
09506     set args_m ""
09507     catch { set args_m $dico_applis(app.$nom_appli.args) }
09508     set redirect_m "true"
09509     catch { set redirect_m $dico_applis(app.$nom_appli.redirect) }
09510     if { $redirect_m == "true" } {
09511         set redirect_m "yes"
09512     } else {
09513         set redirect_m "no"
09514     }
09515     
09516     #== number of processes
09517     set min_proc $dico_applis(app.$nom_appli.proc.min_value)
09518     set max_proc $dico_applis(app.$nom_appli.proc.max_value)
09519     set increment $dico_applis(app.$nom_appli.proc.increment)
09520     
09521     # Start mode
09522     set start_mode $dico_applis(app.$nom_appli.start_mode)
09523     array set translation_dict { spawn Spawn notspawn {Not spawn} notspawn_or_spawn {Either spawn or not spawn}}
09524     set start_mode $translation_dict($start_mode)
09525     
09526     # Recopie les infos concernant les ordinateurs hosts de l'application
09527     global hosts_m
09528     array set hosts_m {}
09529     if { [info exists dico_applis(app.$nom_appli.lhosts)] && $dico_applis(app.$nom_appli.lhosts) != "" } {
09530         set hosts_m(lhosts) $dico_applis(app.$nom_appli.lhosts)
09531         foreach id_host $dico_applis(app.$nom_appli.lhosts) {
09532             set clef app.$nom_appli.host.$id_host
09533             set hosts_m(host.$id_host.name) $dico_applis($clef.name)
09534             set hosts_m(host.$id_host.nb_procs) $dico_applis($clef.nb_procs)
09535         }    
09536     } else {
09537         # Definit un "host" arbitraire avec N processus
09538         set hosts_m(lhosts) "1"
09539         set hosts_m(host.1.name) "Enter name..."
09540         set hosts_m(host.1.nb_procs) $max_proc
09541         if {$max_proc == 0} {inc hosts_m(host.1.nb_procs)}
09542     }
09543     
09544     # un toplevel
09545     set u .mod_appli
09546     catch {destroy $u}
09547     toplevel $u
09548     wm title $u "Edit Application"
09549     wm iconname $u "Mod_Appli"
09550     window_position $u 100 100
09551     
09552     set w $u.menu
09553     frame $w
09554     grid $w -row 2 -column 0
09555     grid configure $w -columnspan 2 -padx 4 -pady 4
09556 
09557     frame $w.buttons
09558     pack $w.buttons -side bottom -fill x -pady 2m
09559 
09560     button $w.buttons.dismiss  -text Cancel -command "destroy $u"
09561     button $w.buttons.ok  -text Ok -command "application_update $u $nom_appli \$exe_name_m \$args_m \$redirect_m hosts_m"
09562 
09563     pack $w.buttons.dismiss -side left -expand 1 -padx 10
09564     pack $w.buttons.ok -side left -expand 1 -padx 10
09565 
09566     frame $w.sep1 -width 200 -height 2 -borderwidth 1 -relief sunken
09567     pack $w.sep1 -side bottom -fill x -pady 2m -expand 1
09568 
09569     set w $u.char
09570     frame $w
09571     grid $w -row 0 -column 1
09572     grid configure $w -padx 4 -pady 4
09573     set r 1
09574 
09575 #== nom
09576     label $w.label1 -text "Name: "
09577     label $w.label11 -relief sunken -text $nom_appli
09578     grid  $w.label1 $w.label11 -row $r -sticky w
09579     incr r
09580 
09581 #== number of processes
09582     label $w.label2 -text "Number of processes : "
09583     label $w.label21 -relief sunken -text "$min_proc to $max_proc step $increment"
09584     grid  $w.label2 $w.label21 -row $r -sticky w
09585     incr r
09586 
09587 #== start mode
09588     label $w.label20 -text "Allowed start mode : "
09589     label $w.label201 -relief sunken -text "$start_mode"
09590     grid  $w.label20 $w.label201 -row $r -sticky w
09591     incr r
09592 
09593     # Séparateur
09594     frame $w.sep1 -width 200 -height 2 -borderwidth 1 -relief sunken
09595     grid  $w.sep1 -row $r -columnspan 2 -pady 5
09596     incr r
09597 
09598 #== executable
09599     label $w.label3 -text "Executable name : " -fg #ee3333
09600     entry $w.entry3 -textvariable exe_name_m -width 25 -background white
09601     grid  $w.label3 $w.entry3 -row $r -sticky w
09602     incr r
09603 
09604     # Ajoute une bulle d'aide pour 'Executable name'
09605     set help_text {Enter here the application executable name, defined by the compiling environment (used only in spawn mode as argument of the MPI_Comm_Spawn_Multiple).}
09606     entry_help_balloon $w.entry3 $help_text
09607 
09608 #== Executable arguments
09609     label $w.label4 -text "Executable args : "
09610     entry $w.entry4 -textvariable args_m -width 25 -background white
09611     grid  $w.label4 $w.entry4 -row $r -sticky w
09612     incr r
09613 
09614     # Ajoute une bulle d'aide pour 'Executable args'
09615     set help_text {Enter here a list of launching arguments for the application executable}
09616     entry_help_balloon $w.entry4 $help_text
09617 
09618 #== redirect stdout
09619     label $w.label5 -text "Redirect stdout : " 
09620     combobox_lim $w.cmb5 {yes no} {} redirect_m 10 $redirect_m
09621     grid  $w.label5 $w.cmb5 -row $r -sticky w
09622     incr r
09623 
09624     # Ajoute une bulle d'aide pour 'redirect stdout'
09625     set help_text {Enter here whether the application stdout shall be redirected or not.}
09626     entry_help_balloon $w.cmb5 $help_text
09627 
09628     # Séparateur
09629     frame $w.sep2 -width 200 -height 2 -borderwidth 1 -relief sunken
09630     grid  $w.sep2 -row $r -columnspan 2 -pady 5
09631     incr r
09632 
09633 #== Hosts
09634     label $w.label6 -text "Hosts : "
09635     scrollform_create $w.sform
09636     set form [scrollform_interior $w.sform]
09637     button $w.insert_host -text "Add" -command "host_insert $form"
09638     grid  $w.label6 $w.insert_host -row $r -sticky w
09639     incr r
09640 
09641     # une frame et un label pour le titre
09642     frame $w.fr2 
09643     grid $w.fr2 -row $r -sticky w -columnspan 2
09644     label $w.fr2.title1 -text "     Name                            Nb processes " -borderwidth 1 -justify left
09645     pack $w.fr2.title1 -anchor w
09646     incr r
09647     
09648     # une frame scrollable pour les "hosts"
09649     grid $w.sform -row $r -columnspan 2 -sticky ew
09650     $form configure -borderwidth 1 -relief sunken
09651     # grid columnconfigure $form 0 -minsize 15 -weight 15
09652     # grid columnconfigure $form 1 -minsize 10 -weight 10
09653 
09654    # insertion des boutons pour les hosts existant
09655     set nb_hosts 0
09656     foreach id_host $hosts_m(lhosts) {
09657         host_boutons_insert $form $id_host $nb_hosts
09658         incr nb_hosts
09659     }
09660 
09661     # Ajoute une bulle d'aide pour le bouton 'Add Host'
09662     global no_help_balloon
09663     if { $no_help_balloon == 0 } {
09664         set help_text {Click here to add a host to the list.}
09665         set help_action "help_object %W [list $help_text] message"
09666         bind $w.insert_host <Any-Enter> $help_action
09667         bind $w.insert_host <FocusIn>   $help_action
09668         bind $w.insert_host <Any-Leave>    "help_object_hide"
09669         bind $w.insert_host <FocusOut>     "help_object_hide"
09670         bind $w.insert_host <ButtonPress>  "help_object_hide"
09671     }
09672     
09673     # Ajoute une bulle d'aide pour la liste des 'hosts'
09674     set help_text {Enter here a list of hosts; for each host:
09675    
09676    1) the host name (used only in spawn mode as argument of the MPI_Comm_Spawn_Multiple).
09677 
09678    2) the number of processes to run this host (used in the not spawn method to split the global communicator; for the spawn method, used as argument in MPI_Comm_Spawn_Multiple ).}
09679     entry_help_balloon $w.sform $help_text
09680 
09681     # Si fermeture de la fenêtre, cache la bulle d'aide    
09682     bind $u <Destroy> "help_object_hide"
09683 }
09684 
09685 ret  {application_update} (type win , type nom_, type appli , type exe_, type name , type args , type redirect , type hosts_, type list) {
09686 # Validation de saisie des parametres d'une application
09687     global dico_applis
09688     
09689     upvar 1 $hosts_list hosts
09690 
09691     # Si l'application doit être lancée par OASIS
09692     set start_mode $dico_applis(app.$nom_appli.start_mode)
09693     if {$start_mode != "notspawn"} {
09694         # Vérifie le nom du prog. exe
09695         if { $exe_name == "" } {
09696             notice_show "Please, enter an executable file name !" error
09697             focus .mod_appli.char.entry3
09698             return error
09699         }
09700     }
09701         
09702     # Vérifie le nombre des ordinateurs hosts de l'application
09703     # --------------------------------------------------------
09704 
09705     if { [llength $hosts(lhosts)] != 0 } {
09706         
09707         set nb_proc_total 0
09708         foreach id_host $hosts(lhosts) {
09709             # Si le nom ou le nombre de processus de l'ordinateur host est defini
09710             if {$hosts(host.$id_host.name) != "" || $hosts(host.$id_host.nb_procs) != ""} {
09711                 # Si le nbre de processus est défini
09712                 if { [regexp {^[0-9]+$} $hosts(host.$id_host.nb_procs)] } {
09713                     # Additionne au total
09714                     incr nb_proc_total $hosts(host.$id_host.nb_procs)
09715                 }
09716             }
09717         }
09718         
09719         # Verifie que le nombre de processus est dans la fourchette acceptable pour l'appli
09720         set min_proc $dico_applis(app.$nom_appli.proc.min_value)
09721         set max_proc $dico_applis(app.$nom_appli.proc.max_value)
09722         set increment $dico_applis(app.$nom_appli.proc.increment)
09723         if {$max_proc != 0} {
09724             if { $nb_proc_total >= $min_proc && $nb_proc_total <= $max_proc } {
09725             
09726                 # Vérifie que le nombre est un multiple attendu
09727                 if {[expr ($nb_proc_total - $min_proc) % $increment] == 0} {
09728                     # OK
09729                 } else {
09730                     notice_show "Total number of processes ($nb_proc_total) out of the arithmetic series : \[$min_proc to $max_proc\] step $increment !" error
09731                     return error
09732                 }
09733             } else {
09734                 notice_show "Total number of processes ($nb_proc_total) out of the range \[$min_proc, $max_proc\]!" error
09735                 return error
09736             }
09737         }
09738         
09739         # Recopie les infos concernant l'application
09740         set dico_applis(app.$nom_appli.executable_name) $exe_name
09741         set dico_applis(app.$nom_appli.args) $args
09742         if { $redirect == "yes" } {
09743             set redirect "true"
09744         } else {
09745             set redirect "false"
09746         }
09747         set dico_applis(app.$nom_appli.redirect) $redirect
09748         
09749         # Recopie les infos concernant les ordinateurs hosts de l'application
09750         set dico_applis(app.$nom_appli.lhosts) {}
09751         array unset dico_applis app.$nom_appli.host.*
09752         foreach id_host $hosts(lhosts) {
09753             # Si le nom de l'ordinateur host est defini
09754             if {$hosts(host.$id_host.name) != ""} {
09755                 # Si le nbre de processus est défini
09756                 if { [regexp {^[0-9]+$} $hosts(host.$id_host.nb_procs)] } {
09757                     lappend dico_applis(app.$nom_appli.lhosts) $id_host
09758                     set clef app.$nom_appli.host.$id_host
09759                     set dico_applis($clef.name) $hosts(host.$id_host.name)
09760                     set dico_applis($clef.nb_procs) $hosts(host.$id_host.nb_procs)
09761                 }
09762             }
09763         }
09764         destroy $win
09765     } else {
09766         notice_show "Please, enter some host on which the application will run !" error
09767         return error
09768     }
09769 }
09770 
09771 ret  {chemin} (type x1 , type y1 , type x2 , type y2) {
09772 #=========de dessus a dessous
09773         if { $y1 < $y2 } {
09774             set y10 [expr $y1 +12 ]
09775             set y11 [expr $y2 -12]
09776             set y3 [expr $y1+($y2-$y1)/10 +12]
09777             set y4 [expr $y2-($y2-$y1)/10 -12]
09778             set x3 [expr $x1+($x2-$x1)/10 ]
09779             set x4 [expr $x2-($x2-$x1)/10 ]
09780             return "$x1 $y1 $x1 $y10 $x3 $y3 $x4 $y4  $x2 $y11 $x2 $y2"
09781         #=========de dessous a dessus
09782         } else {
09783             set y3 [expr $y1 +12]
09784             set y4 [expr $y2 -12]
09785             set x3 [expr ($x1+$x2)/2]
09786             return "$x1 $y1 $x1 $y3 $x3 $y3 $x3 $y4 $x2 $y4 $x2 $y2"
09787         }
09788 }
09789 
09790 ret  {chemin_force} (type x1 , type y1 , type x2 , type y2 , optional milieu_x ={) {milieu_y {}}} {
09791 /*  Chemin forcé : de passer par un point milieu, s'il est donné*/
09792         /* =========de dessus a dessous*/
09793         if { $y1 < $y2 } {
09794              y10 =  [expr $y1 +12 ]
09795              y11 =  [expr $y2 -12]
09796              y3 =  [expr $y1+($y2-$y1)/10 +12]
09797              y4 =  [expr $y2-($y2-$y1)/10 -12]
09798              x3 =  [expr $x1+($x2-$x1)/10 ]
09799              x4 =  [expr $x2-($x2-$x1)/10 ]
09800             if { $milieu_x != ""} {
09801                 return "$x1 $y1 $x1 $y10 $x3 $y3 $milieu_x $milieu_y $x4 $y4  $x2 $y11 $x2 $y2"
09802             } else {
09803                 return "$x1 $y1 $x1 $y10 $x3 $y3 $x4 $y4  $x2 $y11 $x2 $y2"
09804             }
09805         /* =========de dessous a dessus*/
09806         } else {
09807              y3 =  [expr $y1 +12]
09808              y4 =  [expr $y2 -12]
09809              x3 =  [expr ($x1+$x2)/2]
09810             if { $milieu_x != ""} {
09811                 if { $milieu_y > $y3 } {
09812                      y3 =  [expr $milieu_y +12]
09813                 }
09814                 if { $milieu_y < $y4 } {
09815                      y4 =  [expr $milieu_y -12]
09816                 }
09817                 return "$x1 $y1 $x1 $y3 $milieu_x $y3 $milieu_x $milieu_y $milieu_x $y4 $x2 $y4 $x2 $y2"
09818             } else {
09819                 return "$x1 $y1 $x1 $y3 $x3 $y3 $x3 $y4 $x2 $y4 $x2 $y2"
09820             }
09821         }
09822 }
09823 
09824 ret  {choose_font} (optional choose_init ={helvetica 12) texttoedit colortext just} {
09825 set w .choose_font
09826     global dxf
09827     set dxf(text) $texttoedit
09828     set dxf(choose_font_cb_color) $colortext
09829     set dxf(choose_font_cb_justify) $just
09830     set tmp [get_actual_font $choose_init]
09831 
09832     window.font $choose_init $texttoedit
09833     update
09834 
09835     catch {.choose_font.view delete 0.0 end}
09836     catch {.choose_font.view insert 0.0 $texttoedit}
09837     catch {.choose_font.view configure -foreground $colortext}
09838     catch {.choose_font.view configure -justify $just}
09839     update
09840     catch {grab $w}
09841     focus -force $w
09842     wm deiconify $w
09843     tkwait window $w
09844 
09845     grab release $w
09846     return [list $dxf(tmp) $dxf(text) $dxf(choose_font_cb_color) $dxf(choose_font_cb_justify)]
09847 }
09848 
09849 ret  {color_fonce} (type color) {
09850 set r 0
09851 set g 0
09852 set b 0
09853 scan $color "\#%2x%2x%2x" r g b
09854 set r [expr $r*7/8]
09855 set g [expr $g*7/8]
09856 set b [expr $b*7/8]
09857 
09858 return [color_rgb $r $g $b]
09859 }
09860 
09861 ret  {color_melange} (type color1 , type color2) {
09862 set r 0
09863 set g 0
09864 set b 0
09865 set r1 0
09866 set g1 0
09867 set b1 0
09868 
09869 
09870 scan $color1 "\#%2x%2x%2x" r g b
09871 scan $color2 "\#%2x%2x%2x" r1 g1 b1
09872 set r [expr ($r+$r1)/2]
09873 set g [expr ($g+$g1)/2]
09874 set b [expr ($b+$b1)/2]
09875 return [color_rgb $r $g $b]
09876 }
09877 
09878 ret  {color_pastelise} (type color) {
09879 set r 0
09880 set g 0
09881 set b 0
09882 
09883 
09884 scan $color "\#%2x%2x%2x" r g b
09885 set r [expr ($r+127)/3*2]
09886 set g [expr ($g+127)/3*2]
09887 set b [expr ($b+127)/3*2]
09888 return [color_rgb $r $g $b]
09889 }
09890 
09891 ret  {color_rgb} (type r , type g , type b) {
09892 set rgb \#[format "%.2X%.2X%.2X" $r $g $b]
09893 return $rgb
09894 }
09895 
09896 ret  {color_tresclair} (type color) {
09897 set r 0
09898 set g 0
09899 set b 0
09900 scan $color "\#%2x%2x%2x" r g b
09901 
09902 while {[expr $r+$g+$b] < 712} {
09903   set r [expr ($r+127)/3*2]
09904   set g [expr ($g+127)/3*2]
09905   set b [expr ($b+127)/3*2]
09906 }
09907 return [color_rgb $r $g $b]
09908 }
09909 
09910 ret  {color_white_or_black} (type color) {
09911 set r 0
09912 set g 0
09913 set b 0
09914 
09915 scan $color "\#%2x%2x%2x" r g b
09916 set colorreturn white
09917 if {[expr $r + $g + $b] > 300 } {set colorreturn black}
09918 
09919 return $colorreturn
09920 }
09921 
09922 ret  {combobox_init} () {
09923 # Copyright (c) 1998-2002, Bryan Oakley
09924 # All Rights Reservered
09925 #
09926 # Bryan Oakley
09927 # oakley@bardo.clearlight.com
09928 #
09929 # combobox v2.2.1 September 22, 2002
09930 #
09931 # a combobox / dropdown listbox (pick your favorite name) widget
09932 # written in pure tcl
09933 #
09934 # this code is freely distributable without restriction, but is
09935 # provided as-is with no warranty expressed or implied.
09936 #
09937 # thanks to the following people who provided beta test support or
09938 # patches to the code (in no particular order):
09939 #
09940 # Scott Beasley     Alexandre Ferrieux      Todd Helfter
09941 # Matt Gushee       Laurent Duperval        John Jackson
09942 # Fred Rapp         Christopher Nelson
09943 # Eric Galluzzo     Jean-Francois Moine
09944 #
09945 # A special thanks to Martin M. Hunt who provided several good ideas,
09946 # and always with a patch to implement them. Jean-Francois Moine,
09947 # Todd Helfter and John Jackson were also kind enough to send in some
09948 # code patches.
09949 #
09950 # ... and many others over the years.
09951 
09952 package require Tk 8.0
09953 package provide combobox 2.2.1
09954 
09955 namespace eval ::combobox {
09956 
09957     # this is the public interface
09958     namespace export combobox
09959 
09960     # these contain references to available options
09961     variable widgetOptions
09962 
09963     # these contain references to available commands and subcommands
09964     variable widgetCommands
09965     variable scanCommands
09966     variable listCommands
09967 }
09968 
09969 # ::combobox::combobox --
09970 #
09971 #     This is the command that gets exported. It creates a new
09972 #     combobox widget.
09973 #
09974 # Arguments:
09975 #
09976 #     w        path of new widget to create
09977 #     args     additional option/value pairs (eg: -background white, etc.)
09978 #
09979 # Results:
09980 #
09981 #     It creates the widget and sets up all of the default bindings
09982 #
09983 # Returns:
09984 #
09985 #     The name of the newly create widget
09986 
09987 proc ::combobox::combobox {w args} {
09988     variable widgetOptions
09989     variable widgetCommands
09990     variable scanCommands
09991     variable listCommands
09992 
09993     # perform a one time initialization
09994     if {![info exists widgetOptions]} {
09995     Init
09996     }
09997 
09998     # build it...
09999     eval Build $w $args
10000 
10001     # set some bindings...
10002     SetBindings $w
10003 
10004     # and we are done!
10005     return $w
10006 }
10007 
10008 
10009 # ::combobox::Init --
10010 #
10011 #     Initialize the namespace variables. This should only be called
10012 #     once, immediately prior to creating the first instance of the
10013 #     widget
10014 #
10015 # Arguments:
10016 #
10017 #    none
10018 #
10019 # Results:
10020 #
10021 #     All state variables are set to their default values; all of
10022 #     the option database entries will exist.
10023 #
10024 # Returns:
10025 #
10026 #     empty string
10027 
10028 proc ::combobox::Init {} {
10029     variable widgetOptions
10030     variable widgetCommands
10031     variable scanCommands
10032     variable listCommands
10033     variable defaultEntryCursor
10034 
10035     array set widgetOptions [list  -background          {background          Background}  -bd                  -borderwidth  -bg                  -background  -borderwidth         {borderWidth         BorderWidth}  -command             {command             Command}  -commandstate        {commandState        State}  -cursor              {cursor              Cursor}  -disabledbackground  {disabledBackground  DisabledBackground}  -disabledforeground  {disabledForeground  DisabledForeground}  -dropdownwidth       {dropdownWidth       DropdownWidth}  -editable            {editable            Editable}  -fg                  -foreground  -font                {font                Font}  -foreground          {foreground          Foreground}  -height              {height              Height}  -highlightbackground {highlightBackground HighlightBackground}  -highlightcolor      {highlightColor      HighlightColor}  -highlightthickness  {highlightThickness  HighlightThickness}  -image               {image               Image}  -maxheight           {maxHeight           Height}  -opencommand         {opencommand         Command}  -relief              {relief              Relief}  -selectbackground    {selectBackground    Foreground}  -selectborderwidth   {selectBorderWidth   BorderWidth}  -selectforeground    {selectForeground    Background}  -state               {state               State}  -takefocus           {takeFocus           TakeFocus}  -textvariable        {textVariable        Variable}  -value               {value               Value}  -width               {width               Width}  -xscrollcommand      {xScrollCommand      ScrollCommand}  ]
10036 
10037 
10038     set widgetCommands [list  bbox      cget     configure    curselection  delete    get      icursor      index         insert    list     scan         selection     xview     select   toggle       open          close      ]
10039 
10040     set listCommands [list  delete       get       index        insert       size  ]
10041 
10042     set scanCommands [list mark dragto]
10043 
10044     # why check for the Tk package? This lets us be sourced into
10045     # an interpreter that doesn't have Tk loaded, such as the slave
10046     # interpreter used by pkg_mkIndex. In theory it should have no
10047     # side effects when run
10048     if {[lsearch -exact [package names] "Tk"] != -1} {
10049 
10050     ##################################################################
10051     #- this initializes the option database. Kinda gross, but it works
10052     #- (I think).
10053     ##################################################################
10054 
10055     # the image used for the button...
10056     if {$::tcl_platform(platform) == "windows"} {
10057         image create bitmap ::combobox::bimage -data {
10058         #define down_arrow_width 12
10059         #define down_arrow_height 12
10060         static char down_arrow_bits[] = {
10061             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
10062             0xfc,0xf1,0xf8,0xf0,0x70,0xf0,0x20,0xf0,
10063             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00;
10064         }
10065         }
10066     } else {
10067         image create bitmap ::combobox::bimage -data  {
10068         #define down_arrow_width 15
10069         #define down_arrow_height 15
10070         static char down_arrow_bits[] = {
10071             0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,
10072             0x00,0x80,0xf8,0x8f,0xf0,0x87,0xe0,0x83,
10073             0xc0,0x81,0x80,0x80,0x00,0x80,0x00,0x80,
10074             0x00,0x80,0x00,0x80,0x00,0x80
10075         }
10076         }
10077     }
10078 
10079     # compute a widget name we can use to create a temporary widget
10080     set tmpWidget ".__tmp__"
10081     set count 0
10082     while {[winfo exists $tmpWidget] == 1} {
10083         set tmpWidget ".__tmp__$count"
10084         incr count
10085     }
10086 
10087     # get the scrollbar width. Because we try to be clever and draw our
10088     # own button instead of using a tk widget, we need to know what size
10089     # button to create. This little hack tells us the width of a scroll
10090     # bar.
10091     #
10092     # NB: we need to be sure and pick a window  that doesn't already
10093     # exist...
10094     scrollbar $tmpWidget
10095     set sb_width [winfo reqwidth $tmpWidget]
10096     destroy $tmpWidget
10097 
10098     # steal options from the entry widget
10099     # we want darn near all options, so we'll go ahead and do
10100     # them all. No harm done in adding the one or two that we
10101     # don't use.
10102     entry $tmpWidget
10103     foreach foo [$tmpWidget configure] {
10104         # the cursor option is special, so we'll save it in
10105         # a special way
10106         if {[lindex $foo 0] == "-cursor"} {
10107         set defaultEntryCursor [lindex $foo 4]
10108         }
10109         if {[llength $foo] == 5} {
10110         set option [lindex $foo 1]
10111         set value [lindex $foo 4]
10112         option add *Combobox.$option $value widgetDefault
10113 
10114         # these options also apply to the dropdown listbox
10115         if {[string compare $option "foreground"] == 0  || [string compare $option "background"] == 0  || [string compare $option "font"] == 0} {
10116             option add *Combobox*ComboboxListbox.$option $value  widgetDefault
10117         }
10118         }
10119     }
10120     destroy $tmpWidget
10121 
10122     # these are unique to us...
10123     option add *Combobox.dropdownWidth       {}     widgetDefault
10124     option add *Combobox.openCommand         {}     widgetDefault
10125     option add *Combobox.cursor              {}     widgetDefault
10126     option add *Combobox.commandState        normal widgetDefault
10127     option add *Combobox.editable            1      widgetDefault
10128     option add *Combobox.maxHeight           10     widgetDefault
10129     option add *Combobox.height              0
10130     }
10131 
10132     # set class bindings
10133     SetClassBindings
10134 }
10135 
10136 # ::combobox::SetClassBindings --
10137 #
10138 #    Sets up the default bindings for the widget class
10139 #
10140 #    this proc exists since it's The Right Thing To Do, but
10141 #    I haven't had the time to figure out how to do all the
10142 #    binding stuff on a class level. The main problem is that
10143 #    the entry widget must have focus for the insertion cursor
10144 #    to be visible. So, I either have to have the entry widget
10145 #    have the Combobox bindtag, or do some fancy juggling of
10146 #    events or some such. What a pain.
10147 #
10148 # Arguments:
10149 #
10150 #    none
10151 #
10152 # Returns:
10153 #
10154 #    empty string
10155 
10156 proc ::combobox::SetClassBindings {} {
10157 
10158     # make sure we clean up after ourselves...
10159     bind Combobox <Destroy> [list ::combobox::DestroyHandler %W]
10160 
10161     # this will (hopefully) close (and lose the grab on) the
10162     # listbox if the user clicks anywhere outside of it. Note
10163     # that on Windows, you can click on some other app and
10164     # the listbox will still be there, because tcl won't see
10165     # that button click
10166     set this {[::combobox::convert %W -W]}
10167     bind Combobox <Any-ButtonPress>   "$this close"
10168     bind Combobox <Any-ButtonRelease> "$this close"
10169 
10170     # this helps (but doesn't fully solve) focus issues. The general
10171     # idea is, whenever the frame gets focus it gets passed on to
10172     # the entry widget
10173     bind Combobox <FocusIn> {::combobox::tkTabToWindow [::combobox::convert %W -W].entry}
10174 
10175     # this closes the listbox if we get hidden
10176     bind Combobox <Unmap> {[::combobox::convert %W -W] close}
10177 
10178     return ""
10179 }
10180 
10181 # ::combobox::SetBindings --
10182 #
10183 #    here's where we do most of the binding foo. I think there's probably
10184 #    a few bindings I ought to add that I just haven't thought
10185 #    about...
10186 #
10187 #    I'm not convinced these are the proper bindings. Ideally all
10188 #    bindings should be on "Combobox", but because of my juggling of
10189 #    bindtags I'm not convinced thats what I want to do. But, it all
10190 #    seems to work, its just not as robust as it could be.
10191 #
10192 # Arguments:
10193 #
10194 #    w    widget pathname
10195 #
10196 # Returns:
10197 #
10198 #    empty string
10199 
10200 proc ::combobox::SetBindings {w} {
10201     upvar ::combobox::${w}::widgets  widgets
10202     upvar ::combobox::${w}::options  options
10203 
10204     # juggle the bindtags. The basic idea here is to associate the
10205     # widget name with the entry widget, so if a user does a bind
10206     # on the combobox it will get handled properly since it is
10207     # the entry widget that has keyboard focus.
10208     bindtags $widgets(entry)  [concat $widgets(this) [bindtags $widgets(entry)]]
10209 
10210     bindtags $widgets(button)  [concat $widgets(this) [bindtags $widgets(button)]]
10211 
10212     # override the default bindings for tab and shift-tab. The
10213     # focus procs take a widget as their only parameter and we
10214     # want to make sure the right window gets used (for shift-
10215     # tab we want it to appear as if the event was generated
10216     # on the frame rather than the entry.
10217     bind $widgets(entry) <Tab>  "::combobox::tkTabToWindow \[tk_focusNext $widgets(entry)\]; break"
10218     bind $widgets(entry) <Shift-Tab>  "::combobox::tkTabToWindow \[tk_focusPrev $widgets(this)\]; break"
10219 
10220     # this makes our "button" (which is actually a label)
10221     # do the right thing
10222     bind $widgets(button) <ButtonPress-1> [list $widgets(this) toggle]
10223 
10224     # this lets the autoscan of the listbox work, even if they
10225     # move the cursor over the entry widget.
10226     bind $widgets(entry) <B1-Enter> "break"
10227 
10228     bind $widgets(listbox) <ButtonRelease-1>  "::combobox::Select [list $widgets(this)]  \[$widgets(listbox) nearest %y\]; break"
10229 
10230     bind $widgets(vsb) <ButtonPress-1>   {continue}
10231     bind $widgets(vsb) <ButtonRelease-1> {continue}
10232 
10233     bind $widgets(listbox) <Any-Motion> {
10234     %W selection clear 0 end
10235     %W activate @%x,%y
10236     %W selection anchor @%x,%y
10237     %W selection set @%x,%y @%x,%y
10238     # need to do a yview if the cursor goes off the top
10239     # or bottom of the window... (or do we?)
10240     }
10241 
10242     # these events need to be passed from the entry widget
10243     # to the listbox, or otherwise need some sort of special
10244     # handling.
10245     foreach event [list <Up> <Down> <Tab> <Return> <Escape>  <Next> <Prior> <Double-1> <1> <Any-KeyPress>  <FocusIn> <FocusOut>] {
10246     bind $widgets(entry) $event  [list ::combobox::HandleEvent $widgets(this) $event]
10247     }
10248 
10249     # like the other events, <MouseWheel> needs to be passed from
10250     # the entry widget to the listbox. However, in this case we
10251     # need to add an additional parameter
10252     catch {
10253     bind $widgets(entry) <MouseWheel>  [list ::combobox::HandleEvent $widgets(this) <MouseWheel> %D]
10254     }
10255 }
10256 
10257 # ::combobox::Build --
10258 #
10259 #    This does all of the work necessary to create the basic
10260 #    combobox.
10261 #
10262 # Arguments:
10263 #
10264 #    w        widget name
10265 #    args     additional option/value pairs
10266 #
10267 # Results:
10268 #
10269 #    Creates a new widget with the given name. Also creates a new
10270 #    namespace patterened after the widget name, as a child namespace
10271 #    to ::combobox
10272 #
10273 # Returns:
10274 #
10275 #    the name of the widget
10276 
10277 proc ::combobox::Build {w args } {
10278     variable widgetOptions
10279 
10280     if {[winfo exists $w]} {
10281     error "window name \"$w\" already exists"
10282     }
10283 
10284     # create the namespace for this instance, and define a few
10285     # variables
10286     namespace eval ::combobox::$w {
10287 
10288     variable ignoreTrace 0
10289     variable oldFocus    {}
10290     variable oldGrab     {}
10291     variable oldValue    {}
10292     variable options
10293     variable this
10294     variable widgets
10295 
10296     set widgets(foo) foo  ;# coerce into an array
10297     set options(foo) foo  ;# coerce into an array
10298 
10299     unset widgets(foo)
10300     unset options(foo)
10301     }
10302 
10303     # import the widgets and options arrays into this proc so
10304     # we don't have to use fully qualified names, which is a
10305     # pain.
10306     upvar ::combobox::${w}::widgets widgets
10307     upvar ::combobox::${w}::options options
10308 
10309     # this is our widget -- a frame of class Combobox. Naturally,
10310     # it will contain other widgets. We create it here because
10311     # we need it in order to set some default options.
10312     set widgets(this)   [frame  $w -class Combobox -takefocus 0]
10313     set widgets(entry)  [entry  $w.entry -takefocus 1]
10314     set widgets(button) [label  $w.button -takefocus 0]
10315 
10316     # this defines all of the default options. We get the
10317     # values from the option database. Note that if an array
10318     # value is a list of length one it is an alias to another
10319     # option, so we just ignore it
10320     foreach name [array names widgetOptions] {
10321     if {[llength $widgetOptions($name)] == 1} continue
10322 
10323     set optName  [lindex $widgetOptions($name) 0]
10324     set optClass [lindex $widgetOptions($name) 1]
10325 
10326     set value [option get $w $optName $optClass]
10327     set options($name) $value
10328     }
10329 
10330     # a couple options aren't available in earlier versions of
10331     # tcl, so we'll set them to sane values. For that matter, if
10332     # they exist but are empty, set them to sane values.
10333     if {[string length $options(-disabledforeground)] == 0} {
10334         set options(-disabledforeground) $options(-foreground)
10335     }
10336     if {[string length $options(-disabledbackground)] == 0} {
10337         set options(-disabledbackground) $options(-background)
10338     }
10339 
10340     # if -value is set to null, we'll remove it from our
10341     # local array. The assumption is, if the user sets it from
10342     # the option database, they will set it to something other
10343     # than null (since it's impossible to determine the difference
10344     # between a null value and no value at all).
10345     if {[info exists options(-value)]  && [string length $options(-value)] == 0} {
10346     unset options(-value)
10347     }
10348 
10349     # we will later rename the frame's widget proc to be our
10350     # own custom widget proc. We need to keep track of this
10351     # new name, so we'll define and store it here...
10352     set widgets(frame) ::combobox::${w}::$w
10353 
10354     # gotta do this sooner or later. Might as well do it now
10355     pack $widgets(entry)  -side left  -fill both -expand yes
10356     pack $widgets(button) -side right -fill y    -expand no
10357 
10358     # I should probably do this in a catch, but for now it's
10359     # good enough... What it does, obviously, is put all of
10360     # the option/values pairs into an array. Make them easier
10361     # to handle later on...
10362     array set options $args
10363 
10364     # now, the dropdown list... the same renaming nonsense
10365     # must go on here as well...
10366     set widgets(dropdown)   [toplevel  $w.top]
10367     set widgets(listbox) [listbox   $w.top.list]
10368     set widgets(vsb)     [scrollbar $w.top.vsb]
10369 
10370     pack $widgets(listbox) -side left -fill both -expand y
10371 
10372     # fine tune the widgets based on the options (and a few
10373     # arbitrary values...)
10374 
10375     # NB: we are going to use the frame to handle the relief
10376     # of the widget as a whole, so the entry widget will be
10377     # flat. This makes the button which drops down the list
10378     # to appear "inside" the entry widget.
10379 
10380     $widgets(vsb) configure  -command "$widgets(listbox) yview"  -highlightthickness 0
10381 
10382     $widgets(button) configure  -highlightthickness 0  -borderwidth 1  -relief raised  -width [expr {[winfo reqwidth $widgets(vsb)] - 2}]
10383 
10384     $widgets(entry) configure  -borderwidth 0  -relief flat  -highlightthickness 0
10385 
10386     $widgets(dropdown) configure  -borderwidth 1  -relief sunken
10387 
10388     $widgets(listbox) configure  -selectmode browse  -background [$widgets(entry) cget -bg]  -yscrollcommand "$widgets(vsb) set"  -exportselection false  -borderwidth 0
10389 
10390 
10391 #    trace variable ::combobox::${w}::entryTextVariable w  #        [list ::combobox::EntryTrace $w]
10392     
10393     # do some window management foo on the dropdown window
10394     wm overrideredirect $widgets(dropdown) 1
10395     wm transient        $widgets(dropdown) [winfo toplevel $w]
10396     wm group            $widgets(dropdown) [winfo parent $w]
10397     wm resizable        $widgets(dropdown) 0 0
10398     wm withdraw         $widgets(dropdown)
10399 
10400     # this moves the original frame widget proc into our
10401     # namespace and gives it a handy name
10402     rename ::$w $widgets(frame)
10403 
10404     # now, create our widget proc. Obviously (?) it goes in
10405     # the global namespace. All combobox widgets will actually
10406     # share the same widget proc to cut down on the amount of
10407     # bloat.
10408     proc ::$w {command args}  "eval ::combobox::WidgetProc $w \$command \$args"
10409 
10410 
10411     # ok, the thing exists... let's do a bit more configuration.
10412     if {[catch "::combobox::Configure [list $widgets(this)] [array get options]" error]} {
10413     catch {destroy $w}
10414     error "internal error: $error"
10415     }
10416 
10417     return ""
10418 
10419 }
10420 
10421 # ::combobox::HandleEvent --
10422 #
10423 #    this proc handles events from the entry widget that we want
10424 #    handled specially (typically, to allow navigation of the list
10425 #    even though the focus is in the entry widget)
10426 #
10427 # Arguments:
10428 #
10429 #    w       widget pathname
10430 #    event   a string representing the event (not necessarily an
10431 #            actual event)
10432 #    args    additional arguments required by particular events
10433 
10434 proc ::combobox::HandleEvent {w event args} {
10435     upvar ::combobox::${w}::widgets  widgets
10436     upvar ::combobox::${w}::options  options
10437     upvar ::combobox::${w}::oldValue oldValue
10438 
10439     # for all of these events, if we have a special action we'll
10440     # do that and do a "return -code break" to keep additional
10441     # bindings from firing. Otherwise we'll let the event fall
10442     # on through.
10443     switch $event {
10444 
10445         "<MouseWheel>" {
10446         if {[winfo ismapped $widgets(dropdown)]} {
10447                 set D [lindex $args 0]
10448                 # the '120' number in the following expression has
10449                 # it's genesis in the tk bind manpage, which suggests
10450                 # that the smallest value of %D for mousewheel events
10451                 # will be 120. The intent is to scroll one line at a time.
10452                 $widgets(listbox) yview scroll [expr {-($D/120)}] units
10453             }
10454         }
10455 
10456     "<Any-KeyPress>" {
10457         # if the widget is editable, clear the selection.
10458         # this makes it more obvious what will happen if the
10459         # user presses <Return> (and helps our code know what
10460         # to do if the user presses return)
10461         if {$options(-editable)} {
10462         $widgets(listbox) see 0
10463         $widgets(listbox) selection clear 0 end
10464         $widgets(listbox) selection anchor 0
10465         $widgets(listbox) activate 0
10466         }
10467     }
10468 
10469     "<FocusIn>" {
10470         set oldValue [$widgets(entry) get]
10471     }
10472 
10473     "<FocusOut>" {
10474         if {![winfo ismapped $widgets(dropdown)]} {
10475         # did the value change?
10476         set newValue [$widgets(entry) get]
10477         if {$oldValue != $newValue} {
10478             CallCommand $widgets(this) $newValue
10479         }
10480         }
10481     }
10482 
10483     "<1>" {
10484         set editable [::combobox::GetBoolean $options(-editable)]
10485         if {!$editable} {
10486         if {[winfo ismapped $widgets(dropdown)]} {
10487             $widgets(this) close
10488             return -code break;
10489 
10490         } else {
10491             if {$options(-state) != "disabled"} {
10492             $widgets(this) open
10493             return -code break;
10494             }
10495         }
10496         }
10497     }
10498 
10499     "<Double-1>" {
10500         if {$options(-state) != "disabled"} {
10501         $widgets(this) toggle
10502         return -code break;
10503         }
10504     }
10505 
10506     "<Tab>" {
10507         if {[winfo ismapped $widgets(dropdown)]} {
10508         ::combobox::Find $widgets(this) 0
10509         return -code break;
10510         } else {
10511         ::combobox::SetValue $widgets(this) [$widgets(this) get]
10512         }
10513     }
10514 
10515     "<Escape>" {
10516 #       $widgets(entry) delete 0 end
10517 #       $widgets(entry) insert 0 $oldValue
10518         if {[winfo ismapped $widgets(dropdown)]} {
10519         $widgets(this) close
10520         return -code break;
10521         }
10522     }
10523 
10524     "<Return>" {
10525         # did the value change?
10526         set newValue [$widgets(entry) get]
10527         if {$oldValue != $newValue} {
10528         CallCommand $widgets(this) $newValue
10529         }
10530 
10531         if {[winfo ismapped $widgets(dropdown)]} {
10532         ::combobox::Select $widgets(this)  [$widgets(listbox) curselection]
10533         return -code break;
10534         }
10535 
10536     }
10537 
10538     "<Next>" {
10539         $widgets(listbox) yview scroll 1 pages
10540         set index [$widgets(listbox) index @0,0]
10541         $widgets(listbox) see $index
10542         $widgets(listbox) activate $index
10543         $widgets(listbox) selection clear 0 end
10544         $widgets(listbox) selection anchor $index
10545         $widgets(listbox) selection set $index
10546 
10547     }
10548 
10549     "<Prior>" {
10550         $widgets(listbox) yview scroll -1 pages
10551         set index [$widgets(listbox) index @0,0]
10552         $widgets(listbox) activate $index
10553         $widgets(listbox) see $index
10554         $widgets(listbox) selection clear 0 end
10555         $widgets(listbox) selection anchor $index
10556         $widgets(listbox) selection set $index
10557     }
10558 
10559     "<Down>" {
10560         if {[winfo ismapped $widgets(dropdown)]} {
10561         ::combobox::tkListboxUpDown $widgets(listbox) 1
10562         return -code break;
10563 
10564         } else {
10565         if {$options(-state) != "disabled"} {
10566             $widgets(this) open
10567             return -code break;
10568         }
10569         }
10570     }
10571     "<Up>" {
10572         if {[winfo ismapped $widgets(dropdown)]} {
10573         ::combobox::tkListboxUpDown $widgets(listbox) -1
10574         return -code break;
10575 
10576         } else {
10577         if {$options(-state) != "disabled"} {
10578             $widgets(this) open
10579             return -code break;
10580         }
10581         }
10582     }
10583     }
10584 
10585     return ""
10586 }
10587 
10588 # ::combobox::DestroyHandler {w} --
10589 #
10590 #    Cleans up after a combobox widget is destroyed
10591 #
10592 # Arguments:
10593 #
10594 #    w    widget pathname
10595 #
10596 # Results:
10597 #
10598 #    The namespace that was created for the widget is deleted,
10599 #    and the widget proc is removed.
10600 
10601 proc ::combobox::DestroyHandler {w} {
10602 
10603     # if the widget actually being destroyed is of class Combobox,
10604     # crush the namespace and kill the proc. Get it? Crush. Kill.
10605     # Destroy. Heh. Danger Will Robinson! Oh, man! I'm so funny it
10606     # brings tears to my eyes.
10607     if {[string compare [winfo class $w] "Combobox"] == 0} {
10608     upvar ::combobox::${w}::widgets  widgets
10609     upvar ::combobox::${w}::options  options
10610 
10611     # delete the namespace and the proc which represents
10612     # our widget
10613     namespace delete ::combobox::$w
10614     rename $w {}
10615     }
10616 
10617     return ""
10618 }
10619 
10620 # ::combobox::Find
10621 #
10622 #    finds something in the listbox that matches the pattern in the
10623 #    entry widget and selects it
10624 #
10625 #    N.B. I'm not convinced this is working the way it ought to. It
10626 #    works, but is the behavior what is expected? I've also got a gut
10627 #    feeling that there's a better way to do this, but I'm too lazy to
10628 #    figure it out...
10629 #
10630 # Arguments:
10631 #
10632 #    w      widget pathname
10633 #    exact  boolean; if true an exact match is desired
10634 #
10635 # Returns:
10636 #
10637 #    Empty string
10638 
10639 proc ::combobox::Find {w {exact 0}} {
10640     upvar ::combobox::${w}::widgets widgets
10641     upvar ::combobox::${w}::options options
10642 
10643     ## *sigh* this logic is rather gross and convoluted. Surely
10644     ## there is a more simple, straight-forward way to implement
10645     ## all this. As the saying goes, I lack the time to make it
10646     ## shorter...
10647 
10648     # use what is already in the entry widget as a pattern
10649     set pattern [$widgets(entry) get]
10650 
10651     if {[string length $pattern] == 0} {
10652     # clear the current selection
10653     $widgets(listbox) see 0
10654     $widgets(listbox) selection clear 0 end
10655     $widgets(listbox) selection anchor 0
10656     $widgets(listbox) activate 0
10657     return
10658     }
10659 
10660     # we're going to be searching this list...
10661     set list [$widgets(listbox) get 0 end]
10662 
10663     # if we are doing an exact match, try to find,
10664     # well, an exact match
10665     set exactMatch -1
10666     if {$exact} {
10667     set exactMatch [lsearch -exact $list $pattern]
10668     }
10669 
10670     # search for it. We'll try to be clever and not only
10671     # search for a match for what they typed, but a match for
10672     # something close to what they typed. We'll keep removing one
10673     # character at a time from the pattern until we find a match
10674     # of some sort.
10675     set index -1
10676     while {$index == -1 && [string length $pattern]} {
10677     set index [lsearch -glob $list "$pattern*"]
10678     if {$index == -1} {
10679         regsub {.$} $pattern {} pattern
10680     }
10681     }
10682 
10683     # this is the item that most closely matches...
10684     set thisItem [lindex $list $index]
10685 
10686     # did we find a match? If so, do some additional munging...
10687     if {$index != -1} {
10688 
10689     # we need to find the part of the first item that is
10690     # unique WRT the second... I know there's probably a
10691     # simpler way to do this...
10692 
10693     set nextIndex [expr {$index + 1}]
10694     set nextItem [lindex $list $nextIndex]
10695 
10696     # we don't really need to do much if the next
10697     # item doesn't match our pattern...
10698     if {[string match $pattern* $nextItem]} {
10699         # ok, the next item matches our pattern, too
10700         # now the trick is to find the first character
10701         # where they *don't* match...
10702         set marker [string length $pattern]
10703         while {$marker <= [string length $pattern]} {
10704         set a [string index $thisItem $marker]
10705         set b [string index $nextItem $marker]
10706         if {[string compare $a $b] == 0} {
10707             append pattern $a
10708             incr marker
10709         } else {
10710             break
10711         }
10712         }
10713     } else {
10714         set marker [string length $pattern]
10715     }
10716     
10717     } else {
10718     set marker end
10719     set index 0
10720     }
10721 
10722     # ok, we know the pattern and what part is unique;
10723     # update the entry widget and listbox appropriately
10724     if {$exact && $exactMatch == -1} {
10725     # this means we didn't find an exact match
10726     $widgets(listbox) selection clear 0 end
10727     $widgets(listbox) see $index
10728 
10729     } elseif {!$exact}  {
10730     # this means we found something, but it isn't an exact
10731     # match. If we find something that *is* an exact match we
10732     # don't need to do the following, since it would merely
10733     # be replacing the data in the entry widget with itself
10734     set oldstate [$widgets(entry) cget -state]
10735     $widgets(entry) configure -state normal
10736     $widgets(entry) delete 0 end
10737     $widgets(entry) insert end $thisItem
10738     $widgets(entry) selection clear
10739     $widgets(entry) selection range $marker end
10740     $widgets(listbox) activate $index
10741     $widgets(listbox) selection clear 0 end
10742     $widgets(listbox) selection anchor $index
10743     $widgets(listbox) selection set $index
10744     $widgets(listbox) see $index
10745     $widgets(entry) configure -state $oldstate
10746     }
10747 }
10748 
10749 # ::combobox::Select --
10750 #
10751 #    selects an item from the list and sets the value of the combobox
10752 #    to that value
10753 #
10754 # Arguments:
10755 #
10756 #    w      widget pathname
10757 #    index  listbox index of item to be selected
10758 #
10759 # Returns:
10760 #
10761 #    empty string
10762 
10763 proc ::combobox::Select {w index} {
10764     upvar ::combobox::${w}::widgets widgets
10765     upvar ::combobox::${w}::options options
10766 
10767     # the catch is because I'm sloppy -- presumably, the only time
10768     # an error will be caught is if there is no selection.
10769     if {![catch {set data [$widgets(listbox) get [lindex $index 0]]}]} {
10770     ::combobox::SetValue $widgets(this) $data
10771 
10772     $widgets(listbox) selection clear 0 end
10773     $widgets(listbox) selection anchor $index
10774     $widgets(listbox) selection set $index
10775 
10776     }
10777     $widgets(entry) selection range 0 end
10778 
10779     $widgets(this) close
10780 
10781     return ""
10782 }
10783 
10784 # ::combobox::HandleScrollbar --
10785 #
10786 #    causes the scrollbar of the dropdown list to appear or disappear
10787 #    based on the contents of the dropdown listbox
10788 #
10789 # Arguments:
10790 #
10791 #    w       widget pathname
10792 #    action  the action to perform on the scrollbar
10793 #
10794 # Returns:
10795 #
10796 #    an empty string
10797 
10798 proc ::combobox::HandleScrollbar {w {action "unknown"}} {
10799     upvar ::combobox::${w}::widgets widgets
10800     upvar ::combobox::${w}::options options
10801 
10802     if {$options(-height) == 0} {
10803     set hlimit $options(-maxheight)
10804     } else {
10805     set hlimit $options(-height)
10806     }       
10807 
10808     switch $action {
10809     "grow" {
10810         if {$hlimit > 0 && [$widgets(listbox) size] > $hlimit} {
10811         pack $widgets(vsb) -side right -fill y -expand n
10812         }
10813     }
10814 
10815     "shrink" {
10816         if {$hlimit > 0 && [$widgets(listbox) size] <= $hlimit} {
10817         pack forget $widgets(vsb)
10818         }
10819     }
10820 
10821     "crop" {
10822         # this means the window was cropped and we definitely
10823         # need a scrollbar no matter what the user wants
10824         pack $widgets(vsb) -side right -fill y -expand n
10825     }
10826 
10827     default {
10828         if {$hlimit > 0 && [$widgets(listbox) size] > $hlimit} {
10829         pack $widgets(vsb) -side right -fill y -expand n
10830         } else {
10831         pack forget $widgets(vsb)
10832         }
10833     }
10834     }
10835 
10836     return ""
10837 }
10838 
10839 # ::combobox::ComputeGeometry --
10840 #
10841 #    computes the geometry of the dropdown list based on the size of the
10842 #    combobox...
10843 #
10844 # Arguments:
10845 #
10846 #    w     widget pathname
10847 #
10848 # Returns:
10849 #
10850 #    the desired geometry of the listbox
10851 
10852 proc ::combobox::ComputeGeometry {w} {
10853     upvar ::combobox::${w}::widgets widgets
10854     upvar ::combobox::${w}::options options
10855 
10856     if {$options(-height) == 0 && $options(-maxheight) != "0"} {
10857     # if this is the case, count the items and see if
10858     # it exceeds our maxheight. If so, set the listbox
10859     # size to maxheight...
10860     set nitems [$widgets(listbox) size]
10861     if {$nitems > $options(-maxheight)} {
10862         # tweak the height of the listbox
10863         $widgets(listbox) configure -height $options(-maxheight)
10864     } else {
10865         # un-tweak the height of the listbox
10866         $widgets(listbox) configure -height 0
10867     }
10868     update idletasks
10869     }
10870 
10871     # compute height and width of the dropdown list
10872     set bd [$widgets(dropdown) cget -borderwidth]
10873     set height [expr {[winfo reqheight $widgets(dropdown)] + $bd + $bd}]
10874     if {[string length $options(-dropdownwidth)] == 0 ||
10875         $options(-dropdownwidth) == 0} {
10876         set width [winfo width $widgets(this)]
10877     } else {
10878         set m [font measure [$widgets(listbox) cget -font] "m"]
10879         set width [expr {$options(-dropdownwidth) * $m}]
10880     }
10881 
10882     # figure out where to place it on the screen, trying to take into
10883     # account we may be running under some virtual window manager
10884     set screenWidth  [winfo screenwidth $widgets(this)]
10885     set screenHeight [winfo screenheight $widgets(this)]
10886     set rootx        [winfo rootx $widgets(this)]
10887     set rooty        [winfo rooty $widgets(this)]
10888     set vrootx       [winfo vrootx $widgets(this)]
10889     set vrooty       [winfo vrooty $widgets(this)]
10890 
10891     # the x coordinate is simply the rootx of our widget, adjusted for
10892     # the virtual window. We won't worry about whether the window will
10893     # be offscreen to the left or right -- we want the illusion that it
10894     # is part of the entry widget, so if part of the entry widget is off-
10895     # screen, so will the list. If you want to change the behavior,
10896     # simply change the if statement... (and be sure to update this
10897     # comment!)
10898     set x  [expr {$rootx + $vrootx}]
10899     if {0} {
10900     set rightEdge [expr {$x + $width}]
10901     if {$rightEdge > $screenWidth} {
10902         set x [expr {$screenWidth - $width}]
10903     }
10904     if {$x < 0} {set x 0}
10905     }
10906 
10907     # the y coordinate is the rooty plus vrooty offset plus
10908     # the height of the static part of the widget plus 1 for a
10909     # tiny bit of visual separation...
10910     set y [expr {$rooty + $vrooty + [winfo reqheight $widgets(this)] + 1}]
10911     set bottomEdge [expr {$y + $height}]
10912 
10913     if {$bottomEdge >= $screenHeight} {
10914     # ok. Fine. Pop it up above the entry widget isntead of
10915     # below.
10916     set y [expr {($rooty - $height - 1) + $vrooty}]
10917 
10918     if {$y < 0} {
10919         # this means it extends beyond our screen. How annoying.
10920         # Now we'll try to be real clever and either pop it up or
10921         # down, depending on which way gives us the biggest list.
10922         # then, we'll trim the list to fit and force the use of
10923         # a scrollbar
10924 
10925         # (sadly, for windows users this measurement doesn't
10926         # take into consideration the height of the taskbar,
10927         # but don't blame me -- there isn't any way to detect
10928         # it or figure out its dimensions. The same probably
10929         # applies to any window manager with some magic windows
10930         # glued to the top or bottom of the screen)
10931 
10932         if {$rooty > [expr {$screenHeight / 2}]} {
10933         # we are in the lower half of the screen --
10934         # pop it up. Y is zero; that parts easy. The height
10935         # is simply the y coordinate of our widget, minus
10936         # a pixel for some visual separation. The y coordinate
10937         # will be the topof the screen.
10938         set y 1
10939         set height [expr {$rooty - 1 - $y}]
10940 
10941         } else {
10942         # we are in the upper half of the screen --
10943         # pop it down
10944         set y [expr {$rooty + $vrooty +  [winfo reqheight $widgets(this)] + 1}]
10945         set height [expr {$screenHeight - $y}]
10946 
10947         }
10948 
10949         # force a scrollbar
10950         HandleScrollbar $widgets(this) crop
10951     }   
10952     }
10953 
10954     if {$y < 0} {
10955     # hmmm. Bummer.
10956     set y 0
10957     set height $screenheight
10958     }
10959 
10960     set geometry [format "=%dx%d+%d+%d" $width $height $x $y]
10961 
10962     return $geometry
10963 }
10964 
10965 # ::combobox::DoInternalWidgetCommand --
10966 #
10967 #    perform an internal widget command, then mung any error results
10968 #    to look like it came from our megawidget. A lot of work just to
10969 #    give the illusion that our megawidget is an atomic widget
10970 #
10971 # Arguments:
10972 #
10973 #    w           widget pathname
10974 #    subwidget   pathname of the subwidget
10975 #    command     subwidget command to be executed
10976 #    args        arguments to the command
10977 #
10978 # Returns:
10979 #
10980 #    The result of the subwidget command, or an error
10981 
10982 proc ::combobox::DoInternalWidgetCommand {w subwidget command args} {
10983     upvar ::combobox::${w}::widgets widgets
10984     upvar ::combobox::${w}::options options
10985 
10986     set subcommand $command
10987     set command [concat $widgets($subwidget) $command $args]
10988     if {[catch $command result]} {
10989     # replace the subwidget name with the megawidget name
10990     regsub $widgets($subwidget) $result $widgets(this) result
10991 
10992     # replace specific instances of the subwidget command
10993     # with out megawidget command
10994     switch $subwidget,$subcommand {
10995         listbox,index  {regsub "index"  $result "list index"  result}
10996         listbox,insert {regsub "insert" $result "list insert" result}
10997         listbox,delete {regsub "delete" $result "list delete" result}
10998         listbox,get    {regsub "get"    $result "list get"    result}
10999         listbox,size   {regsub "size"   $result "list size"   result}
11000     }
11001     error $result
11002 
11003     } else {
11004     return $result
11005     }
11006 }
11007 
11008 
11009 # ::combobox::WidgetProc --
11010 #
11011 #    This gets uses as the widgetproc for an combobox widget.
11012 #    Notice where the widget is created and you'll see that the
11013 #    actual widget proc merely evals this proc with all of the
11014 #    arguments intact.
11015 #
11016 #    Note that some widget commands are defined "inline" (ie:
11017 #    within this proc), and some do most of their work in
11018 #    separate procs. This is merely because sometimes it was
11019 #    easier to do it one way or the other.
11020 #
11021 # Arguments:
11022 #
11023 #    w         widget pathname
11024 #    command   widget subcommand
11025 #    args      additional arguments; varies with the subcommand
11026 #
11027 # Results:
11028 #
11029 #    Performs the requested widget command
11030 
11031 proc ::combobox::WidgetProc {w command args} {
11032     upvar ::combobox::${w}::widgets widgets
11033     upvar ::combobox::${w}::options options
11034     upvar ::combobox::${w}::oldFocus oldFocus
11035     upvar ::combobox::${w}::oldFocus oldGrab
11036 
11037     set command [::combobox::Canonize $w command $command]
11038 
11039     # this is just shorthand notation...
11040     set doWidgetCommand  [list ::combobox::DoInternalWidgetCommand $widgets(this)]
11041 
11042     if {$command == "list"} {
11043     # ok, the next argument is a list command; we'll
11044     # rip it from args and append it to command to
11045     # create a unique internal command
11046     #
11047     # NB: because of the sloppy way we are doing this,
11048     # we'll also let the user enter our secret command
11049     # directly (eg: listinsert, listdelete), but we
11050     # won't document that fact
11051     set command "list-[lindex $args 0]"
11052     set args [lrange $args 1 end]
11053     }
11054 
11055     set result ""
11056 
11057     # many of these commands are just synonyms for specific
11058     # commands in one of the subwidgets. We'll get them out
11059     # of the way first, then do the custom commands.
11060     switch $command {
11061     bbox -
11062     delete -
11063     get -
11064     icursor -
11065     index -
11066     insert -
11067     scan -
11068     selection -
11069     xview {
11070         set result [eval $doWidgetCommand entry $command $args]
11071     }
11072     list-get    {set result [eval $doWidgetCommand listbox get $args]}
11073     list-index  {set result [eval $doWidgetCommand listbox index $args]}
11074     list-size   {set result [eval $doWidgetCommand listbox size $args]}
11075 
11076     select {
11077         if {[llength $args] == 1} {
11078         set index [lindex $args 0]
11079         set result [Select $widgets(this) $index]
11080         } else {
11081         error "usage: $w select index"
11082         }
11083     }
11084 
11085     subwidget {
11086         set knownWidgets [list button entry listbox dropdown vsb]
11087         if {[llength $args] == 0} {
11088         return $knownWidgets
11089         }
11090 
11091         set name [lindex $args 0]
11092         if {[lsearch $knownWidgets $name] != -1} {
11093         set result $widgets($name)
11094         } else {
11095         error "unknown subwidget $name"
11096         }
11097     }
11098 
11099     curselection {
11100         set result [eval $doWidgetCommand listbox curselection]
11101     }
11102 
11103     list-insert {
11104         eval $doWidgetCommand listbox insert $args
11105         set result [HandleScrollbar $w "grow"]
11106     }
11107 
11108     list-delete {
11109         eval $doWidgetCommand listbox delete $args
11110         set result [HandleScrollbar $w "shrink"]
11111     }
11112 
11113     toggle {
11114         # ignore this command if the widget is disabled...
11115         if {$options(-state) == "disabled"} return
11116 
11117         # pops down the list if it is not, hides it
11118         # if it is...
11119         if {[winfo ismapped $widgets(dropdown)]} {
11120         set result [$widgets(this) close]
11121         } else {
11122         set result [$widgets(this) open]
11123         }
11124     }
11125 
11126     open {
11127 
11128         # if this is an editable combobox, the focus should
11129         # be set to the entry widget
11130         if {$options(-editable)} {
11131         focus $widgets(entry)
11132         $widgets(entry) select range 0 end
11133         $widgets(entry) icur end
11134         }
11135 
11136         # if we are disabled, we won't allow this to happen
11137         if {$options(-state) == "disabled"} {
11138         return 0
11139         }
11140 
11141         # if there is a -opencommand, execute it now
11142         if {[string length $options(-opencommand)] > 0} {
11143         # hmmm... should I do a catch, or just let the normal
11144         # error handling handle any errors? For now, the latter...
11145         uplevel \#0 $options(-opencommand)
11146         }
11147 
11148         # compute the geometry of the window to pop up, and set
11149         # it, and force the window manager to take notice
11150         # (even if it is not presently visible).
11151         #
11152         # this isn't strictly necessary if the window is already
11153         # mapped, but we'll go ahead and set the geometry here
11154         # since its harmless and *may* actually reset the geometry
11155         # to something better in some weird case.
11156         set geometry [::combobox::ComputeGeometry $widgets(this)]
11157         wm geometry $widgets(dropdown) $geometry
11158         update idletasks
11159 
11160         # if we are already open, there's nothing else to do
11161         if {[winfo ismapped $widgets(dropdown)]} {
11162         return 0
11163         }
11164 
11165         # save the widget that currently has the focus; we'll restore
11166         # the focus there when we're done
11167         set oldFocus [focus]
11168 
11169         # ok, tweak the visual appearance of things and
11170         # make the list pop up
11171         $widgets(button) configure -relief sunken
11172         raise $widgets(dropdown) [winfo parent $widgets(this)]
11173         wm deiconify $widgets(dropdown)
11174         raise $widgets(dropdown)
11175 
11176         # force focus to the entry widget so we can handle keypress
11177         # events for traversal
11178         focus -force $widgets(entry)
11179 
11180         # select something by default, but only if its an
11181         # exact match...
11182         ::combobox::Find $widgets(this) 1
11183 
11184         # save the current grab state for the display containing
11185         # this widget. We'll restore it when we close the dropdown
11186         # list
11187         set status "none"
11188         set grab [grab current $widgets(this)]
11189         if {$grab != ""} {set status [grab status $grab]}
11190         set oldGrab [list $grab $status]
11191         unset grab status
11192 
11193         # *gasp* do a global grab!!! Mom always told me not to
11194         # do things like this, but sometimes a man's gotta do
11195         # what a man's gotta do.
11196         grab -global $widgets(this)
11197 
11198         # fake the listbox into thinking it has focus. This is
11199         # necessary to get scanning initialized properly in the
11200         # listbox.
11201         event generate $widgets(listbox) <B1-Enter>
11202 
11203         return 1
11204     }
11205 
11206     close {
11207         # if we are already closed, don't do anything...
11208         if {![winfo ismapped $widgets(dropdown)]} {
11209         return 0
11210         }
11211 
11212         # restore the focus and grab, but ignore any errors...
11213         # we're going to be paranoid and release the grab before
11214         # trying to set any other grab because we really really
11215         # really want to make sure the grab is released.
11216         catch {focus $oldFocus} result
11217         catch {grab release $widgets(this)}
11218         catch {
11219         set status [lindex $oldGrab 1]
11220         if {$status == "global"} {
11221             grab -global [lindex $oldGrab 0]
11222         } elseif {$status == "local"} {
11223             grab [lindex $oldGrab 0]
11224         }
11225         unset status
11226         }
11227 
11228         # hides the listbox
11229         $widgets(button) configure -relief raised
11230         wm withdraw $widgets(dropdown)
11231 
11232         # select the data in the entry widget. Not sure
11233         # why, other than observation seems to suggest that's
11234         # what windows widgets do.
11235         set editable [::combobox::GetBoolean $options(-editable)]
11236         if {$editable} {
11237         $widgets(entry) selection range 0 end
11238         $widgets(button) configure -relief raised
11239         }
11240 
11241 
11242         # magic tcl stuff (see tk.tcl in the distribution
11243         # lib directory)
11244         ::combobox::tkCancelRepeat
11245 
11246         return 1
11247     }
11248 
11249     cget {
11250         if {[llength $args] != 1} {
11251         error "wrong # args: should be $w cget option"
11252         }
11253         set opt [::combobox::Canonize $w option [lindex $args 0]]
11254 
11255         if {$opt == "-value"} {
11256         set result [$widgets(entry) get]
11257         } else {
11258         set result $options($opt)
11259         }
11260     }
11261 
11262     configure {
11263         set result [eval ::combobox::Configure {$w} $args]
11264     }
11265 
11266     default {
11267         error "bad option \"$command\""
11268     }
11269     }
11270 
11271     return $result
11272 }
11273 
11274 # ::combobox::Configure --
11275 #
11276 #    Implements the "configure" widget subcommand
11277 #
11278 # Arguments:
11279 #
11280 #    w      widget pathname
11281 #    args   zero or more option/value pairs (or a single option)
11282 #
11283 # Results:
11284 #
11285 #    Performs typcial "configure" type requests on the widget
11286 
11287 proc ::combobox::Configure {w args} {
11288     variable widgetOptions
11289     variable defaultEntryCursor
11290 
11291     upvar ::combobox::${w}::widgets widgets
11292     upvar ::combobox::${w}::options options
11293 
11294     if {[llength $args] == 0} {
11295     # hmmm. User must be wanting all configuration information
11296     # note that if the value of an array element is of length
11297     # one it is an alias, which needs to be handled slightly
11298     # differently
11299     set results {}
11300     foreach opt [lsort [array names widgetOptions]] {
11301         if {[llength $widgetOptions($opt)] == 1} {
11302         set alias $widgetOptions($opt)
11303         set optName $widgetOptions($alias)
11304         lappend results [list $opt $optName]
11305         } else {
11306         set optName  [lindex $widgetOptions($opt) 0]
11307         set optClass [lindex $widgetOptions($opt) 1]
11308         set default [option get $w $optName $optClass]
11309         if {[info exists options($opt)]} {
11310             lappend results [list $opt $optName $optClass  $default $options($opt)]
11311         } else {
11312             lappend results [list $opt $optName $optClass  $default ""]
11313         }
11314         }
11315     }
11316 
11317     return $results
11318     }
11319 
11320     # one argument means we are looking for configuration
11321     # information on a single option
11322     if {[llength $args] == 1} {
11323     set opt [::combobox::Canonize $w option [lindex $args 0]]
11324 
11325     set optName  [lindex $widgetOptions($opt) 0]
11326     set optClass [lindex $widgetOptions($opt) 1]
11327     set default [option get $w $optName $optClass]
11328     set results [list $opt $optName $optClass  $default $options($opt)]
11329     return $results
11330     }
11331 
11332     # if we have an odd number of values, bail.
11333     if {[expr {[llength $args]%2}] == 1} {
11334     # hmmm. An odd number of elements in args
11335     error "value for \"[lindex $args end]\" missing"
11336     }
11337 
11338     # Great. An even number of options. Let's make sure they
11339     # are all valid before we do anything. Note that Canonize
11340     # will generate an error if it finds a bogus option; otherwise
11341     # it returns the canonical option name
11342     foreach {name value} $args {
11343     set name [::combobox::Canonize $w option $name]
11344     set opts($name) $value
11345     }
11346 
11347     # process all of the configuration options
11348     # some (actually, most) options require us to
11349     # do something, like change the attributes of
11350     # a widget or two. Here's where we do that...
11351     #
11352     # note that the handling of disabledforeground and
11353     # disabledbackground is a little wonky. First, we have
11354     # to deal with backwards compatibility (ie: tk 8.3 and below
11355     # didn't have such options for the entry widget), and
11356     # we have to deal with the fact we might want to disable
11357     # the entry widget but use the normal foreground/background
11358     # for when the combobox is not disabled, but not editable either.
11359 
11360     set updateVisual 0
11361     foreach option [array names opts] {
11362     set newValue $opts($option)
11363     if {[info exists options($option)]} {
11364         set oldValue $options($option)
11365     }
11366 
11367     switch -- $option {
11368         -background {
11369         set updateVisual 1
11370         set options($option) $newValue
11371         }
11372 
11373         -borderwidth {
11374         $widgets(frame) configure -borderwidth $newValue
11375         set options($option) $newValue
11376         }
11377 
11378         -command {
11379         # nothing else to do...
11380         set options($option) $newValue
11381         }
11382 
11383         -commandstate {
11384         # do some value checking...
11385         if {$newValue != "normal" && $newValue != "disabled"} {
11386             set options($option) $oldValue
11387             set message "bad state value \"$newValue\";"
11388             append message " must be normal or disabled"
11389             error $message
11390         }
11391         set options($option) $newValue
11392         }
11393 
11394         -cursor {
11395         $widgets(frame) configure -cursor $newValue
11396         $widgets(entry) configure -cursor $newValue
11397         $widgets(listbox) configure -cursor $newValue
11398         set options($option) $newValue
11399         }
11400 
11401         -disabledforeground {
11402         set updateVisual 1
11403         set options($option) $newValue
11404         }
11405 
11406         -disabledbackground {
11407         set updateVisual 1
11408         set options($option) $newValue
11409         }
11410 
11411             -dropdownwidth {
11412                 set options($option) $newValue
11413             }
11414 
11415         -editable {
11416         set updateVisual 1
11417         if {$newValue} {
11418             # it's editable...
11419             $widgets(entry) configure  -state normal  -cursor $defaultEntryCursor
11420         } else {
11421             $widgets(entry) configure  -state disabled  -cursor $options(-cursor)
11422         }
11423         set options($option) $newValue
11424         }
11425 
11426         -font {
11427         $widgets(entry) configure -font $newValue
11428         $widgets(listbox) configure -font $newValue
11429         set options($option) $newValue
11430         }
11431 
11432         -foreground {
11433         set updateVisual 1
11434         set options($option) $newValue
11435         }
11436 
11437         -height {
11438         $widgets(listbox) configure -height $newValue
11439         HandleScrollbar $w
11440         set options($option) $newValue
11441         }
11442 
11443         -highlightbackground {
11444         $widgets(frame) configure -highlightbackground $newValue
11445         set options($option) $newValue
11446         }
11447 
11448         -highlightcolor {
11449         $widgets(frame) configure -highlightcolor $newValue
11450         set options($option) $newValue
11451         }
11452 
11453         -highlightthickness {
11454         $widgets(frame) configure -highlightthickness $newValue
11455         set options($option) $newValue
11456         }
11457     
11458         -image {
11459         if {[string length $newValue] > 0} {
11460             $widgets(button) configure -image $newValue
11461         } else {
11462             $widgets(button) configure -image ::combobox::bimage
11463         }
11464         set options($option) $newValue
11465         }
11466 
11467         -maxheight {
11468         # ComputeGeometry may dork with the actual height
11469         # of the listbox, so let's undork it
11470         $widgets(listbox) configure -height $options(-height)
11471         HandleScrollbar $w
11472         set options($option) $newValue
11473         }
11474 
11475         -opencommand {
11476         # nothing else to do...
11477         set options($option) $newValue
11478         }
11479 
11480         -relief {
11481         $widgets(frame) configure -relief $newValue
11482         set options($option) $newValue
11483         }
11484 
11485         -selectbackground {
11486         $widgets(entry) configure -selectbackground $newValue
11487         $widgets(listbox) configure -selectbackground $newValue
11488         set options($option) $newValue
11489         }
11490 
11491         -selectborderwidth {
11492         $widgets(entry) configure -selectborderwidth $newValue
11493         $widgets(listbox) configure -selectborderwidth $newValue
11494         set options($option) $newValue
11495         }
11496 
11497         -selectforeground {
11498         $widgets(entry) configure -selectforeground $newValue
11499         $widgets(listbox) configure -selectforeground $newValue
11500         set options($option) $newValue
11501         }
11502 
11503         -state {
11504         if {$newValue == "normal"} {
11505             set updateVisual 1
11506             # it's enabled
11507 
11508             set editable [::combobox::GetBoolean  $options(-editable)]
11509             if {$editable} {
11510             $widgets(entry) configure -state normal
11511             $widgets(entry) configure -takefocus 1
11512             }
11513 
11514                     # note that $widgets(button) is actually a label,
11515                     # not a button. And being able to disable labels
11516                     # wasn't possible until tk 8.3. (makes me wonder
11517             # why I chose to use a label, but that answer is
11518             # lost to antiquity)
11519                     if {[info patchlevel] >= 8.3} {
11520                         $widgets(button) configure -state normal
11521                     }
11522 
11523         } elseif {$newValue == "disabled"}  {
11524             set updateVisual 1
11525             # it's disabled
11526             $widgets(entry) configure -state disabled
11527             $widgets(entry) configure -takefocus 0
11528                     # note that $widgets(button) is actually a label,
11529                     # not a button. And being able to disable labels
11530                     # wasn't possible until tk 8.3. (makes me wonder
11531             # why I chose to use a label, but that answer is
11532             # lost to antiquity)
11533                     if {$::tcl_version >= 8.3} {
11534                         $widgets(button) configure -state disabled
11535                     }
11536 
11537         } else {
11538             set options($option) $oldValue
11539             set message "bad state value \"$newValue\";"
11540             append message " must be normal or disabled"
11541             error $message
11542         }
11543 
11544         set options($option) $newValue
11545         }
11546 
11547         -takefocus {
11548         $widgets(entry) configure -takefocus $newValue
11549         set options($option) $newValue
11550         }
11551 
11552         -textvariable {
11553         $widgets(entry) configure -textvariable $newValue
11554         set options($option) $newValue
11555         }
11556 
11557         -value {
11558         ::combobox::SetValue $widgets(this) $newValue
11559         set options($option) $newValue
11560         }
11561 
11562         -width {
11563         $widgets(entry) configure -width $newValue
11564         $widgets(listbox) configure -width $newValue
11565         set options($option) $newValue
11566         }
11567 
11568         -xscrollcommand {
11569         $widgets(entry) configure -xscrollcommand $newValue
11570         set options($option) $newValue
11571         }
11572     }   
11573 
11574     if {$updateVisual} {UpdateVisualAttributes $w}
11575     }
11576 }
11577 
11578 # ::combobox::UpdateVisualAttributes --
11579 #
11580 # sets the visual attributes (foreground, background mostly)
11581 # based on the current state of the widget (normal/disabled,
11582 # editable/non-editable)
11583 #
11584 # why a proc for such a simple thing? Well, in addition to the
11585 # various states of the widget, we also have to consider the
11586 # version of tk being used -- versions from 8.4 and beyond have
11587 # the notion of disabled foreground/background options for various
11588 # widgets. All of the permutations can get nasty, so we encapsulate
11589 # it all in one spot.
11590 #
11591 # note also that we don't handle all visual attributes here; just
11592 # the ones that depend on the state of the widget. The rest are
11593 # handled on a case by case basis
11594 #
11595 # Arguments:
11596 #    w      widget pathname
11597 #
11598 # Returns:
11599 #    empty string
11600 
11601 proc ::combobox::UpdateVisualAttributes {w} {
11602 
11603     upvar ::combobox::${w}::widgets     widgets
11604     upvar ::combobox::${w}::options     options
11605 
11606     if {$options(-state) == "normal"} {
11607 
11608     set foreground $options(-foreground)
11609     set background $options(-background)
11610     
11611     } elseif {$options(-state) == "disabled"} {
11612 
11613     set foreground $options(-disabledforeground)
11614     set background $options(-disabledbackground)
11615     }
11616 
11617     $widgets(entry)   configure -foreground $foreground -background $background
11618     $widgets(listbox) configure -foreground $foreground -background $background
11619     $widgets(button)  configure -foreground $foreground
11620     $widgets(vsb)     configure -background $background -troughcolor $background
11621     $widgets(frame)   configure -background $background
11622 
11623     # we need to set the disabled colors in case our widget is disabled.
11624     # We could actually check for disabled-ness, but we also need to
11625     # check whether we're enabled but not editable, in which case the
11626     # entry widget is disabled but we still want the enabled colors. It's
11627     # easier just to set everything and be done with it.
11628 
11629     if {$::tcl_version >= 8.4} {
11630     $widgets(entry) configure  -disabledforeground $foreground  -disabledbackground $background
11631     $widgets(button)  configure -disabledforeground $foreground
11632     $widgets(listbox) configure -disabledforeground $foreground
11633     }
11634 }
11635 
11636 # ::combobox::SetValue --
11637 #
11638 #    sets the value of the combobox and calls the -command,
11639 #    if defined
11640 #
11641 # Arguments:
11642 #
11643 #    w          widget pathname
11644 #    newValue   the new value of the combobox
11645 #
11646 # Returns
11647 #
11648 #    Empty string
11649 
11650 proc ::combobox::SetValue {w newValue} {
11651 
11652     upvar ::combobox::${w}::widgets     widgets
11653     upvar ::combobox::${w}::options     options
11654     upvar ::combobox::${w}::ignoreTrace ignoreTrace
11655     upvar ::combobox::${w}::oldValue    oldValue
11656 
11657     if {[info exists options(-textvariable)]  && [string length $options(-textvariable)] > 0} {
11658     set variable ::$options(-textvariable)
11659     set $variable $newValue
11660     } else {
11661     set oldstate [$widgets(entry) cget -state]
11662     $widgets(entry) configure -state normal
11663     $widgets(entry) delete 0 end
11664     $widgets(entry) insert 0 $newValue
11665     $widgets(entry) configure -state $oldstate
11666     }
11667 
11668     # set our internal textvariable; this will cause any public
11669     # textvariable (ie: defined by the user) to be updated as
11670     # well
11671 #    set ::combobox::${w}::entryTextVariable $newValue
11672 
11673     # redefine our concept of the "old value". Do it before running
11674     # any associated command so we can be sure it happens even
11675     # if the command somehow fails.
11676     set oldValue $newValue
11677 
11678 
11679     # call the associated command. The proc will handle whether or
11680     # not to actually call it, and with what args
11681     CallCommand $w $newValue
11682 
11683     return ""
11684 }
11685 
11686 # ::combobox::CallCommand --
11687 #
11688 #   calls the associated command, if any, appending the new
11689 #   value to the command to be called.
11690 #
11691 # Arguments:
11692 #
11693 #    w         widget pathname
11694 #    newValue  the new value of the combobox
11695 #
11696 # Returns
11697 #
11698 #    empty string
11699 
11700 proc ::combobox::CallCommand {w newValue} {
11701     upvar ::combobox::${w}::widgets widgets
11702     upvar ::combobox::${w}::options options
11703 
11704     # call the associated command, if defined and -commandstate is
11705     # set to "normal"
11706     if {$options(-commandstate) == "normal" &&  [string length $options(-command)] > 0} {
11707     set args [list $widgets(this) $newValue]
11708     uplevel \#0 $options(-command) $args
11709     }
11710 }
11711 
11712 
11713 # ::combobox::GetBoolean --
11714 #
11715 #     returns the value of a (presumably) boolean string (ie: it should
11716 #     do the right thing if the string is "yes", "no", "true", 1, etc
11717 #
11718 # Arguments:
11719 #
11720 #     value       value to be converted
11721 #     errorValue  a default value to be returned in case of an error
11722 #
11723 # Returns:
11724 #
11725 #     a 1 or zero, or the value of errorValue if the string isn't
11726 #     a proper boolean value
11727 
11728 proc ::combobox::GetBoolean {value {errorValue 1}} {
11729     if {[catch {expr {([string trim $value])?1:0}} res]} {
11730     return $errorValue
11731     } else {
11732     return $res
11733     }
11734 }
11735 
11736 # ::combobox::convert --
11737 #
11738 #     public routine to convert %x, %y and %W binding substitutions.
11739 #     Given an x, y and or %W value relative to a given widget, this
11740 #     routine will convert the values to be relative to the combobox
11741 #     widget. For example, it could be used in a binding like this:
11742 #
11743 #     bind .combobox <blah> {doSomething [::combobox::convert %W -x %x]}
11744 #
11745 #     Note that this procedure is *not* exported, but is intended for
11746 #     public use. It is not exported because the name could easily
11747 #     clash with existing commands.
11748 #
11749 # Arguments:
11750 #
11751 #     w     a widget path; typically the actual result of a %W
11752 #           substitution in a binding. It should be either a
11753 #           combobox widget or one of its subwidgets
11754 #
11755 #     args  should one or more of the following arguments or
11756 #           pairs of arguments:
11757 #
11758 #           -x <x>      will convert the value <x>; typically <x> will
11759 #                       be the result of a %x substitution
11760 #           -y <y>      will convert the value <y>; typically <y> will
11761 #                       be the result of a %y substitution
11762 #           -W (or -w)  will return the name of the combobox widget
11763 #                       which is the parent of $w
11764 #
11765 # Returns:
11766 #
11767 #     a list of the requested values. For example, a single -w will
11768 #     result in a list of one items, the name of the combobox widget.
11769 #     Supplying "-x 10 -y 20 -W" (in any order) will return a list of
11770 #     three values: the converted x and y values, and the name of
11771 #     the combobox widget.
11772 
11773 proc ::combobox::convert {w args} {
11774     set result {}
11775     if {![winfo exists $w]} {
11776     error "window \"$w\" doesn't exist"
11777     }
11778 
11779     while {[llength $args] > 0} {
11780     set option [lindex $args 0]
11781     set args [lrange $args 1 end]
11782 
11783     switch -exact -- $option {
11784         -x {
11785         set value [lindex $args 0]
11786         set args [lrange $args 1 end]
11787         set win $w
11788         while {[winfo class $win] != "Combobox"} {
11789             incr value [winfo x $win]
11790             set win [winfo parent $win]
11791             if {$win == "."} break
11792         }
11793         lappend result $value
11794         }
11795 
11796         -y {
11797         set value [lindex $args 0]
11798         set args [lrange $args 1 end]
11799         set win $w
11800         while {[winfo class $win] != "Combobox"} {
11801             incr value [winfo y $win]
11802             set win [winfo parent $win]
11803             if {$win == "."} break
11804         }
11805         lappend result $value
11806         }
11807 
11808         -w -
11809         -W {
11810         set win $w
11811         while {[winfo class $win] != "Combobox"} {
11812             set win [winfo parent $win]
11813             if {$win == "."} break;
11814         }
11815         lappend result $win
11816         }
11817     }
11818     }
11819     return $result
11820 }
11821 
11822 # ::combobox::Canonize --
11823 #
11824 #    takes a (possibly abbreviated) option or command name and either
11825 #    returns the canonical name or an error
11826 #
11827 # Arguments:
11828 #
11829 #    w        widget pathname
11830 #    object   type of object to canonize; must be one of "command",
11831 #             "option", "scan command" or "list command"
11832 #    opt      the option (or command) to be canonized
11833 #
11834 # Returns:
11835 #
11836 #    Returns either the canonical form of an option or command,
11837 #    or raises an error if the option or command is unknown or
11838 #    ambiguous.
11839 
11840 proc ::combobox::Canonize {w object opt} {
11841     variable widgetOptions
11842     variable columnOptions
11843     variable widgetCommands
11844     variable listCommands
11845     variable scanCommands
11846 
11847     switch $object {
11848     command {
11849         if {[lsearch -exact $widgetCommands $opt] >= 0} {
11850         return $opt
11851         }
11852 
11853         # command names aren't stored in an array, and there
11854         # isn't a way to get all the matches in a list, so
11855         # we'll stuff the commands in a temporary array so
11856         # we can use [array names]
11857         set list $widgetCommands
11858         foreach element $list {
11859         set tmp($element) ""
11860         }
11861         set matches [array names tmp ${opt}*]
11862     }
11863 
11864     {list command} {
11865         if {[lsearch -exact $listCommands $opt] >= 0} {
11866         return $opt
11867         }
11868 
11869         # command names aren't stored in an array, and there
11870         # isn't a way to get all the matches in a list, so
11871         # we'll stuff the commands in a temporary array so
11872         # we can use [array names]
11873         set list $listCommands
11874         foreach element $list {
11875         set tmp($element) ""
11876         }
11877         set matches [array names tmp ${opt}*]
11878     }
11879 
11880     {scan command} {
11881         if {[lsearch -exact $scanCommands $opt] >= 0} {
11882         return $opt
11883         }
11884 
11885         # command names aren't stored in an array, and there
11886         # isn't a way to get all the matches in a list, so
11887         # we'll stuff the commands in a temporary array so
11888         # we can use [array names]
11889         set list $scanCommands
11890         foreach element $list {
11891         set tmp($element) ""
11892         }
11893         set matches [array names tmp ${opt}*]
11894     }
11895 
11896     option {
11897         if {[info exists widgetOptions($opt)]  && [llength $widgetOptions($opt)] == 2} {
11898         return $opt
11899         }
11900         set list [array names widgetOptions]
11901         set matches [array names widgetOptions ${opt}*]
11902     }
11903 
11904     }
11905 
11906     if {[llength $matches] == 0} {
11907     set choices [HumanizeList $list]
11908     error "unknown $object \"$opt\"; must be one of $choices"
11909 
11910     } elseif {[llength $matches] == 1} {
11911     set opt [lindex $matches 0]
11912 
11913     # deal with option aliases
11914     switch $object {
11915         option {
11916         set opt [lindex $matches 0]
11917         if {[llength $widgetOptions($opt)] == 1} {
11918             set opt $widgetOptions($opt)
11919         }
11920         }
11921     }
11922 
11923     return $opt
11924 
11925     } else {
11926     set choices [HumanizeList $list]
11927     error "ambiguous $object \"$opt\"; must be one of $choices"
11928     }
11929 }
11930 
11931 # ::combobox::HumanizeList --
11932 #
11933 #    Returns a human-readable form of a list by separating items
11934 #    by columns, but separating the last two elements with "or"
11935 #    (eg: foo, bar or baz)
11936 #
11937 # Arguments:
11938 #
11939 #    list    a valid tcl list
11940 #
11941 # Results:
11942 #
11943 #    A string which as all of the elements joined with ", " or
11944 #    the word " or "
11945 
11946 proc ::combobox::HumanizeList {list} {
11947 
11948     if {[llength $list] == 1} {
11949     return [lindex $list 0]
11950     } else {
11951     set list [lsort $list]
11952     set secondToLast [expr {[llength $list] -2}]
11953     set most [lrange $list 0 $secondToLast]
11954     set last [lindex $list end]
11955 
11956     return "[join $most {, }] or $last"
11957     }
11958 }
11959 
11960 # This is some backwards-compatibility code to handle TIP 44
11961 # (http://purl.org/tcl/tip/44.html). For all private tk commands
11962 # used by this widget, we'll make duplicates of the procs in the
11963 # combobox namespace.
11964 #
11965 # I'm not entirely convinced this is the right thing to do. I probably
11966 # shouldn't even be using the private commands. Then again, maybe the
11967 # private commands really should be public. Oh well; it works so it
11968 # must be OK...
11969 foreach command {TabToWindow CancelRepeat ListboxUpDown} {
11970     if {[llength [info commands ::combobox::tk$command]] == 1} break;
11971 
11972     set tmp [info commands tk$command]
11973     set proc ::combobox::tk$command
11974     if {[llength [info commands tk$command]] == 1} {
11975         set command [namespace which [lindex $tmp 0]]
11976         proc $proc {args} "uplevel $command \$args"
11977     } else {
11978         if {[llength [info commands ::tk::$command]] == 1} {
11979             proc $proc {args} "uplevel ::tk::$command \$args"
11980         }
11981     }
11982 }
11983 
11984 # end of combobox.tcl
11985 }
11986 
11987 ret  {combobox_lim} (type window , optional loclist ={) {cmdproc {}} {cb_textvar c_var} {cb_width 15} {cb_opt 0} {lim 1} args} {
11988 /* ****combobox_lim*/
11989 /* */
11990 /* */
11991 /* */
11992 /* ***/
11993 /*    window : nom du combobox*/
11994 /*    procedure : procedure pour creer la liste de choix*/
11995 /*    cmdproc : commande executee a chaque nouveau choix (passe aussi le nom du combo)*/
11996 /*    cb_textvar : nom de la variable*/
11997 /*    cb_width taille en char du combo*/
11998 /*    cb_opt : valeur par defaut*/
11999 /*    lim : 1 saisie limitée (par defaut) -1 sinon*/
12000 /*    args : arguments de la procedure cmdproc*/
12001 
12002 catch {namespace import combobox::*}
12003      edita =  1
12004     if {$lim == 1} { edita =  0}
12005     global $cb_textvar
12006      $cb = _textvar $cb_opt
12007     combobox $window -command $cmdret  -editable $edita -textvariable $cb_textvar -width $cb_width -background white
12008     # eval $window list insert end $loclist
12009     foreach elem $loclist (
12010         $type window , type list , type insert , type end $, type elem
12011     )
12012     # Sélectionne la valeur initiale dans la liste
12013     set indice [lsearch $loclist $cb_opt]
12014     if {$indice != -1} { $window select $indice }
12015 }
12016 
12017 ret  {comm_add} (type win , type i_, type source , type i_, type target , type exch_, type per_, type unit , type exch_, type per_, type val , type lag , type src_, type time_, type op , type src_, type add_, type val , type src_, type mult_, type val , type dbg_, type source , type stat_, type source_, type mask , type stat_, type source_, type nmsk , type stat_, type source_, type all , type remail , type tgt_, type time_, type op , type tgt_, type add_, type val , type tgt_, type mult_, type val , type dbg_, type cible , type stat_, type cible_, type mask , type stat_, type cible_, type nmsk , type stat_, type cible_, type all , type file_, type packing , type file_, type scaling , type file_, type adding , type file_, type fill_, type value) {
12018 #****  comm_add - add a new connexion
12019 
12020     # Variables globales
12021     global dico_applis dico_caract_fichiers
12022     global dico_caract_cnx
12023     global dico_caract_points
12024     global liste_cnx_in dico_cnx_out
12025 
12026     # Var. de saisie des options de remaillage
12027     global nbr_neighbours_m bi3_method_m if_masked_m norm_method_m norm_near_nei_m
12028     # Var globale indiquant que l'utilisateur a validé sa saisie des options de remaillage
12029     global valid_remail
12030 
12031     # Vérifie les informations entrées
12032     if { [comm_validation $exch_per_unit $exch_per_val $remail] } {
12033         return error
12034     }
12035 
12036     # Extrait les infos pertinentes
12037     # -----------------------------
12038 
12039     # Nom de l'application du composant source
12040     set appli_source $dico_caract_points($i_source.appli)
12041     # Nom du composant source
12042     set comp_source $dico_caract_points($i_source.comp)
12043     # Nom du champ source
12044     set champ_source $dico_caract_points($i_source.champ)
12045 
12046     # Nom de l'application du composant cible
12047     set appli_cible $dico_caract_points($i_target.appli)
12048     # Nom du composant cible
12049     set comp_cible $dico_caract_points($i_target.comp)
12050     # Nom du champ cible
12051     set champ_cible $dico_caract_points($i_target.champ)
12052 
12053     # Mémorise la nouvelle connexion
12054     # ------------------------------
12055 
12056     # 1) Au niveau des points de couplage
12057     # -----------------------------------
12058 
12059     # 1.a) Point de couplage cible
12060     # Mémorise que le point cible est connecté
12061     set dico_caract_points($i_target.lcnx) "i"
12062 
12063     # 1.b) Point de couplage source
12064     # Le point source peut avoir plusieurs connexions
12065     # Liste des connexions sortantes déjà existantes
12066     set l_cnx_out ""
12067     if { [info exists dico_caract_points($i_source.lcnx)] } {
12068         set l_cnx_out $dico_caract_points($i_source.lcnx)
12069     }
12070 
12071     # Crée un nouvel id pour cette nouvelle connexion sortante
12072     # Il est formé de la lettre "o" suivie d'un numéro
12073     # On prend le dernier numéro utilisé plus 1
12074     set dernier_id [lindex $l_cnx_out end]
12075     if { $dernier_id != "" } {
12076         set num_dernier_id [string range $dernier_id 1 end]
12077     } else {
12078         set num_dernier_id 0
12079     }
12080     incr num_dernier_id
12081     set id_new_cnx_out "o$num_dernier_id"
12082 
12083     # Met à jour la liste des identifiants de connexion pour le point source
12084     lappend dico_caract_points($i_source.lcnx) $id_new_cnx_out
12085 
12086     # 2) Dans les listes des connexions
12087     # ---------------------------------
12088 
12089     # Clef (index) de la connexion sortante
12090     set clef_new_cnx_out $i_source$num_dernier_id
12091 
12092     # 2.a) la liste des connexions entrantes
12093     lappend liste_cnx_in $i_target
12094 
12095     # 2.b) le dico des connexions sortantes
12096     set dico_cnx_out($clef_new_cnx_out) $i_target
12097 
12098     # 2.c) la liste des connexions avec leurs caractéristiques
12099     set dico_caract_cnx($i_target.app_source)  $appli_source
12100     set dico_caract_cnx($i_target.app_cible)   $appli_cible
12101     set dico_caract_cnx($i_target.comp_source) $comp_source
12102     set dico_caract_cnx($i_target.comp_cible)  $comp_cible
12103     set dico_caract_cnx($i_target.champ_source) $champ_source
12104     set dico_caract_cnx($i_target.champ_cible)  $champ_cible
12105     set dico_caract_cnx($i_target.pt_source) $id_new_cnx_out
12106 
12107     # Enlever le 's' de pluriel aux unités de temps de la période des échanges
12108     set exch_per_unit [string range $exch_per_unit 0 end-1]
12109 
12110     # Réinterprète le champ "type de remaillage"
12111     # ------------------------------------------
12112 
12113     # Liste de descriptions lisibles et de mots-clefs correspondants
12114     array set remaillages { None {} {3D Nearest neighbour} nneighbour3D {3D Trilinear} trilinear {2D Nearest neighbour} nneighbour2D
12115         {2D Bilinear} bilinear {2D Bicubic} bicubic {2D Conservativ} conservativ2D }
12116 
12117     # Transforme le mot-clef en description lisible
12118     if { $remail != "" } { set remail $remaillages($remail) }
12119 
12120     # Réinterprète le champ "source time operation"
12121     array set mots_clefs { None {} {Time average} taverage {Time accumulation} accumul }
12122     if { $src_time_op != "" } { set src_time_op $mots_clefs($src_time_op) }
12123 
12124     # Réinterprète le champ "target time operation"
12125     array unset mots_clefs
12126     array set mots_clefs { {None (Closest time)} {} {Time interpolation} time_linear }
12127     if { $tgt_time_op != "" } { set tgt_time_op $mots_clefs($tgt_time_op) }
12128 
12129     # Réinterprète les booléens
12130     # Pour tous les champs booléens
12131     foreach nom_champ { dbg_source dbg_cible } {
12132         set valeur [set $nom_champ]
12133         if { $valeur != "" } {       
12134             if {$valeur == "yes"} {set $nom_champ true} else {set $nom_champ false}
12135         }
12136     }
12137 
12138     # Enregistre les attributs de la connexion
12139     # ----------------------------------------
12140 
12141     # Initialise avec valeurs par defaut
12142     set dico_caract_cnx($i_target.src_time_op) ""
12143     set dico_caract_cnx($i_target.src_add_val)  0
12144     set dico_caract_cnx($i_target.src_mult_val) 1
12145     set dico_caract_cnx($i_target.tgt_time_op) ""
12146     set dico_caract_cnx($i_target.tgt_add_val) 0
12147     set dico_caract_cnx($i_target.tgt_mult_val) 1
12148     set dico_caract_cnx($i_target.remail) ""
12149 
12150     foreach attrib {exch_per_unit exch_per_val lag dbg_source dbg_cible src_time_op src_add_val src_mult_val
12151                     remail tgt_time_op tgt_add_val tgt_mult_val
12152                     stat_source_mask stat_source_nmsk stat_source_all stat_cible_mask stat_cible_nmsk stat_cible_all} {
12153         set value [set $attrib]
12154         if { $value != "" } { set dico_caract_cnx($i_target.$attrib) $value }
12155     }
12156 
12157     # Enregistre les options de remaillage
12158     global options_remail
12159     if { $remail != "" } {
12160         # Réinterprète l'option booléenne norm_near_nei
12161         if {$options_remail(norm_near_nei) == "yes"} {set options_remail(norm_near_nei) true} else {set options_remail(norm_near_nei) false}
12162         # Traduit l'option de remaillage conservatif
12163         switch $options_remail(norm_method) {
12164             "Fractional area"  { set options_remail(norm_method) fracarea }
12165             "Destination area" { set options_remail(norm_method) destarea }
12166         }
12167         foreach option {nbr_neighbours bi3_method if_masked norm_method norm_near_nei} {
12168             set dico_caract_cnx($i_target.$option) $options_remail($option)
12169         }
12170     }
12171     array unset options_remail
12172 
12173     # Si la cible est un fichier NetCDF
12174     if { [string equal $appli_cible "///file///"] } {
12175         # Enregistre les attributs du champ du fichier NetCDF
12176         set dico_caract_fichiers($comp_cible.field.packing) $file_packing 
12177         set dico_caract_fichiers($comp_cible.field.scaling) $file_scaling 
12178         set dico_caract_fichiers($comp_cible.field.adding)  $file_adding 
12179         set dico_caract_fichiers($comp_cible.field.fill_value) $file_fill_value
12180         
12181         # Maintenant qu'il est connecté, le champ du fichier a un type numérique connu
12182         set dico_caract_fichiers($comp_cible.field.type) $dico_applis(app.$appli_source.comp.$comp_source.champ.$champ_source.datatype)
12183     }    
12184 
12185     catch {destroy $win}
12186 }
12187 
12188 ret  {comm_delete} (type clef_, type cnx) {
12189 #** suppression d'une  communication
12190 #
12191     global dico_caract_cnx
12192 
12193     set appli_source $dico_caract_cnx($clef_cnx.app_source)
12194     set appli_cible $dico_caract_cnx($clef_cnx.app_cible)
12195     set comp_source $dico_caract_cnx($clef_cnx.comp_source)
12196     set comp_cible  $dico_caract_cnx($clef_cnx.comp_cible)
12197     
12198     # Remove from memory
12199     comm_remove $clef_cnx
12200     
12201     # Redessine les composants source et cible
12202     catch {Draw_one_unit $appli_source.$comp_source}
12203     catch {Draw_one_unit $appli_cible.$comp_cible}
12204 
12205     # Redessine les connexions des composants source et cible
12206     Draw_comm $appli_source.$comp_source
12207     Draw_comm $appli_cible.$comp_cible
12208 
12209     # Affiche la liste des communications
12210     control_entity CONNECTION
12211 }
12212 
12213 ret  {comm_edit} (type clef_, type cnx) {
12214 #** edition d'une  communications
12215 #
12216     global dico_caract_cnx dico_applis
12217     global viewcommcheck
12218 
12219     # Extrait les infos pertinentes
12220     # -----------------------------
12221 
12222     # Crée une variable pour chaque champ
12223     foreach nom_champ { lag src_time_op src_add_val src_mult_val dbg_source stat_source_mask stat_source_nmsk stat_source_all remail tgt_time_op tgt_add_val tgt_mult_val dbg_cible stat_cible_mask stat_cible_nmsk stat_cible_all } {
12224         set $nom_champ ""
12225     }
12226     
12227     # Extrait tous les champs attributs de la connexion
12228     foreach {name value} [array get dico_caract_cnx $clef_cnx.*] {
12229         set nom_attribut [string range $name [string length $clef_cnx.] end]
12230         # Crée une variable pour chaque attribut
12231         set $nom_attribut $value
12232     }
12233 
12234     # Clef d'accès au champ de couplage source
12235     set clef_champ_source app.$app_source.comp.$comp_source.champ.$champ_source.o
12236     # Minimal period
12237     set period_source [array get dico_applis $clef_champ_source.nbr_*]
12238     set unite_period_source [string range  [lindex $period_source 0] [string length $clef_champ_source.nbr_] end]
12239     set qte_period_source [lindex $period_source 1]
12240     set scatter ""
12241     catch {set scatter $dico_applis($clef_champ_source.scatter)}
12242     # CF name (transient standard name) du champ de couplage source
12243     set std_name_source ""
12244     catch {set std_name_source $dico_applis(app.$app_source.comp.$comp_source.champ.$champ_source.std_name)}
12245 
12246     # Clef d'accès au champ de couplage cible
12247     set clef_champ_cible app.$app_cible.comp.$comp_cible.champ.$champ_cible.i
12248     # Minimal period
12249     set period_cible [array get dico_applis $clef_champ_cible.nbr_*]
12250     set unite_period_cible [string range  [lindex $period_cible 0] [string length $clef_champ_cible.nbr_] end]
12251     set qte_period_cible [lindex $period_cible 1]
12252     set gather ""
12253     catch {set gather $dico_applis($clef_champ_cible.gather)}
12254     # CF name (transient standard name) du champ de couplage cible
12255     set std_name_cible ""
12256     catch {set std_name_cible $dico_applis(app.$app_cible.comp.$comp_cible.champ.$champ_cible.std_name)}
12257 
12258     # Convertir les unites de temps
12259     #   de : secs, mins, hours, days, months, years
12260     #    a : seconds, minutes, hours, days, months, years
12261     array set conversion { {} {} secs seconds mins minutes hours hours days days months months years years }
12262     set unite_period_source $conversion($unite_period_source)
12263     set unite_period_cible  $conversion($unite_period_cible)
12264 
12265     # Ajouter un 's' de pluriel aux unités de temps de la période des échanges
12266     set exch_per_unit ${exch_per_unit}s
12267 
12268     # Réinterprèter le champ "type de remaillage"
12269     # ------------------------------------------
12270 
12271     # Liste de mots-clefs et de descriptions lisibles correspondantes
12272     array set remaillages { {} None nneighbour3D {3D Nearest neighbour} trilinear {3D Trilinear} nneighbour2D {2D Nearest neighbour}
12273         bilinear {2D Bilinear} bicubic {2D Bicubic} conservativ2D {2D Conservativ} }
12274 
12275     # Transforme le mot-clef en description lisible
12276     set remail $remaillages($remail)
12277 
12278     # Réinterprète le champ "source time operation"
12279     array set mots_clefs { {} None taverage {Time average} accumul {Time accumulation} }
12280     set src_time_op $mots_clefs($src_time_op)
12281 
12282     # Réinterprète le champ "target time operation"
12283     array unset mots_clefs
12284     array set mots_clefs { {} {None (closest time)} time_nneighbour {None (closest time)} time_linear {Time interpolation} }
12285     set tgt_time_op $mots_clefs($tgt_time_op)
12286 
12287     # Réinterprète les booléens
12288     # Pour tous les champs booléens
12289     foreach nom_champ { dbg_source dbg_cible } {
12290         set valeur [set $nom_champ]
12291         if {$valeur == "true"} {set $nom_champ yes} else {set $nom_champ no}
12292     }
12293     
12294     if { $remail != "None" } {
12295         if {$norm_near_nei == "true"} {set norm_near_nei yes} else {set norm_near_nei no}
12296         # Traduit l'option de remaillage conservatif
12297         switch $norm_method {
12298             fracarea { set norm_method "Fractional area" }
12299             destarea { set norm_method "Destination area" }
12300         }
12301     }
12302     
12303     # Crée la boite de dialogue de saisie de tous les paramètres d'une connexion
12304     if { $remail != "None" } {
12305         # Passe tous les parametres, avec les options de remaillage
12306         comm_saisie 0 $clef_cnx $comp_source $champ_source $std_name_source $qte_period_source $unite_period_source $comp_cible $champ_cible $std_name_cible $qte_period_cible $unite_period_cible $scatter $gather $exch_per_unit $exch_per_val $lag $src_time_op $src_add_val $src_mult_val $dbg_source $stat_source_mask $stat_source_nmsk $stat_source_all $remail $tgt_time_op $tgt_add_val $tgt_mult_val $dbg_cible $stat_cible_mask $stat_cible_nmsk $stat_cible_all $nbr_neighbours $bi3_method $if_masked $norm_method $norm_near_nei
12307     } else {
12308         # Passe tous les parametres sans les options de remaillage
12309         comm_saisie 0 $clef_cnx $comp_source $champ_source $std_name_source $qte_period_source $unite_period_source $comp_cible $champ_cible $std_name_cible $qte_period_cible $unite_period_cible $scatter $gather $exch_per_unit $exch_per_val $lag $src_time_op $src_add_val $src_mult_val $dbg_source $stat_source_mask $stat_source_nmsk $stat_source_all $remail $tgt_time_op $tgt_add_val $tgt_mult_val $dbg_cible $stat_cible_mask $stat_cible_nmsk $stat_cible_all
12310     }
12311     # Remarque : si la connexion a pour source ou cible un fichier, beaucoup des paramètres passés 
12312     #            à la procedure com_saisie() sont inutilisés
12313     #            De plus, si la connexion a un fichier pour cible, certains paramètres du fichier
12314     #            ne sont pas passés explicitement mais par l'intermédiaire du param. "clef_cnx"
12315 
12316     catch {tkwait window .edit_comm}
12317     # Si les connexions sont dessinées
12318     if {$viewcommcheck != 0} {
12319         # Redessine toutes les cnx allant au composant cible (dont celle qu'on vient de modifier)
12320         Draw_comm $app_cible.$comp_cible
12321     }
12322     # Si la cible est un fichier NetCDF
12323     if { $app_cible == "///file///" } {
12324         # Redessine le fichier car des attributs qui sont affichés dans une info-bulle ont pu changer
12325         Draw_one_unit $app_cible.$comp_cible
12326     }
12327     
12328     # Affiche la liste des connexions et sélectionne celle-ci
12329     control_entity CONNECTION $clef_cnx
12330 }
12331 
12332 ret  {comm_insert} (type i_, type source , type i_, type target) {
12333 # Crée une nouvelle connexion entre deux composants
12334 
12335     # Variables globales
12336     global dico_caract_points dico_applis
12337 
12338     # Extrait les infos pertinentes
12339     # -----------------------------
12340 
12341     # Appli du composant source
12342     set appli_source $dico_caract_points($i_source.appli)
12343     # Nom du composant source
12344     set comp_source $dico_caract_points($i_source.comp)
12345     # Nom du champ source
12346     set champ_source $dico_caract_points($i_source.champ)
12347     
12348     # Si la source est un fichier NetCDF
12349     if { $appli_source == "///file///"} {
12350         set unite_period_source ""
12351         set qte_period_source ""
12352         set scatter ""
12353         set std_name_source ""
12354     } else {
12355         # Clef d'accès au champ de couplage source
12356         set clef_champ_source app.$appli_source.comp.$comp_source.champ.$champ_source.o
12357         # Minimal period
12358         set period_source [array get dico_applis $clef_champ_source.nbr_*]
12359         set unite_period_source [string range  [lindex $period_source 0] [string length $clef_champ_source.nbr_] end]
12360         set qte_period_source [lindex $period_source 1]
12361         set scatter $dico_applis($clef_champ_source.scatter)
12362         # CF name (transient standard name) du champ de couplage source
12363         catch {set std_name_source $dico_applis(app.$appli_source.comp.$comp_source.champ.$champ_source.std_name)}
12364     }
12365     
12366     # Appli du composant cible
12367     set appli_cible $dico_caract_points($i_target.appli)
12368     # Nom du composant cible
12369     set comp_cible $dico_caract_points($i_target.comp)
12370     # Nom du champ cible
12371     set champ_cible $dico_caract_points($i_target.champ)
12372     
12373     # Si la cible est un fichier NetCDF
12374     if { $appli_cible == "///file///"} {
12375         set unite_period_cible ""
12376         set qte_period_cible ""
12377         set gather ""
12378         set std_name_cible ""
12379     } else {
12380         # Clef d'accès au champ de couplage cible
12381         set clef_champ_cible app.$appli_cible.comp.$comp_cible.champ.$champ_cible.i
12382         # Minimal period
12383         set period_cible [array get dico_applis $clef_champ_cible.nbr_*]
12384         set unite_period_cible [string range  [lindex $period_cible 0] [string length $clef_champ_cible.nbr_] end]
12385         set qte_period_cible [lindex $period_cible 1]
12386         set gather $dico_applis($clef_champ_cible.gather)
12387         # CF name (transient standard name) du champ de couplage cible
12388         catch {set std_name_cible $dico_applis(app.$appli_cible.comp.$comp_cible.champ.$champ_cible.std_name)}
12389     }
12390 
12391     # Convertit les unites de temps
12392     #   de : secs, mins, hours, days, months, years
12393     #    a : seconds, minutes, hours, days, months, years
12394     array set conversion { secs seconds mins minutes hours hours days days months months years years }
12395     if { $unite_period_source != "" } {
12396         set unite_period_source $conversion($unite_period_source)
12397     }
12398     if { $unite_period_cible != "" } {
12399         set unite_period_cible  $conversion($unite_period_cible)
12400     }
12401 
12402     # Crée la boite de dialogue de saisie de tous les paramètres d'une connexion
12403     comm_saisie 1 "$i_source $i_target" $comp_source $champ_source $std_name_source $qte_period_source $unite_period_source $comp_cible $champ_cible $std_name_cible $qte_period_cible $unite_period_cible $scatter $gather
12404 }
12405 
12406 ret  {comm_new} (type flag , type point_, type index) {
12407 #
12408     global oldput oldget
12409     global oldcolorput oldcolorget
12410     global i_source i_target
12411     global dico_cnx_out
12412     global dico_caract_cnx
12413     global dico_caract_points
12414     global entityselected objectselected tagselected oldcolor
12415 
12416     set w .gui.pr.cpd22.03
12417 
12418     #** annulation
12419     #-------------
12420     if { $flag == "menage" } {
12421         # S'il y avait un point d'entrée sélectionné
12422         if { [info exists oldget ]} {
12423             $w itemconfigure $oldget -fill $oldcolorget
12424             # Désélectionne et oublie ce point
12425             unset oldget
12426             unset i_target
12427         }
12428         # S'il y avait un point de sortie sélectionné
12429         if { [info exists oldput ]} {
12430             $w itemconfigure $oldput -fill $oldcolorput
12431             # Désélectionne et oublie ce point
12432             unset oldput
12433             unset i_source
12434         }
12435     } else {
12436 
12437         # Si il y avait un objet sélectionné
12438         if {$objectselected != ""} {
12439             # Déselectionne cet obet sur le graphe
12440             $w itemconfigure $tagselected -fill $oldcolor
12441             set tagselected ""
12442             set objectselected ""
12443         }
12444 
12445         #** connexion sortante d'un composant
12446         #------------------------------------
12447         if { $flag == "out" } {
12448 
12449             if { [info exists oldput ]} { $w itemconfigure $oldput -fill $oldcolorput}
12450 
12451             # Si on a cliqué sur le point sélectionné
12452             set point [$w find withtag current]
12453             if { [info exists oldput ] && $oldput == $point } {
12454                 # Désélectionne et oublie ce point
12455                 unset oldput
12456                 unset i_source
12457             } else {
12458                 # Mémorise le point sélectionné et sa couleur d'origine
12459                 set oldput $point
12460                 set oldcolorput [$w itemcget $oldput -fill ]
12461                 # Change la couleur du point sélectionné
12462                 $w itemconfigure current -fill red
12463                 # Mémorise le point de couplage source (de la future connexion)
12464                 set i_source $point_index
12465             }
12466         }
12467 
12468         #** connexion entrante dans un composant
12469         #---------------------------------------
12470         if { $flag == "in" } {
12471             # Si le point cible n'est pas déjà connecté
12472             set lcnx ""
12473             catch {set lcnx $dico_caract_points($point_index.lcnx)}
12474             if { $lcnx == "" } {
12475     
12476                 # S'il y avait déjà un point sélectionné
12477                 if { [info exists oldget ]} {
12478                     $w itemconfigure $oldget -fill $oldcolorget
12479                 }
12480                 # Si on a cliqué sur le point sélectionné
12481                 set point [$w find withtag current]
12482                 if { [info exists oldget ] && $oldget == $point } {
12483                     # Désélectionne et oublie ce point
12484                     unset oldget
12485                     unset i_target
12486                 } else {
12487                     # Mémorise le point sélectionné et sa couleur d'origine
12488                     set oldget $point
12489                     set oldcolorget [$w itemcget $oldget -fill ]
12490                     # Change la couleur du point sélectionné
12491                     $w itemconfigure current -fill red
12492                     # Mémorise le point de couplage source (de la future connexion)
12493                     set i_target $point_index
12494                 }
12495             }
12496         }
12497     }
12498 
12499     # Si un point source et un point cible sont sélectionnés
12500     if { [info exists i_source ] && [info exists i_target ] } {
12501 
12502         set si_annule 0
12503         # Nom de l'application du composant source
12504         set appli_source $dico_caract_points($i_source.appli)
12505         # Nom de l'application du composant cible
12506         set appli_cible $dico_caract_points($i_target.appli)
12507         
12508         # Si la source et la cible sont tous deux des fichiers NetCDF
12509         if { $appli_source == "///file///" && $appli_cible == "///file///" } {
12510             # On ne peut pas créer une telle connexion
12511             set si_annule 1
12512         } else {
12513     
12514             # On saisit les paramètres de la connexion
12515             comm_insert $i_source $i_target
12516             tkwait window .edit_comm
12517             # A ce stade, l'utilisateur peut avoir annulé la creation de la connexion
12518     
12519             # Si le point cible est maintenant connecté
12520             if { [info exists dico_caract_points($i_target.lcnx)] } {
12521     
12522                 # Nom du composant source
12523                 set comp_source $dico_caract_points($i_source.comp)
12524                 # Nom du composant cible
12525                 set comp_cible $dico_caract_points($i_target.comp)
12526     
12527                 # Redessine les composants source et cible
12528                 catch {Draw_one_unit $appli_source.$comp_source}
12529                 catch {Draw_one_unit $appli_cible.$comp_cible}
12530     
12531                 # Redessine les connexions du cible
12532                 # Draw_comm $appli_source.$comp_source
12533                 Draw_comm $appli_cible.$comp_cible
12534     
12535                 # Affiche la liste des communications en sélectionnant la dernière créée
12536                 # et annule la selection des points de couplage
12537                 minimenu_comm $i_target
12538             } else {
12539                 # L'utilisateur a annulé la creation de la connexion
12540                 set si_annule 1
12541             }
12542         }
12543             
12544         if { $si_annule } {
12545             # On annule la selection du dernier point de couplage
12546             if { $flag == "in" } {
12547                 # Désélectionne et oublie ce point
12548                 $w itemconfigure $oldget -fill $oldcolorget
12549                 unset oldget
12550                 unset i_target
12551             } else {
12552                 # Désélectionne et oublie ce point
12553                 $w itemconfigure $oldput -fill $oldcolorput
12554                 unset oldput
12555                 unset i_source
12556             }
12557         }
12558     }
12559 }
12560 
12561 ret  {comm_remove} (type clef_, type cnx) {
12562 # Remove from memory one connexion between two component or between one component and one file
12563     global dico_caract_cnx
12564     global dico_caract_points
12565     global liste_cnx_in dico_cnx_out
12566 
12567     # Extrait les infos pertinentes
12568     # -----------------------------
12569 
12570     set appli_source $dico_caract_cnx($clef_cnx.app_source)
12571     set appli_cible $dico_caract_cnx($clef_cnx.app_cible)
12572     set comp_source $dico_caract_cnx($clef_cnx.comp_source)
12573     set comp_cible  $dico_caract_cnx($clef_cnx.comp_cible)
12574     set champ_source $dico_caract_cnx($clef_cnx.champ_source)
12575     set champ_cible  $dico_caract_cnx($clef_cnx.champ_cible)
12576     # L'id (la clef) de la connexion est celui du point de couplage cible
12577     set id_point_in $clef_cnx
12578     # Recherche le numéro du point de couplage source (o1 ou o2, o3,...)
12579     set num_point_out $dico_caract_cnx($clef_cnx.pt_source)
12580     # Id du point de couplage source
12581     set id_point_out $appli_source.$comp_source.$champ_source.o
12582 
12583     # Supprime la memoire de la connexion
12584     # -----------------------------------
12585 
12586     # 1) Au niveau des points de couplage
12587     # -----------------------------------
12588 
12589     # 1.a) Point de couplage cible
12590     # Mémorise que le point cible n'est plus connecté
12591     unset dico_caract_points($id_point_in.lcnx)
12592 
12593     # 1.b) Point de couplage source
12594     # Le point source peut avoir plusieurs connexions
12595     # Liste de toutes les connexions sortantes
12596     set l_cnx_out $dico_caract_points($id_point_out.lcnx)
12597     # Supprime de la liste la connexion en question
12598     set dico_caract_points($id_point_out.lcnx) [lsearch -not -all -inline $l_cnx_out $num_point_out]
12599 
12600     # 2) Des listes des connexions
12601     # ----------------------------
12602 
12603     # 2.a) la liste des connexions sortantes
12604     set clef_cnx_out $appli_source.$comp_source.$champ_source.$num_point_out  
12605     unset dico_cnx_out($clef_cnx_out)
12606 
12607     # 2.b) la liste des connexions entrantes
12608     set liste_cnx_in [lsearch -not -all -inline $liste_cnx_in $clef_cnx]
12609 
12610     # 2.c) la liste des connexions avec leurs caractéristiques
12611     array unset dico_caract_cnx $clef_cnx.*
12612 }
12613 
12614 ret  {comm_saisie} (type si_, type nouveau , type clef_, type cnx , type comp_, type source , type champ_, type source , type std_, type name_, type source , type qte_, type period_, type source , type unite_, type period_, type source , type comp_, type cible , type champ_, type cible , type std_, type name_, type cible , type qte_, type period_, type cible , type unite_, type period_, type cible , type scatter , type gather , optional exch_per_unit ={) {exch_per_val {}} {lag {}} {src_time_op None} {src_add_val 0} {src_mult_val 1} {dbg_source no} {stat_source_mask off} {stat_source_nmsk off} {stat_source_all off} {remail None} {tgt_time_op {None (Closest time)}} {tgt_add_val 0} {tgt_mult_val 1} {dbg_cible no} {stat_cible_mask off} {stat_cible_nmsk off} {stat_cible_all off} {nbr_neighbours {}} {bi3_ret  Choose...} (type if_, type masked , optional No =value) {norm_method None} {norm_near_nei no}} {
12615 /*  Crée la boite de dialogue permettant de choisir tous les paramètres d'une connexion*/
12616 /*    - si_nouveau  :   indique si on crée une nouvelle connexion ou si on en modifie une existante*/
12617 /*    - clef_cnx    :   si création, paire de clef des points de couplage source et cible*/
12618 /*                      sinon clef d'acces a une connexion existante*/
12619 /* */
12620     global dico_caract_points dico_caract_cnx
12621     global dico_caract_fichiers
12622     
12623     /*  Variables globales utilisées pour la saisie*/
12624     foreach var_name { exch_per_unit_m exch_per_val_m lag_m src_time_op_m src_add_val_m src_mult_val_m scatter_m dbg_source_m stat_cible_mask_m stat_cible_nmsk_m stat_cible_all_m remail_m tgt_time_op_m tgt_add_val_m tgt_mult_val_m gather_m dbg_cible_m stat_cible_mask_m stat_cible_nmsk_m stat_cible_all_m file_packing_m file_scaling_m file_adding_m file_fill_value_m } {
12625         global $var_name
12626          $var = _name ""
12627     }
12628     
12629     /* =====================================================================*/
12630     /* ===                     Crée une boite de dialogue*/
12631     /* =====================================================================*/
12632      u =  .edit_comm
12633     catch {destroy $u}
12634     toplevel $u
12635     if {$si_nouveau} {
12636         wm title $u "Insert a connection"
12637         wm iconname $u "Insert_Cnx"
12638         /*  Détermine les points de couplage source et cible*/
12639          i = _source [lindex $clef_cnx 0]
12640          i = _target [lindex $clef_cnx 1]
12641         /*  Appli du composant source*/
12642          appli = _source $dico_caract_points($i_source.appli)
12643         /*  Appli du composant cible*/
12644          appli = _cible $dico_caract_points($i_target.appli)
12645         /*  Définit la commande  à exécuter lors de la validation de saisie*/
12646         /*    --> ajout d'une nouvelle connexion*/
12647          on = _OK "comm_add $u $i_source $i_target"
12648     } else {
12649         wm title $u "Change connection properties"
12650         wm iconname $u "change_cnx"
12651         /*  Appli du composant source*/
12652          appli = _source $dico_caract_cnx($clef_cnx.app_source)
12653         /*  Appli du composant cible*/
12654          appli = _cible $dico_caract_cnx($clef_cnx.app_cible)
12655         /*  Définit la commande à exécuter lors de la validation de saisie*/
12656         /*    --> mise à jour des paramètres de la connexion*/
12657          on = _OK "comm_update $u $clef_cnx"
12658     }
12659     
12660     /*  Determine si la source ou la cible est un fichier NetCDF*/
12661      si = _source_fichier [string equal $appli_source "///file///"]
12662      si = _cible_fichier [string equal $appli_cible "///file///"]
12663     
12664     /*  Si la période des échanges n'est pas renseignée*/
12665     if {$exch_per_unit == ""} {
12666         
12667         /*  Initialise la période des échanges*/
12668         /*  ----------------------------------*/
12669         
12670         /*  Si connexion d'un composant avec un fichier NetCDF*/
12671         if {$si_source_fichier || $si_cible_fichier} {
12672             if {$si_source_fichier} {
12673                 /*  La période des échanges est dictée par la cible*/
12674                  exch = _per_unit $unite_period_cible
12675                  exch = _per_val  $qte_period_cible
12676             } else {
12677                 /*  La période des échanges est dictée par la source*/
12678                  exch = _per_unit $unite_period_source
12679                  exch = _per_val  $qte_period_source
12680             }
12681             
12682         } else {
12683             /*  Connexion entre deux composants ayant chacun une période propre*/
12684             
12685             /*  Unité de temps la plus grande des deux périodes (parmi source et cible)*/
12686              liste = _unites {seconds minutes hours days months years}
12687              indx = _period_source [lsearch $liste_unites $unite_period_source]
12688              indx = _period_cible  [lsearch $liste_unites $unite_period_cible]
12689             
12690             /*  Si l'unité des périodes source et cibles sont différentes*/
12691             if { $indx_period_source != $indx_period_cible } {
12692                 /*  Choisit l'unité la plus petite*/
12693                 if { $indx_period_source < $indx_period_cible } {
12694                      min = _index $indx_period_source
12695                      max = _index $indx_period_cible
12696                      exch = _per_unit $unite_period_source
12697                 } else {
12698                      min = _index $indx_period_cible
12699                      max = _index $indx_period_source
12700                      exch = _per_unit $unite_period_cible
12701                 }
12702                 
12703                 /*  Convertit les périodes source et cible dans cette unité*/
12704                 /*  -------------------------------------------------------*/
12705                 
12706                 /*  Calcule le facteur multiplicatif a appliquer sur une unité*/
12707                  liste = _facteurs {60 60 24 30 12}
12708                  liste = _facteurs [lrange $liste_facteurs $min_index [expr $max_index-1]]
12709                  facteur =  1
12710                 foreach nombre $liste_facteurs {
12711                      facteur =  [expr $facteur * $nombre]
12712                 }
12713                 /*  Si il faut convertir la période cible*/
12714                 if {$indx_period_source < $indx_period_cible} {
12715                      valeur = _cible [expr $qte_period_cible * $facteur]
12716                      valeur = _source $qte_period_source
12717                 } else {
12718                      valeur = _source [expr $qte_period_source * $facteur]
12719                      valeur = _cible  $qte_period_cible
12720                 }
12721             
12722             /*  Si l'unité des périodes source et cibles sont les mêmes*/
12723             } else {
12724                 /*  La période des échanges est initialisée avec cette unité*/
12725                  exch = _per_unit $unite_period_source
12726                  valeur = _source $qte_period_source
12727                  valeur = _cible  $qte_period_cible
12728             }
12729             /*  Initialise la valeur de la période avec le PPCM (Pplus Petit Commun Multiple) des deux valeurs*/
12730             /*  PPCM(A,B) = A * B / PGCD(A,B)*/
12731              exch = _per_val [expr $valeur_cible * $valeur_source / [pgcd $valeur_cible $valeur_source]]
12732         }
12733     }
12734 
12735      x =  [expr [winfo rootx .gui]+200]
12736      y =  [expr [winfo rooty .gui]-20]
12737     wm geometry $u "+$x+$y"
12738 
12739      w =  $u.menu
12740     frame $w
12741     grid $w -row 2 -column 0
12742     grid configure $w -columnspan 2 -padx 4 -pady 4
12743 
12744     /* =====================================================================*/
12745     /* ===                     boutons*/
12746     /* =====================================================================*/
12747 
12748     frame $w.buttons
12749     pack $w.buttons -side bottom -fill x -pady 2m
12750     button $w.buttons.dismiss -text Cancel -command "catch {destroy .remaillage}; destroy $u"
12751     button $w.buttons.ok -text OK -command "$on_OK \$exch_per_unit_m \$exch_per_val_m \$lag_m \$src_time_op_m \$src_add_val_m \$src_mult_val_m \$dbg_source_m \$stat_source_mask_m \$stat_source_nmsk_m \$stat_source_all_m \$remail_m \$tgt_time_op_m \$tgt_add_val_m \$tgt_mult_val_m \$dbg_cible_m \$stat_cible_mask_m \$stat_cible_nmsk_m \$stat_cible_all_m \$file_packing_m \$file_scaling_m \$file_adding_m \$file_fill_value_m"
12752     /*  focus $w.buttons.ok*/
12753     bind $w.buttons.ok <KeyPress-Return> "$on_OK \$exch_per_unit_m \$exch_per_val_m \$lag_m \$src_time_op_m \$src_add_val_m \$src_mult_val_m \$dbg_source_m \$stat_source_mask_m \$stat_source_nmsk_m \$stat_source_all_m \$remail_m \$tgt_time_op_m \$tgt_add_val_m \$tgt_mult_val_m \$dbg_cible_m \$stat_cible_mask_m \$stat_cible_nmsk_m \$stat_cible_all_m \$file_packing_m \$file_scaling_m \$file_adding_m \$file_fill_value_m"
12754 
12755     pack $w.buttons.dismiss -side left -expand 1 -padx 10
12756     pack $w.buttons.ok -side right -expand 1 -padx 10
12757     
12758     /*  frame $w.sep1 -width 430 -height 2 -borderwidth 1 -relief sunken*/
12759     /*  pack $w.sep1 -side bottom -fill x -pady 2m -expand 1*/
12760 
12761      w =  $u.char
12762     frame $w
12763     grid $w -row 0 -column 1
12764     grid configure $w -padx 4 -pady 4
12765 
12766     /* =====================================================================*/
12767     /* ===                     source*/
12768     /* =====================================================================*/
12769 
12770     /* ===source name*/
12771      r =  0
12772     if {$si_source_fichier} {
12773         label $w.label0 -text "Source file :"
12774     } else {
12775         label $w.label0 -text "Source component :"
12776     }
12777     grid $w.label0 -row $r -column 0 -sticky e -padx 10 -pady 4
12778     grid [label $w.label0b -relief sunken -width 60 -text $comp_source -anchor w] -row $r -column 1 -sticky w -padx 2
12779     incr r
12780     /* ===object name at source*/
12781     grid [label $w.label1 -text "Source field :"] -row $r -column 0 -sticky e -padx 10 -pady 4
12782     grid [label $w.label1b -relief sunken -width 60 -text $champ_source -anchor w] -row $r -column 1 -sticky w -padx 2
12783     incr r
12784     /* ===standard name at source*/
12785     grid [label $w.label1c -text "CF standard name:"] -row $r -column 0 -sticky e -padx 10 -pady 4
12786     grid [label $w.label1d -relief sunken -width 60 -text $std_name_source -anchor w] -row $r -column 1 -sticky w -padx 2
12787     incr r
12788     /* ===minimal period of output*/
12789     if {! $si_source_fichier} {
12790         grid [label $w.label2 -text "Minimal period :"] -row $r -column 0 -sticky e -padx 10 -pady 4
12791         grid [label $w.label22 -relief sunken -text "$qte_period_source $unite_period_source"] -row $r -column 1 -sticky w -padx 2
12792         incr r
12793     }
12794 
12795     /* =====================================================================*/
12796     /* ===                     target*/
12797     /* =====================================================================*/
12798     grid [ frame $w.sep1 -width 430 -height 2 -borderwidth 1 -relief sunken] -row $r -column 0 -pady 5 -columnspan 2
12799     incr r
12800 
12801     /* ===target name*/
12802     if {$si_cible_fichier} {
12803         /*  Le champ "comp_cible" est l'identifiant du fichier : il faut determiner son nom*/
12804          nom = _fichier $dico_caract_fichiers($comp_cible.name)
12805         label $w.label3 -text "Target file :"
12806         label $w.label3b -relief sunken -width 60 -text $nom_fichier -anchor w
12807     } else {
12808         label $w.label3 -text "Target component :"
12809         label $w.label3b -relief sunken -width 60 -text $comp_cible -anchor w
12810     }
12811     grid $w.label3 -row $r -column 0 -sticky e -padx 10 -pady 4
12812     grid $w.label3b -row $r -column 1 -sticky w -padx 2
12813     incr r
12814     /* ===object name at target*/
12815     grid [label $w.label4 -text "Target field :"] -row $r -column 0 -sticky e -padx 10 -pady 4
12816     grid [label $w.entry4 -relief sunken -width 60 -text $champ_cible -anchor w] -row $r -column 1 -sticky w -padx 2
12817     incr r
12818     /* ===standard name at target*/
12819     grid [label $w.label4a -text "CF standard name:"] -row $r -column 0 -sticky e -padx 10 -pady 4
12820     grid [label $w.label4b -relief sunken -width 60 -text $std_name_cible -anchor w] -row $r -column 1 -sticky w -padx 2
12821     incr r
12822     /* ===minimal period of input*/
12823     if {! $si_cible_fichier} {
12824         grid [label $w.label5 -text "Minimal period :"] -row $r -column 0 -sticky e -padx 10 -pady 4
12825         grid [label $w.label55 -relief sunken -text "$qte_period_cible $unite_period_cible"] -row $r -column 1 -sticky w -padx 2
12826         incr r
12827     }
12828 
12829     /* =====================================================================*/
12830     /* ===                     attributs de la cnx*/
12831     /* =====================================================================*/
12832     grid [ frame $w.sep2 -width 430 -height 2 -borderwidth 1 -relief sunken] -row $r -column 0 -pady 5 -columnspan 2
12833     incr r
12834 
12835     /* ===exchange period*/
12836     grid [label $w.label6 -text "Exchange period :" ] -row $r -column 0 -sticky e -padx 10 -pady 4
12837 
12838     grid [frame $w.frexch] -row $r -column 1 -sticky w -padx 2
12839     global exch_per_val_m exch_per_val_m_regexp
12840     trace variable exch_per_val_m w {entry_forceRegexp {^[0-9]*$} }
12841      exch = _per_val_m $exch_per_val
12842     pack [entry $w.frexch.e1 -takefocus 1 -textvariable exch_per_val_m -width 10 -background white] -side left
12843     /*  $w.frexch.e1 delete 0 end ; $w.frexch.e1 insert 0 $exch_per_val*/
12844      liste = _unites {seconds minutes hours days months years}
12845     combobox_lim $w.frexch.cmb $liste_unites {} exch_per_unit_m 28 $exch_per_unit
12846     pack $w.frexch.cmb -side left -padx 2
12847     incr r
12848 
12849     /*  Ajoute une bulle d'aide pour 'exchange_period'*/
12850      help = _text {Enter here the exchange period for the connection.}
12851     entry_help_balloon $w.frexch $help_text
12852     
12853     /* =====================================================================*/
12854     /* ===                     Paramètres à la source*/
12855     /* =====================================================================*/
12856     
12857     /*  Si la source est un fichier (n'est pas un composant d'une application)*/
12858     if {$si_source_fichier} {
12859         /*  Initialise des var globales inutilisées mais nécessaires par compatibilité avec l'autre option*/
12860         global src_time_op_m src_add_val_m src_mult_val_m scatter_m dbg_source_m stat_source_mask_m stat_source_nmsk_m stat_source_all_m
12861          src = _time_op_m ""
12862          src = _add_val_m ""
12863          src = _mult_val_m ""
12864          scatter = _m ""
12865          dbg = _source_m ""
12866          stat = _source_mask_m ""
12867          stat = _source_nmsk_m ""
12868          stat = _source_all_m ""
12869     } else {
12870         /*  Met dans un cadre spécial tous les champs paramètres de la connexion du cotè source*/
12871         comm_saisie_param_source $w $r $lag $src_time_op $src_add_val $src_mult_val $scatter $dbg_source $stat_source_mask $stat_source_nmsk $stat_source_all 
12872     }
12873     incr r
12874 
12875     /* =====================================================================*/
12876     /* ===                     transformations au milieu (remaillage)*/
12877     /* =====================================================================*/
12878 
12879     /*  Si la source et la cible sont des composants d'application*/
12880     if {! ($si_cible_fichier || $si_source_fichier) } {
12881         
12882         /*  Fait une copie des options de remaillage dans une var globale*/
12883         /*  Car les options de remaillage sont saisies dans*/
12884         /*  une boite de dialogue complémentaire (comm_saisie_remaillage)*/
12885         global options_remail
12886         foreach option {nbr_neighbours bi3_ret  if_masked norm_method norm_near_nei} (
12887             type set , type value [, type set $, type option]
12888             , type set , type options_, type remail($, type option) $, type value
12889         )
12890 
12891         # Champs de saisie
12892         grid [label $w.label13 -text "Regridding :" ] -row $r -column 0 -sticky e -padx 10 -pady 4
12893         grid [frame $w.fr_remail] -row $r -column 1 -sticky w -padx 2
12894         set liste_descript { None "3D Nearest neighbour" "3D Trilinear" "2D Nearest neighbour"
12895                             "2D Bilinear" "2D Bicubic" "2D Conservativ" }
12896         global si_debut_remail;  si = _debut_remail 1
12897         combobox_lim $w.fr_remail.cmb5 $liste_descript comm_saisie_remaillage remail_m 28 $remail
12898         pack $w.fr_remail.cmb5 -side left
12899         button $w.fr_remail.det -text Options -command [list comm_saisie_remaillage "$w.fr_remail.cmb5" -1 ]
12900         pack $w.fr_remail.det  -side left -expand 1 -padx 10
12901         incr r
12902 
12903         /*  Ajoute une bulle d'aide pour 'remaillage'*/
12904          help = _text {Choose the regridding to be performed on the output coupling field to express it on the target model grid.
12905 Note that the regridding will provide values interpolated from the source field for all target grid cells except for the following ones:
12906 • the target cell does not intersect any part of the source grid domain; for those cells, the target field keeps the same value as before the call to prism get ;
12907 • the target cell is masked; for those cells, the target field keeps the same value as before the call to prism get ;
12908 • the target cell is not masked, but the interpolation as requested in the SMIOC file cannot be performed; for those cells, the target field will take the 'psmile_undef' value (=-280177).
12909 
12910 A '2D conservative' regridding means that the weight of a source cell is proportional to area intersected by target cell.}
12911         entry_help_balloon $w.fr_remail.cmb5 $help_text
12912  
12913         /*  Ajoute une bulle d'aide pour le bouton 'Options'*/
12914         global no_help_balloon
12915         if { $no_help_balloon == 0 } {
12916              help = _text {Click here to select the interpolation options.}
12917              help = _action "help_object %W [list $help_text] message"
12918             bind $w.fr_remail.det <Any-Enter> $help_action
12919             bind $w.fr_remail.det <FocusIn>   $help_action
12920             bind $w.fr_remail.det <Any-Leave>    "help_object_hide"
12921             bind $w.fr_remail.det <FocusOut>     "help_object_hide"
12922             bind $w.fr_remail.det <ButtonPress>  "help_object_hide"
12923         }
12924     }
12925 
12926     /* =====================================================================*/
12927     /* ===                     Paramètres à la cible*/
12928     /* =====================================================================*/
12929 
12930     /*  Si la cible est un fichier*/
12931     if {$si_cible_fichier} {
12932         /*  Met dans un cadre spécial tous les attributs du fichier NetCDF*/
12933         if { $si_nouveau } {
12934              file = _packing ""
12935              file = _scaling ""
12936              file = _adding ""
12937              file = _fill_value ""
12938         } else {
12939              file = _packing $dico_caract_fichiers($comp_cible.field.packing)
12940              file = _scaling $dico_caract_fichiers($comp_cible.field.scaling)
12941              file = _adding $dico_caract_fichiers($comp_cible.field.adding)
12942              file = _fill_value $dico_caract_fichiers($comp_cible.field.fill_value)
12943         }
12944         comm_saisie_attrib_NetCDF $w $r $file_packing $file_scaling $file_adding $file_fill_value
12945     
12946         /*  Initialise des var globales inutilisées mais nécessaires par compatibilité avec l'autre option (cible = composant)*/
12947         global tgt_time_op_m tgt_add_val_m tgt_mult_val_m gather_m dbg_cible_m stat_cible_mask_m stat_cible_nmsk_m stat_cible_all_m
12948          tgt = _time_op_m ""
12949          tgt = _add_val_m ""
12950          tgt = _mult_val_m ""
12951          gather = _m ""
12952          dbg = _cible_m ""
12953          stat = _cible_mask_m ""
12954          stat = _cible_nmsk_m ""
12955          stat = _cible_all_m ""
12956     } else {
12957         /*  la cible est un composant d'une application*/
12958         /*  Met dans un cadre spécial tous les champs paramètres de la connexion du cotè cible*/
12959         comm_saisie_param_cible $w $r $tgt_time_op $tgt_add_val $tgt_mult_val $gather $dbg_cible $stat_cible_mask $stat_cible_nmsk  $stat_cible_all $si_source_fichier
12960     }
12961     incr r
12962     
12963     /*  Cache la bulle d'aide et ferme la fenetre d'options de remaillage si fermeture de la fenetre*/
12964     bind $u <Destroy> "help_object_hide; catch {destroy .remaillage}"
12965 
12966 }
12967 
12968 ret  {pgcd} (type a , type b) {
12969 # Calcule de Plus Grand Diviseur Commun de deux nombres entiers 
12970 # selon la méthode Euclidienne
12971     if {$a > $b} {
12972         set n1 $a; set n2 $b
12973     } else {
12974         set n1 $b; set n2 $a
12975     }
12976 
12977     set r [expr $n1 % $n2]
12978     if { $r == 0} {
12979         return $n2
12980     } else {
12981         return [pgcd $n2 $r]
12982     }
12983 }
12984 
12985 ret  {comm_saisie_attrib_NetCDF} (type win , type row , type file_, type packing , type file_, type scaling , type file_, type adding , type file_, type fill_, type value) {
12986 # Prépare un cadre rempli de zones de saisie pour la saisie des attributs d'un champ de fichier NetCDF
12987 #
12988     # Crée le cadre de couleur et le place dans la grille de la fenêtre mère
12989     labelframe $win.netCDF -bg AntiqueWhite1 -borderwidth 1 -relief solid -text " NetCDF field attributes "
12990     grid  $win.netCDF -row $row -column 0 -columnspan 2 -pady 4
12991     set fr $win.netCDF
12992     set r 0
12993     
12994     #=== packing
12995     grid [label $fr.label2 -text "Packing :"] -row $r -column 0 -sticky e -padx 10 -pady 4
12996     global file_packing_m
12997     trace variable file_packing_m w {entry_forceRegexp {^[ 1248]?$} }
12998     set file_packing_m $file_packing
12999     grid [entry $fr.entry2 -textvariable file_packing_m -width 10 -background white] -row $r -column 1 -sticky w -padx 2
13000     incr r
13001 
13002     # Ajoute une bulle d'aide pour 'packing'
13003     set help_text {If packing is chosen, data on file is compressed to 1, 2, 4 or 8 bit data fields. Choose either 1, 2, 4 or 8 or none.}
13004     entry_help_balloon $fr.entry2 $help_text
13005 
13006     #=== scaling
13007     grid [label $fr.label3 -text "Scaling :"] -row $r -column 0 -sticky e -padx 10 -pady 4
13008     global file_scaling_m
13009     trace variable file_scaling_m w {entry_forceReal }
13010     set file_scaling_m $file_scaling
13011     grid [entry $fr.entry3 -textvariable file_scaling_m -width 10 -background white] -row $r -column 1 -sticky w -padx 2
13012     incr r
13013 
13014     # Ajoute une bulle d'aide pour 'scaling'
13015     set help_text {if present, data are stored in file after dividing by the ‘scaling’ value (1.0 by default).
13016 The scaling factor is also stored in the file for the purpose of reading applications.}
13017     entry_help_balloon $fr.entry3 $help_text
13018 
13019     #=== adding
13020     grid [label $fr.label4 -text "Adding :"] -row $r -column 0 -sticky e -padx 10 -pady 4
13021     global file_adding_m
13022     trace variable file_adding_m w {entry_forceReal }
13023     set file_adding_m $file_adding
13024     grid [entry $fr.entry4 -textvariable file_adding_m -width 10 -background white] -row $r -column 1 -sticky w -padx 2
13025     incr r
13026 
13027     # Ajoute une bulle d'aide pour 'adding'
13028     set help_text {if present, data are stored in file after subtracting the adding’ value (0.0 by default).
13029 The adding value is also stored in the file for the purpose of reading applications.}
13030     entry_help_balloon $fr.entry4 $help_text
13031 
13032     #=== fill_value
13033     grid [label $fr.label5 -text "Fill value :"] -row $r -column 0 -sticky e -padx 10 -pady 4
13034     global file_fill_value_m
13035     trace variable file_fill_value_m w {entry_forceReal }
13036     set file_fill_value_m $file_fill_value
13037     grid [entry $fr.entry5 -textvariable file_fill_value_m -width 10 -background white] -row $r -column 1 -sticky w -padx 2
13038     incr r
13039 
13040     # Ajoute une bulle d'aide pour 'fill_value'
13041     set help_text {Specifies the value given in the file to undefined or missing data. For example, value given to grid points for which no meaningfull value was calculated.}
13042     entry_help_balloon $fr.entry5 $help_text
13043 }
13044 
13045 ret  {comm_saisie_param_cible} (type win , type row , type tgt_, type time_, type op , type tgt_, type add_, type val , type tgt_, type mult_, type val , type gather , type dbg_, type cible , type stat_, type cible_, type mask , type stat_, type cible_, type nmsk , type stat_, type cible_, type all , type si_, type source_, type fichier) {
13046 # Prépare un cadre rempli de zones de saisie pour la saisie d'une connexion
13047 # Ce cadre est spécialisé dans les paramètres du côté de la cible de la connexion
13048 #
13049     # Crée le cadre de couleur et le place dans la grille de la fenêtre mère
13050     labelframe $win.tgt_param -bg AntiqueWhite1 -borderwidth 1 -relief solid -text " Target parameters "
13051     grid  $win.tgt_param -row $row -column 0 -columnspan 2 -pady 4
13052     set fr_tgt $win.tgt_param
13053     set r_tgt 0
13054     
13055     # Si la source est un fichier
13056     if {$si_source_fichier} {
13057         #=== time operation
13058         grid [label $fr_tgt.label14 -text "Time operation :"] -row $r_tgt -column 0 -sticky e -padx 10 -pady 4
13059         combobox_lim $fr_tgt.cmb6 {{None (Closest time)} {Time interpolation}} {} tgt_time_op_m 28 $tgt_time_op
13060         grid $fr_tgt.cmb6 -row $r_tgt -column 1 -sticky w -padx 2
13061         incr r_tgt
13062     
13063         # Ajoute une bulle d'aide pour 'Time operation'
13064         set help_text {Target time interpolation is supported only for IO data read from a file. It is a linear time interpolation between the two closest timestamps in the input file.}
13065         entry_help_balloon $fr_tgt.cmb6 $help_text
13066     }
13067     
13068     #=== opération algébrique
13069     grid [label $fr_tgt.label15 -text "Algebraic operation :"] -row $r_tgt -column 0 -sticky e -padx 10 -pady 4
13070     grid [frame $fr_tgt.fr_tgt_algb] -row $r_tgt -column 1 -sticky w -padx 2
13071     
13072     pack [label $fr_tgt.fr_tgt_algb.label1 -text "Mult by "] -side left -padx 2
13073     global tgt_mult_val_m
13074     trace variable tgt_mult_val_m w {entry_forceReal}
13075     set tgt_mult_val_m $tgt_mult_val
13076     pack [entry $fr_tgt.fr_tgt_algb.e1 -textvariable tgt_mult_val_m -width 10 -background white] -side left
13077     
13078     pack [label $fr_tgt.fr_tgt_algb.label2 -text "Add "] -side left -padx 2
13079     global tgt_add_val_m
13080     trace variable tgt_add_val_m w {entry_forceReal}
13081     set tgt_add_val_m $tgt_add_val
13082     pack [entry $fr_tgt.fr_tgt_algb.e2 -textvariable tgt_add_val_m -width 10 -background white] -side left
13083     incr r_tgt
13084 
13085     # Ajoute une bulle d'aide pour 'Algebraic operation'
13086     set help_text {Each grid point coupling/IO field value is multiplied then increased by scalars specified in this element after  being received from source component.}
13087     entry_help_balloon $fr_tgt.fr_tgt_algb $help_text
13088 
13089     #===non masked value gathering
13090     grid [label $fr_tgt.label16 -text "Non masked value gathering :"] -row $r_tgt -column 0 -sticky e -padx 10 -pady 4
13091     array set conversion { 1 yes 0 no "" no }
13092     label $fr_tgt.label17 -relief sunken -text $conversion($gather)
13093     grid $fr_tgt.label17 -row $r_tgt -column 1 -sticky w -padx 2
13094     incr r_tgt
13095 
13096     #===mode debug at target
13097     grid [label $fr_tgt.label9 -text "Target debug mode :"] -row $r_tgt -column 0 -sticky e -padx 10 -pady 4
13098     combobox_lim $fr_tgt.cmb2 "yes no" {} dbg_cible_m 28 $dbg_cible
13099     grid $fr_tgt.cmb2 -row $r_tgt -column 1 -sticky w -padx 2
13100     incr r_tgt
13101 
13102     # Ajoute une bulle d'aide pour 'debug mode'
13103     set help_text {If set to true, the input coupling/IO field is automatically written to a file each time it is received from the source component.}
13104     entry_help_balloon $fr_tgt.cmb2 $help_text    
13105     
13106     #=== statistics at target
13107     grid [label $fr_tgt.label9b -text "Target statistics :"] -row $r_tgt -column 0 -sticky e -padx 10 -pady 4
13108     grid [frame $fr_tgt.fr_tgt_stat] -row $r_tgt -column 1 -sticky w -padx 2
13109     pack [label $fr_tgt.fr_tgt_stat.label1 -text "Masked :"] -side left -padx 2
13110     combobox_lim $fr_tgt.fr_tgt_stat.cmb1 "on off" {} stat_cible_mask_m 9 $stat_cible_mask
13111     pack $fr_tgt.fr_tgt_stat.cmb1 -side left
13112     
13113     pack [label $fr_tgt.fr_tgt_stat.label2 -text "Non Masked :"] -side left -padx 2
13114     combobox_lim $fr_tgt.fr_tgt_stat.cmb2 "on off" {} stat_cible_nmsk_m 9 $stat_cible_nmsk
13115     pack $fr_tgt.fr_tgt_stat.cmb2 -side left
13116     
13117     pack [label $fr_tgt.fr_tgt_stat.label3 -text "All :"] -side left -padx 2
13118     combobox_lim $fr_tgt.fr_tgt_stat.cmb3 "on off" {} stat_cible_all_m 9 $stat_cible_all
13119     pack $fr_tgt.fr_tgt_stat.cmb3 -side left
13120     incr r_tgt
13121 
13122     # Ajoute une bulle d'aide pour 'Statistics'
13123     set help_text {Different statistics (minimum, maximum, integral) are calculated for the field on the masked points, and/or on the non masked points, and/or on all points of the input coupling/IO field.
13124 This is done each time the input field is received (before the time operation if any).
13125 These statitistics are printed to the log file for information only; they do not transform the input coupling/IO field.}
13126     entry_help_balloon $fr_tgt.fr_tgt_stat $help_text
13127 }
13128 
13129 ret  {comm_saisie_param_source} (type win , type row , type lag , type src_, type time_, type op , type src_, type add_, type val , type src_, type mult_, type val , type scatter , type dbg_, type source , type stat_, type source_, type mask , type stat_, type source_, type nmsk , type stat_, type source_, type all) {
13130 # Prépare un cadre rempli de zones de saisie pour la saisie d'une connexion
13131 # Ce cadre est spécialisé dans les paramètres du côté de la source de la connexion
13132 #
13133     # Crée le cadre de couleur et le place dans la grille de la fenêtre mère
13134     labelframe $win.src_param -bg LavenderBlush -borderwidth 1 -text " Source parameters " -relief solid
13135     grid  $win.src_param -row $row -column 0 -columnspan 2 -pady 4
13136     set fr_src $win.src_param
13137     set r_src 0
13138         
13139     #===lag
13140     grid [label $fr_src.label7 -text "Lag :"] -row $r_src -column 0 -sticky e -padx 10 -pady 4
13141     global lag_m lag_m_regexp
13142     trace variable lag_m w {entry_forceRegexp {^[0-9]*$} }
13143     set lag_m $lag
13144     grid [entry $fr_src.e7 -textvariable lag_m -width 10 -background white] -row $r_src -column 1 -sticky w -padx 2
13145     incr r_src
13146     
13147     # Ajoute une bulle d'aide pour 'lag'
13148     set help_text {The lag specifies the number of periods to be added to the output date (prism_put) to match the corresponding input date (prism_get) in the target component.
13149 
13150 Note : if lag > 0 (see Oasis user guide, section 5.5.4), a coupling restart file is needed to start the run.}
13151     entry_help_balloon $fr_src.e7 $help_text
13152     
13153     #=== time operation
13154     grid [label $fr_src.label10 -text "Time operation :"] -row $r_src -column 0 -sticky e -padx 10 -pady 4
13155     combobox_lim $fr_src.cmb3 {None {Time accumulation} {Time average}} {} src_time_op_m 28 $src_time_op
13156     grid $fr_src.cmb3 -row $r_src -column 1 -sticky w -padx 2
13157     incr r_src
13158 
13159     # Ajoute une bulle d'aide pour 'Time operation'
13160     set help_text {For each grid point, the output coupling/IO field can be averaged or accumulated over the last coupling period before transfer to target component.}
13161     entry_help_balloon $fr_src.cmb3 $help_text
13162     
13163     #=== opération algébrique
13164     grid [label $fr_src.label11 -text "Algebraic operation :"] -row $r_src -column 0 -sticky e -padx 10 -pady 4
13165     grid [frame $fr_src.fr_src_algb] -row $r_src -column 1 -sticky w -padx 2
13166 
13167     pack [label $fr_src.fr_src_algb.label1 -text "Mult by "] -side left -padx 2
13168     global src_mult_val_m
13169     trace variable src_mult_val_m w {entry_forceReal}
13170     set src_mult_val_m $src_mult_val
13171     pack [entry $fr_src.fr_src_algb.e1 -textvariable src_mult_val_m -width 10 -background white] -side left
13172     
13173     pack [label $fr_src.fr_src_algb.label2 -text "Add "] -side left -padx 2
13174     global src_add_val_m
13175     trace variable src_add_val_m w {entry_forceReal}
13176     set src_add_val_m $src_add_val
13177     pack [entry $fr_src.fr_src_algb.e2 -textvariable src_add_val_m -width 10 -background white] -side left
13178     incr r_src
13179 
13180     # Ajoute une bulle d'aide pour 'Algebraic operation'
13181     set help_text {Each grid point coupling/IO field value is multiplied then increased by scalars specified in this element before transfer to target component.}
13182     entry_help_balloon $fr_src.fr_src_algb $help_text    
13183 
13184     #===non masked value scattering
13185     grid [label $fr_src.label12 -text "Non masked value scattering :"] -row $r_src -column 0 -sticky e -padx 10 -pady 4
13186     array set conversion { 1 yes 0 no "" no }
13187     label $fr_src.label13 -relief sunken -text $conversion($scatter)
13188     grid $fr_src.label13 -row $r_src -column 1 -sticky w -padx 2
13189     incr r_src
13190 
13191     #===mode debug at source
13192     grid [label $fr_src.label8 -text "Debug mode :"] -row $r_src -column 0 -sticky e -padx 10 -pady 4
13193     combobox_lim $fr_src.cmb1 "yes no" {} dbg_source_m 28 $dbg_source
13194     grid $fr_src.cmb1 -row $r_src -column 1 -sticky w -padx 2
13195     incr r_src
13196 
13197     # Ajoute une bulle d'aide pour 'Debug mode'
13198     set help_text {If set to true, the output coupling/IO field is automatically written to a file each time it is sent to the target component.}
13199     entry_help_balloon $fr_src.cmb1 $help_text    
13200 
13201     #=== statistics at source
13202     grid [label $fr_src.label8b -text "Statistics :"] -row $r_src -column 0 -sticky e -padx 10 -pady 4
13203     grid [frame $fr_src.fr_src_stat] -row $r_src -column 1 -sticky w -padx 2
13204     pack [label $fr_src.fr_src_stat.label1 -text "Masked :"] -side left -padx 2
13205     combobox_lim $fr_src.fr_src_stat.cmb1 "on off" {} stat_source_mask_m 9 $stat_source_mask
13206     pack $fr_src.fr_src_stat.cmb1 -side left
13207     
13208     pack [label $fr_src.fr_src_stat.label2 -text "Non Masked :"] -side left -padx 2
13209     combobox_lim $fr_src.fr_src_stat.cmb2 "on off" {} stat_source_nmsk_m 9 $stat_source_nmsk
13210     pack $fr_src.fr_src_stat.cmb2 -side left
13211     
13212     pack [label $fr_src.fr_src_stat.label3 -text "All :"] -side left -padx 2
13213     combobox_lim $fr_src.fr_src_stat.cmb3 "on off" {} stat_source_all_m 9 $stat_source_all
13214     pack $fr_src.fr_src_stat.cmb3 -side left
13215     incr r_src
13216 
13217     # Ajoute une bulle d'aide pour 'Statistics'
13218     set help_text {Different statistics (minimum, maximum, integral) are calculated for the field on the masked points, and/or on the non masked points, and/or on all points of the output coupling/IO field.
13219 This is done each time the output field is sent, after any time operation (accumulation or average).
13220 These statitistics are printed to the log file for information only; they do not transform the output coupling/IO field.}
13221     entry_help_balloon $fr_src.fr_src_stat $help_text
13222 }
13223 
13224 ret  {comm_saisie_remaillage} (type nom_, type widget , type indice_, type select) {
13225 # Procedure appelee automatiquement sur selection d'un mode de remaillage
13226 # dans la listbox lors de la specification d'une connexion.
13227 # Cette meme proc. est appelee sur clic du bouton "Options" de la meme boite de dialogue.
13228 # Dans ce cas, le parametre "indice_select" n'est pas renseigné et vaut -1.
13229 #
13230     # Copie les options de remaillage dans des var globales
13231     global options_remail
13232     global nbr_neighbours_m nbr_neighbours_m_regexp bi3_method_m if_masked_m norm_method_m norm_near_nei_m
13233     foreach option {nbr_neighbours bi3_method if_masked norm_method norm_near_nei} {
13234         set value $options_remail($option)
13235         set $option\_m $value
13236     }
13237 
13238     # Var globale indiquant que l'utilisateur a validé sa saisie
13239     global valid_remail
13240     # Var globale indiquant qu'on vient de créer la liste déroulante (listbox)
13241     global si_debut_remail
13242 
13243     # Si sélection du type de remaillage dans la liste déroulante
13244     if { $indice_select != -1 } {
13245         # Ferme la liste déroulante
13246         $nom_widget close
13247         # Si c'est la première fois
13248         if { $si_debut_remail } {
13249             # Ce n'est pas une sélection manuelle : celle faite à l'init de la liste déroulante
13250             set si_debut_remail 0
13251             return
13252         }
13253     }
13254 
13255     # Type de remaillage sélectionné
13256     set type_remail [$nom_widget get]
13257     if { $type_remail != "None" } {
13258         # Création de la fenêtre de saisie du mode de remaillage avec les champs appropriés
13259         set u .remaillage
13260         catch {destroy $u}
13261         toplevel $u
13262         wm title $u "Regridding options"
13263         wm iconname $u "regrid_option"
13264         set x [expr [winfo rootx $nom_widget]+60]
13265         set y [expr [winfo rooty $nom_widget]+20]
13266         wm geometry $u "+$x+$y"
13267         # Var globale indiquant que l'utilisateur a validé sa saisie
13268         set valid_remail 0
13269 
13270         set w $u.menu
13271         frame $w
13272         grid $w -row 2 -column 0
13273         grid configure $w -columnspan 2 -padx 4 -pady 4
13274 
13275         # Boutons OK et Cancel
13276         # ---------------------
13277         frame $w.buttons
13278         pack $w.buttons -side bottom -fill x -pady 2m
13279 
13280         button $w.buttons.dismiss  -text Cancel -command "destroy $u"
13281         button $w.buttons.ok  -text Ok -command "global valid_remail; set valid_remail 1; destroy $u"
13282 
13283         pack $w.buttons.dismiss -side left -expand 1 -padx 10
13284         pack $w.buttons.ok -side left -expand 1 -padx 10
13285 
13286         frame $w.sep1 -width 200 -height 2 -borderwidth 1 -relief sunken
13287         pack $w.sep1 -side bottom -fill x -pady 2m -expand 1
13288 
13289         # Champs de saisie
13290         # ----------------
13291 
13292         set w $u.char
13293         frame $w
13294         grid $w -row 0 -column 1
13295         grid configure $w -padx 4 -pady 4
13296         set r 0
13297 
13298         # Type de remaillage
13299         label $w.label_t1 -text "Regridding method :"
13300         label $w.label_t2 -relief sunken -text $type_remail
13301         grid $w.label_t1 $w.label_t2 -row $r -sticky w
13302         incr r
13303         # Séparateur
13304         grid [ frame $w.sep1 -width 200 -height 2 -borderwidth 1 -relief sunken] -row $r -column 0 -pady 5 -columnspan 2
13305         incr r
13306 
13307         # Suivant le type de remaillage
13308         switch $type_remail {
13309             # Si remaillage par la methode des voisins
13310             "3D Nearest neighbour" -
13311             "2D Nearest neighbour" {
13312                 label $w.label0 -text "Nbr of neighbours :"
13313                 trace variable nbr_neighbours_m w {entry_forceRegexp {^[0-9]*$} }
13314                 if {$nbr_neighbours_m == 0} {set nbr_neighbours_m 3}
13315                 entry $w.entry0 -relief sunken -width 40 -textvariable nbr_neighbours_m -background white
13316                 grid $w.label0 $w.entry0 -row $r -sticky w
13317                 incr r
13318             }
13319             "2D Bicubic" {
13320                 label $w.label0 -text "Bicubic method :"
13321                 combobox_lim $w.comb0 "gradient sixteen" {} bi3_method_m 28 $bi3_method_m
13322                 grid $w.label0 $w.comb0 -row $r -sticky w
13323                 incr r
13324 
13325                 # Ajoute une bulle d'aide pour 'Bicubic method'
13326                 set help_text {Either 'gradient' (the four enclosing source neighbour values and gradient values are used), or 'sixteen' (the sixteen enclosing source neighbour values are used).}
13327                 entry_help_balloon $w.comb0 $help_text    
13328             }
13329             "2D Conservativ" {
13330                 label $w.label0 -text "Normalisation method :"
13331                 combobox_lim $w.comb0 {None {Fractional area} {Destination area}} {} norm_method_m 28 $norm_method_m
13332                 grid $w.label0 $w.comb0 -row $r -sticky w
13333                 incr r
13334                 label $w.label1 -text "Near neighbour :"
13335                 combobox_lim $w.comb1 "yes no" {} norm_near_nei_m 28 $norm_near_nei_m
13336                 grid $w.label1 $w.comb1 -row $r -sticky w
13337                 incr r
13338 
13339                 # Ajoute une bulle d'aide pour 'Normalisation method'
13340                 set help_text {It can be:
13341 · fracarea: The sum of the non-masked source cell intersected areas is used to normalise each target cell field value: the flux is not locally conserved, but the flux value itself is reasonable.
13342 
13343 · destarea: The total target cell area is used to normalise each target cell field value even if it only partly intersects non-masked source grid cells: local flux conservation is ensured, but unreasonable flux values may result.
13344 
13345 · none: No normalisation is applied.}
13346                 entry_help_balloon $w.comb0 $help_text    
13347             }
13348         }
13349         if { $type_remail != "2D Conservativ" } {
13350             label $w.label2 -text "If masked :"
13351             combobox_lim $w.comb2 {{no_value} tneighbour nneighbour} {} if_masked_m 28 $if_masked_m
13352             grid $w.label2 $w.comb2 -row $r -sticky w
13353             incr r
13354 
13355             # Ajoute une bulle d'aide pour 'If masked '
13356             set help_text {Policy applied when some of N nearest neighbour points are masked (N being dependant on the interpolation method):
13357 * novalue: psmile_undef' value (=-280177) is given to the target point;
13358 
13359 * tneighbour: the non-masked points among neighbours are used for calculating a weighted average; if all N neighbours are masked, 'psmile_undef' value is given to that target point;
13360 
13361 * nneighbour: the non-masked points among neighbours are used for calculating a weighted average; if all N neighbours are masked, the non-masked nearest neighbour is used.}
13362             entry_help_balloon $w.comb2 $help_text    
13363         }
13364         bind $u <Destroy> "help_object_hide"
13365         tkwait window .remaillage
13366 
13367         # Si la fenêtre est fermée pour la dernière fois
13368         if {[info exists valid_remail]} {
13369             # Si on a validé la saisie
13370             if { $valid_remail } {
13371                 # Sauve les options de remaillage saisies dans une var globale
13372                 global options_remail
13373                 foreach option {nbr_neighbours bi3_method if_masked norm_method norm_near_nei} {
13374                     set value [set $option\_m]
13375                     set options_remail($option) $value
13376                     unset $option\_m
13377                 }
13378             }
13379             unset valid_remail
13380         }
13381     }
13382 }
13383 
13384 ret  {comm_update} (type win , type clef_, type cnx , type exch_, type per_, type unit , type exch_, type per_, type val , type lag , type src_, type time_, type op , type src_, type add_, type val , type src_, type mult_, type val , type dbg_, type source , type stat_, type source_, type mask , type stat_, type source_, type nmsk , type stat_, type source_, type all , type remail , type tgt_, type time_, type op , type tgt_, type add_, type val , type tgt_, type mult_, type val , type dbg_, type cible , type stat_, type cible_, type mask , type stat_, type cible_, type nmsk , type stat_, type cible_, type all , type file_, type packing , type file_, type scaling , type file_, type adding , type file_, type fill_, type value) {
13385 # Variables globales
13386     global dico_caract_fichiers
13387     global dico_caract_cnx
13388     global dico_caract_points
13389     global dico_cnx_out
13390 
13391     # Vérifie les informations entrées
13392     if { [comm_validation $exch_per_unit $exch_per_val $remail] } {
13393         return error
13394     }
13395 
13396     # Enlever le 's' de pluriel aux unités de temps de la période des échanges
13397     set exch_per_unit [string range $exch_per_unit 0 end-1]
13398 
13399     # Réinterprète le champ "type de remaillage"
13400     # ------------------------------------------
13401 
13402     # Liste de descriptions lisibles et de mots-clefs correspondants
13403     array set remaillages { None {} {3D Nearest neighbour} nneighbour3D {3D Trilinear} trilinear {2D Nearest neighbour} nneighbour2D
13404         {2D Bilinear} bilinear {2D Bicubic} bicubic {2D Conservativ} conservativ2D }
13405 
13406     # Transforme le mot-clef en description lisible
13407     if { $remail != "" } { set remail $remaillages($remail) }
13408 
13409     # Réinterprète le champ "source time operation"
13410     array set mots_clefs { None {} {Time average} taverage {Time accumulation} accumul }
13411     if { $src_time_op != "" } { set src_time_op $mots_clefs($src_time_op) }
13412 
13413     # Réinterprète le champ "target time operation"
13414     array unset mots_clefs
13415     array set mots_clefs { {None (Closest time)} {} {Time interpolation} time_linear }
13416     if { $tgt_time_op != "" } { set tgt_time_op $mots_clefs($tgt_time_op) }
13417 
13418     # Réinterprète les booléens
13419     # Pour tous les champs booléens
13420     foreach nom_champ { dbg_source dbg_cible } {
13421         set valeur [set $nom_champ]
13422         if { $valeur != "" } {       
13423             if {$valeur == "yes"} {set $nom_champ true} else {set $nom_champ false}
13424         }
13425     }
13426 
13427     # Enregistre les attributs de la connexion
13428     # ----------------------------------------
13429 
13430     set dico_caract_cnx($clef_cnx.src_time_op) ""
13431     set dico_caract_cnx($clef_cnx.src_add_val)  0
13432     set dico_caract_cnx($clef_cnx.src_mult_val) 1
13433     set dico_caract_cnx($clef_cnx.tgt_time_op) ""
13434     set dico_caract_cnx($clef_cnx.tgt_add_val) 0
13435     set dico_caract_cnx($clef_cnx.tgt_mult_val) 1
13436     set dico_caract_cnx($clef_cnx.remail) ""
13437 
13438     foreach attrib {exch_per_unit exch_per_val lag dbg_source dbg_cible src_time_op src_add_val src_mult_val
13439                     remail tgt_time_op tgt_add_val tgt_mult_val
13440                     stat_source_mask stat_source_nmsk stat_source_all stat_cible_mask stat_cible_nmsk stat_cible_all } {
13441         set value [set $attrib]
13442         if { $value != "" } { set dico_caract_cnx($clef_cnx.$attrib) $value }
13443     }
13444 
13445     # Enregistre les options de remaillage
13446     global options_remail
13447     if { $remail != "" } {
13448         # Réinterprète l'option booléenne norm_near_nei
13449         if {$options_remail(norm_near_nei) == "yes"} {set options_remail(norm_near_nei) true} else {set options_remail(norm_near_nei) false}
13450         # Traduit l'option de remaillage conservatif
13451         switch $options_remail(norm_method) {
13452             "Fractional area"  { set options_remail(norm_method) fracarea }
13453             "Destination area" { set options_remail(norm_method) destarea }
13454         }
13455         foreach option {nbr_neighbours bi3_method if_masked norm_method norm_near_nei} {
13456             set dico_caract_cnx($clef_cnx.$option) $options_remail($option)
13457         }
13458     }
13459     array unset options_remail
13460 
13461     # Appli du composant cible
13462     set appli_cible $dico_caract_cnx($clef_cnx.app_cible)
13463     # Si la cible est un fichier NetCDF
13464     if { [string equal $appli_cible "///file///"] } {
13465         # Identifiant du fichier cible
13466         set id_fichier $dico_caract_cnx($clef_cnx.comp_cible)
13467         # Enregistre les attributs du champ du fichier NetCDF
13468         set dico_caract_fichiers($id_fichier.field.packing) $file_packing 
13469         set dico_caract_fichiers($id_fichier.field.scaling) $file_scaling 
13470         set dico_caract_fichiers($id_fichier.field.adding)  $file_adding 
13471         set dico_caract_fichiers($id_fichier.field.fill_value) $file_fill_value
13472     }    
13473     
13474     catch {destroy .remaillage}; destroy $win
13475 }
13476 
13477 ret  {comm_validation} (type exch_, type per_, type unit , type exch_, type per_, type val , type remail) {
13478 # Vérifie les informations entrées dans la boite de dialogue de paramétrisation
13479 # d'une connexion. 
13480 # Retourne 0 si pas d'erreur, 1 sinon.
13481 
13482     # Les options de remaillage
13483     global options_remail
13484     
13485     if {$exch_per_val == "" || $exch_per_unit == "Choose..."} {
13486         notice_show "Exchange period not filled !" error
13487         return 1
13488     }
13489 
13490     # Suivant le type de remaillage
13491     switch $remail {
13492         # Si remaillage par la methode des voisins
13493         "3D Nearest neighbour" -
13494         "2D Nearest neighbour" {
13495             if { $options_remail(nbr_neighbours) == "" } {
13496                 notice_show "Regridding option : nbr of neighbours not filled !" error
13497                 return 1
13498             }
13499         }
13500     }
13501 
13502     return 0
13503 }
13504 
13505 ret  {component_edit} (type clef_, type comp) {
13506 #
13507     global dico_applis dico_units
13508 
13509     # Extrait les informations pertinentes
13510     # ------------------------------------
13511 
13512     set nom_composant $dico_units($clef_comp)
13513     # Extrait de la clef le nom de l'appli
13514     set len_comp [string length $nom_composant]
13515     incr len_comp
13516     set nom_appli [string range $clef_comp 0 end-$len_comp]
13517     # Nom long du composant
13518     set clef_comp_2 app.$nom_appli.comp.$nom_composant
13519     set nom_long_comp ""
13520     if {[info exists dico_applis($clef_comp_2.long_name)]} {
13521         set nom_long_comp $dico_applis($clef_comp_2.long_name)
13522     }
13523     #== nb de processus
13524     set clef_proc $clef_comp_2.proc
13525     set min_procnumber 0
13526     catch {set min_procnumber $dico_applis($clef_proc.min_value)}
13527     set max_procnumber $min_procnumber
13528     catch {set max_procnumber $dico_applis($clef_proc.max_value)}
13529     set step_procnumber 1
13530     catch {set step_procnumber $dico_applis($clef_proc.increment)}
13531     # Plage du nombre de processus
13532     if { $max_procnumber != $min_procnumber } {
13533         if { $step_procnumber != 1 } {
13534             set procnumber "$min_procnumber to $max_procnumber by step of $step_procnumber"
13535         } else {
13536             set procnumber "$min_procnumber to $max_procnumber"
13537         }
13538     } else {
13539         set procnumber $min_procnumber
13540     }
13541 
13542     # Rang des processus dans l'application
13543     global min_rank_m min_rank_m_regexp
13544     global max_rank_m max_rank_m_regexp
13545 
13546     set min_rank_m 0
13547     catch { set min_rank_m $dico_applis($clef_comp_2.min_rank) }
13548     set max_rank_m $max_procnumber
13549     if {$max_procnumber != 0} {incr max_rank_m -1}
13550     catch { set max_rank_m $dico_applis($clef_comp_2.max_rank) }
13551 
13552     # Cree une boite de dialogue
13553     # --------------------------
13554 
13555     set u .mod_component
13556     catch {destroy $u}
13557     toplevel $u
13558     wm title $u "Edit component"
13559     wm iconname $u "Mod_Compt"
13560     window_position $u 20 20
13561    
13562     set w $u.menu
13563     frame $w
13564     grid $w -row 2 -column 0
13565     grid configure $w -columnspan 2 -padx 4 -pady 4
13566 
13567     frame $w.buttons
13568     pack $w.buttons -side bottom -fill x -pady 2m
13569 
13570     button $w.buttons.dismiss  -text Cancel -command "destroy $u"
13571     button $w.buttons.ok  -text Ok -command "component_update $u $clef_comp_2 $nom_appli $nom_composant \$min_rank_m \$max_rank_m \$color_m"
13572 
13573     pack $w.buttons.dismiss -side left -expand 1 -padx 10
13574     pack $w.buttons.ok -side left -expand 1 -padx 10
13575 
13576     frame $w.sep1 -width 200 -height 2 -borderwidth 1 -relief sunken
13577     pack $w.sep1 -side bottom -fill x -pady 2m -expand 1
13578 
13579     set w $u.char
13580     frame $w
13581     grid $w -row 0 -column 1
13582     grid configure $w -padx 4 -pady 4
13583     set r 1
13584 
13585 #== nom
13586     label $w.label1 -text "Name:"
13587     label $w.label11 -relief sunken -text $nom_composant
13588     grid  $w.label1 $w.label11 -row $r -sticky w
13589     incr r
13590 
13591 #== nom long
13592     label $w.label2 -text "Long name:"
13593     label $w.label22 -relief sunken -text $nom_long_comp
13594     grid  $w.label2 $w.label22 -row $r -sticky w
13595     incr r
13596 
13597 #== nb process
13598     label $w.label3 -text "Number of Processes :"
13599     label $w.label33 -relief sunken -text $procnumber
13600     grid  $w.label3 $w.label33 -row $r -sticky w
13601     incr r
13602 
13603     grid [ frame $w.sep1 -width 430 -height 2 -borderwidth 1 -relief sunken] -row $r -column 0 -pady 5 -columnspan 2
13604     incr r
13605 
13606 #== rang des processus dans l'application
13607     label $w.label4 -text "Ranks of Process :"  -fg #ee3333
13608     frame $w.frrank
13609     trace variable min_rank_m w {entry_forceRegexp {^[0-9]*$} }
13610     trace variable max_rank_m w {entry_forceRegexp {^[0-9]*$} }
13611     pack [entry $w.frrank.e1 -textvariable min_rank_m -width 10 -background white] -side left
13612     pack [label $w.frrank.l1 -text " to "] -side left -padx 2
13613     pack [entry $w.frrank.e2 -textvariable max_rank_m -width 10 -background white] -side left -padx 2
13614     grid $w.label4 $w.frrank -row $r -sticky w
13615     incr r
13616     
13617 #== couleur
13618     global color_m
13619     set color_m $dico_units($clef_comp.colour)
13620     label $w.label5 -text "Color :"
13621     button $w.entry5 -relief sunken -width 20 -text $color_m -background $color_m  -command {
13622         global color_m
13623         set color_m [tk_chooseColor -initialcolor $color_m]
13624         catch {.mod_component.char.entry5 configure -background $color_m}
13625         }
13626     grid  $w.label5 $w.entry5 -row $r -sticky w
13627 
13628     # Ajoute une bulle d'aide pour 'min_rank' and 'max_rank'
13629     set help_text {Enter here the ranks of first and last process of this component as they appear in the application process group.
13630 Process ranks start from 0. Component processes are consecutive.}
13631     entry_help_balloon $w.frrank $help_text
13632 
13633     # Ajoute une bulle d'aide pour 'color'
13634     global no_help_balloon
13635     if { $no_help_balloon == 0 } {
13636         set help_text {Click here to change the color of the component.}
13637         set help_action "help_object %W [list $help_text] message"
13638         bind $w.entry5 <Any-Enter> $help_action
13639         bind $w.entry5 <FocusIn>   $help_action
13640         bind $w.entry5 <Any-Leave>    "help_object_hide"
13641         bind $w.entry5 <FocusOut>     "help_object_hide"
13642         bind $w.entry5 <ButtonPress>  "help_object_hide"
13643         
13644         bind $u <Destroy> "help_object_hide"
13645     }
13646 }
13647 
13648 ret  {entry_help_balloon} (type win , type help_, type text) {
13649 # Programme l'affichage d'un texte d'aide pour la zone de saisie 'win'
13650     global no_help_balloon
13651 
13652     if { $no_help_balloon == 0 } {
13653        set help_action "help_object %W [list $help_text] message"
13654        # Le message d'aide s'affiche quand la souris survole la zone de saisie
13655        bind $win <Any-Enter> $help_action
13656        # et quand le curseur entre dans la zone de saisie
13657        bind $win <FocusIn>   $help_action
13658        # Le message d'aide disparait quand la souris ne survole plus la zone de saisie
13659        bind $win <Any-Leave>    "help_object_hide"
13660        bind $win <FocusOut>     "help_object_hide"
13661        # ou quand l'utilisateur a frappe une touche
13662        bind $win <Any-KeyPress> "help_object_hide"
13663     }
13664 }
13665 
13666 ret  {component_update} (type win , type clef_, type comp , type nom_, type appli , type nom_, type composant , type min_, type rank , type max_, type rank , type couleur) {
13667 #
13668     global dico_applis dico_units
13669 
13670     set min_rank_b $min_rank
13671     set max_rank_b $max_rank
13672     if {$min_rank_b > $max_rank_b} {
13673         set min_rank_b $max_rank
13674         set max_rank_b $min_rank
13675     }
13676 
13677     # Nombre de processus de l'application
13678     catch { set proc_appli $dico_applis(app.$nom_appli.nbr_proc) }
13679     if { [info exists proc_appli] && $max_rank_b >= $proc_appli } {
13680       notice_show "invalid rank of process\n must be less than total number of application processes: $proc_appli" error
13681       return
13682     }
13683     
13684     # Nombre max de processus du composant
13685     set clef_proc $clef_comp.proc
13686     set max_procnumber 0
13687     catch {set max_procnumber $dico_applis($clef_proc.max_value)}
13688     if { $max_procnumber && $max_rank_b >= $max_procnumber } {
13689       notice_show "invalid rank of process\n must be less than maximum number of component processes: $max_procnumber" error
13690       return
13691     }
13692 
13693     # Met a jour le rang des processus dans l'application
13694     set dico_applis($clef_comp.min_rank) $min_rank_b
13695     set dico_applis($clef_comp.max_rank) $max_rank_b
13696     # Met a jour la couleur
13697     set clef_comp_1 $nom_appli.$nom_composant
13698     set dico_units($clef_comp_1.colour) $couleur
13699     
13700     catch {destroy $win}
13701     # Redessine le composant
13702     Draw_one_unit $clef_comp_1
13703 }
13704 
13705 ret  {config_RAZ} () {
13706 # Efface toutes les données de configuration du couplage
13707 #
13708     global dico_experiment dico_applis
13709     global liste_points_i liste_points_o dico_caract_points
13710     global liste_cnx_in dico_cnx_out dico_caract_cnx
13711     global dico_units
13712     global liste_fichiers dico_caract_fichiers
13713     
13714     array unset dico_experiment *
13715     array unset dico_applis *
13716     set liste_points_i {}
13717     set liste_points_o {}
13718     array unset dico_caract_points *
13719     
13720     set liste_cnx_in {}
13721     array unset dico_cnx_out *
13722     array unset dico_caract_cnx *
13723     
13724     array unset dico_units *
13725     set liste_fichiers {}
13726     array unset dico_caract_fichiers *
13727 }
13728 
13729 ret  {config_new} () {
13730 #
13731 # Starts a new coupling configuration
13732     
13733     global env
13734     global dico_applis
13735     global file_lue
13736 
13737     # S'il y a un couplage a l'ecran (au moins une appli chargee)
13738     set continuer 1
13739     set liste_applis {}
13740     catch { set liste_applis $dico_applis(lapplis) }
13741     if {[llength $liste_applis]} {
13742         set rep [tk_messageBox -message "Save current coupling configuration ?" -type yesno -icon question]
13743         if {$rep == "yes"} {
13744             set continuer [sauve_tout]
13745         }
13746     }
13747 
13748     if { $continuer } {
13749         # Efface la config du couplage
13750         config_RAZ
13751         set file_lue ""
13752         wm title .gui "OASIS - New configuration"
13753     
13754         global entityselected
13755         control_entity $entityselected
13756         drawall
13757     }
13758 }
13759 
13760 ret  {config_read} () {
13761 #
13762 # Load a coupling configuration
13763 #
13764 #** Verify that file SCC exists and read its content
13765 #
13766     global env
13767     global dico_applis
13768     global file_lue
13769 
13770     # S'il y a un couplage a l'ecran (au moins une appli chargee)
13771     set continuer 1
13772     set liste_applis {}
13773     catch { set liste_applis $dico_applis(lapplis) }
13774     if {[llength $liste_applis]} {
13775         set rep [tk_messageBox -message "Save current coupling configuration ?" -type yesno -icon question]
13776         if {$rep == "yes"} {
13777             set continuer [sauve_tout]
13778         }
13779     }
13780 
13781     set file ""
13782     if { $continuer } {
13783         set tk_strictMotif 1
13784         set types {
13785             {{Coupling configuration} {scc.xml}}
13786         }
13787         set file [tk_getOpenFile -initialfile scc.xml -title "Load coupling configuration scc.xml"  -filetypes $types ]
13788     }
13789     if {$file == ""} {return}
13790 
13791 #
13792 #** Lecture du fichier .XML
13793 #
13794     if {[file exists $file]} {
13795         .gui configure -cursor watch
13796         update
13797         # Efface la config du couplage
13798         config_RAZ
13799         set erreur [lit_fichier_SCC $file]
13800         .gui configure -cursor ""
13801         
13802         if {! $erreur } {
13803             set file_lue $file
13804             wm title .gui "OASIS - $file"
13805         
13806             global entityselected
13807             control_entity $entityselected
13808             drawall
13809         } else {
13810             # Efface les traces d'une config qui serait lue de façon incomplète
13811             config_RAZ
13812         }
13813     }
13814 }
13815 
13816 ret  {sauve_tout} () {
13817     
13818     # Sauve le SCC.xml
13819     set res_list [sauve_scc]
13820     set result [lindex $res_list 0]
13821 
13822     # Si la sauvegarde du SCC s'est bien passée
13823     if {$result != 0} {
13824         # Répertoire où on a sauvé le SCC
13825         set dir_name [lindex $res_list 1]
13826         # Sauve les SMIOC
13827         set result [sauve_smioc $dir_name]
13828      }
13829      return $result
13830 }
13831 
13832 ret  {sauve_scc} () {
13833 # Génère les fichiers XML de configuration d'un couplage :
13834 #  - le fichier SCC.XML
13835 #
13836     global dico_applis
13837     global dico_units
13838 
13839     set result 1
13840     set dir_name ""
13841 
13842     # Vérifie qu'il y a au moins un composant ou un fichier NetCDF chargé en mémoire
13843     set liste_comp [array names dico_units]
13844     if { [llength $liste_comp] > 0 } {
13845         # Génere le texte XML pour le fichier SCC.xml
13846         set texte_xml [genere_SCC]
13847         
13848         # Si génération possible
13849         if {$texte_xml != ""} {
13850 
13851             # Sauve le texte dans un fichier scc.xml
13852             # Demande le choix du répertoire
13853 
13854             set dir_name [tk_chooseDirectory -title "directory where to save file scc.xml" -mustexist yes]
13855             if {$dir_name != ""} {
13856 
13857                 set nom_fichier_SCC [file join $dir_name scc.xml]           
13858                 set continuer yes
13859                 # Si le fichier existe déjà
13860                 if { [file exists $nom_fichier_SCC] } {
13861                     # Affiche un message d'avertissement
13862                     set continuer [tk_messageBox -message "Overwrite existing file \"$nom_fichier_SCC\" ?" -type yesno -icon question]
13863                 }
13864                 if {$continuer == "yes"} {
13865 
13866                     set fichier_XML [open $nom_fichier_SCC w]
13867                     puts $fichier_XML $texte_xml
13868                     close $fichier_XML
13869                     # if {$si_erreur} { file delete $nom_fichier_SCC }
13870 
13871                     # Si la sauvegarde s'est bien passée
13872                     if {$result != 0} {
13873                         # Sauve les parametres graphiques des composants du couplage dans un fichier XML 
13874                         # ------------------------------------------------------------------------------
13875                         
13876                         # Cree un fichier appelé Graphics.xml dans le même répertoire que le fichier SCC.XML
13877                         set nom_fichier_graphics [file join $dir_name Graphics.xml]
13878                         set fichier_XML [open $nom_fichier_graphics w]
13879                         # Remplit le fichier du texte XML
13880                         puts $fichier_XML [genere_graphics_XML]
13881                         close $fichier_XML
13882                     }
13883                 } else {
13884                     set result 0
13885                 }
13886             } else {
13887                 set result 0
13888             }
13889         } else {
13890             set result 0
13891         }
13892     }
13893     
13894     return [list $result $dir_name]
13895 }
13896 
13897 ret  {sauve_smioc} (optional dir_name ="") {
13898 
13899     # Variables globales
13900     global dico_applis
13901     global dico_units
13902 
13903     set result 1
13904 
13905     # Vérifie qu'il y a au moins un composant ou un fichier NetCDF chargé en mémoire
13906     set liste_comp [array names dico_units]
13907     if { [llength $liste_comp] > 0 } {
13908     
13909         # Génère les SMIOC de tous les composants chargés en mémoire
13910         # ----------------------------------------------------------
13911         if {$dir_name == ""} {
13912             set dir_name [tk_chooseDirectory -title "directory where to save file SMIOC.xml" -mustexist yes]
13913         }
13914         if {$dir_name != ""} {
13915             # Pour toutes les applications chargées en mémoire
13916             foreach nom_appli $dico_applis(lapplis) {
13917                 # Pour tous les composants de cette application
13918                 foreach nom_composant $dico_applis(app.$nom_appli.lcomp) {
13919                     # Détermine le nom du fichier SMIOC (dans le même répertoire que le fichier SCC)
13920                     set nom_fichier [file join $dir_name $nom_appli\_$nom_composant\_smioc.xml]
13921                     set continuer yes
13922                     # Si le fichier existe déjà
13923                     if { [file exists $nom_fichier] } {
13924                         # Affiche un message d'avertissement
13925                         set continuer [tk_messageBox -message "Overwrite existing file \"$nom_fichier\" ?" -type yesno -icon question]
13926                     }
13927                     if {$continuer == "yes"} {
13928                         # Ecrit dans le fichier XML
13929                         set fichier_XML [open $nom_fichier w]
13930                         puts $fichier_XML [genere_SMIOC $nom_appli $nom_composant]
13931                         close $fichier_XML
13932                     } else {
13933                       set result 0
13934                       break
13935                     }
13936                 }
13937                 # Si un composant n'a pas été sauvegardé
13938                 if {$result == 0} {
13939                     # Annule tout
13940                     break
13941                 }
13942             }
13943             return $result
13944         } else {
13945             return 0
13946         }
13947     } else {
13948         return 0
13949     }
13950 }
13951 
13952 ret  {control_delete_entity} () {
13953 #
13954     global entityselected
13955     global objectselected
13956 
13957     switch $entityselected {
13958         CONNECTION {
13959             # Si une connexion est sélectionnée
13960             if {$objectselected != ""} {
13961                 comm_delete $objectselected
13962                 set objectselected ""
13963             }
13964         }
13965         COUPLING_FIELD {
13966             # Les champs ne peuvent pas être supprimés
13967         }
13968         FILE {
13969             # Si un fichier est sélectionné
13970             if {$objectselected != ""} {
13971                 NetCDF_file_delete $objectselected
13972                 set objectselected ""
13973             }
13974         }
13975         COMPONENT {
13976             # a completer
13977         }
13978     }
13979 }
13980 
13981 ret  {control_edit_entity} () {
13982 # action sur le bouton edit
13983     global entityselected
13984     global objectselected tagselected oldcolor
13985     global dico_units
13986 
13987     switch $entityselected {
13988         CONNECTION {
13989             # Si une connexion est sélectionnée
13990             if {$objectselected != ""} {
13991                 comm_edit $objectselected
13992             }
13993         }
13994         FILE {
13995             # Si un fichier est sélectionné
13996             if {$objectselected != ""} {
13997                 set id_fichier $dico_units($objectselected)
13998                 # Permet la modif des param. de ce fichier
13999                 NetCDF_file_edit $id_fichier
14000                 tkwait window .mod_file
14001 
14002                 # Le nom ou la couleur ont pu changer : raffiche le fichier sur le graphe
14003                 set clef ///file///.$id_fichier
14004                 Draw_one_unit $clef
14005         
14006                 # Remet la couleur de l'objet sélectionné
14007                 set w .gui.pr.cpd22.03
14008                 set oldcolor [$w itemcget $tagselected  -fill ]
14009                 $w itemconfigure $tagselected -fill red
14010             }
14011         }
14012         COUPLING_FIELD {
14013             # a completer
14014         }
14015         COMPONENT {
14016             # Si un composant est sélectionné
14017             if {$objectselected != ""} {
14018                 # Si c'est un composant d'application
14019                 if { [info exists dico_units($objectselected)] } {
14020                     component_edit $objectselected
14021                     catch {tkwait window .mod_component}
14022                 
14023                     # La couleur a pu changer : raffiche le composant sur le graphe
14024                     Draw_one_unit $objectselected
14025                     # Remet la couleur de l'objet sélectionné
14026                     set w .gui.pr.cpd22.03
14027                     set oldcolor [$w itemcget $tagselected  -fill ]
14028                     $w itemconfigure $tagselected -fill red
14029                 } else {
14030                     application_edit $objectselected
14031                     catch {tkwait window .mod_appli}
14032                 }
14033             }
14034         }
14035     }
14036 }
14037 
14038 ret  {control_entity} (type entity , optional element ={)} {
14039 # reinitialise la multi-lisbox en fonction de l'entity
14040 
14041     global dico_units
14042     global objectselected tagselected oldcolor
14043     global entityselected
14044     global orderentity
14045     global ordertri
14046     global entity_id
14047 
14048     # Si changement de type d'entités affichées
14049     if { $entity != $entityselected } {
14050     
14051         /*  Si il y avait un autre élément sélectionné (a part les points de couplage)*/
14052         if {$tagselected != ""} {
14053             /*  Désélectionne cet élément sur le graphe*/
14054              w =  .gui.pr.cpd22.03
14055             $w itemconfigure $tagselected -fill $oldcolor
14056         }
14057     }
14058      entityselected =  $entity
14059 
14060     /*  Détermine la liste des lignes de la Multi-list-box*/
14061     switch $entity {
14062         COMPONENT    {
14063             /*  Numero du bouton radio*/
14064              entity = _id 0
14065             /*  Titre des colonnes*/
14066              listopt =  {application component "long name" "start mode" "coupling mode" "nb ret esses"}
14067             # Contenu de la multiliste
14068             set loclist [liste_tous_composants]
14069         }
14070         FILE      (
14071             # type Numero , type du , type bouton , type radio
14072             , type set , type entity_, type id 3
14073             # , type Titre , type des , type colonnes
14074             , type set , type listopt , optional name ="io direction" ="io mode" ="suffix"
14075             # , type Contenu , type de , type la , type multiliste
14076             , type set , type loclist [, type liste_, type tous_, type fichiers]
14077         )
14078         COUPLING_FIELD        {
14079             # Numero du bouton radio
14080             set entity_id 1
14081             # Titre des colonnes
14082             set listopt {name "long name" application component type datatype "I/O"}
14083             # Contenu de la multiliste
14084             set loclist [liste_points_de_couplage]
14085         }
14086         CONNECTION {
14087             /*  Numero du bouton radio*/
14088              entity = _id 2
14089             /*  Titre des colonnes*/
14090              listopt =  {from "from component" to "to component" type datatype}
14091             /*  Contenu de la multiliste*/
14092              loclist =  [liste_toutes_connexions]
14093         }
14094         default {
14095              entity = _id 0
14096              listopt =  {}
14097              loclist =  {}
14098         }
14099     }
14100 
14101     /* ** clear list*/
14102      w =  .gui.pr.toolframe.cpd18.01
14103     /* ==destruction des colonnes*/
14104     foreach col [$w column names] {
14105         $w column delete $col
14106     }
14107     $w delete 0 end
14108 
14109     /*  Initialise la largeur des colonnes*/
14110      nbcol =  [llength $listopt]
14111     for { i =  0} {$i < $nbcol} {incr i} { lenmax = ($i) [string length [lindex $listopt $i]]}
14112 
14113     /*  Calcul de la largeur max des colonnes*/
14114     foreach elm $loclist {
14115          i =  0
14116         foreach col $elm {
14117             if { $i < $nbcol } {
14118                  longueur =  [string length $col]
14119                 if { [string is upper $col] } {
14120                      longueur =  [expr $longueur * 10 / 8]
14121                 }
14122                 if {$longueur > $lenmax($i)} { lenmax = ($i) $longueur}
14123             }
14124             incr i
14125         }
14126     }
14127 
14128     /*  tri de la liste en fonction du critère*/
14129     if { $entity != "COMPONENT" } {
14130          loclist =  [lsort -dictionary -index $orderentity($entity) $loclist]
14131     }
14132 
14133     /*  creation des colonnes*/
14134     for { j =  0} {$j < $nbcol} {incr j} {
14135          col =  [lindex $listopt $j]
14136          nom = _col [string map { " " "-" } $col]
14137         $w column add $nom_col -label $col -width [expr $lenmax($j)+1]
14138         if {[MOD $j 2] == 0} {$w column configure $nom_col -background grey95}
14139         /* =======tri*/
14140         $w label bind $nom_col  <ButtonPress-1> " orderentity = ($entityselected) $j ; control_entity $entityselected "
14141     }
14142 
14143     /*  Insere les éléments*/
14144     foreach elm $loclist {
14145        $w insert end [lrange $elm 0 end-1]
14146     }
14147 
14148     /*  Mémorise l'ordre des éléments*/
14149     array un ordertri = 
14150      i =  0
14151     foreach elm $loclist {
14152          clef =  [lrange $elm end end]
14153          ordertri = ($clef) $i
14154         incr i
14155     }
14156 
14157     .gui.pr.toolframe.buttonfrc.deleteb configure -state disabled
14158     .gui.pr.toolframe.buttonfrc.editb configure -state disabled
14159 
14160     /*  Si il y a un élément de la liste a sélectionner*/
14161     if {$element != ""} {
14162         /*  Sélectionne cet élément*/
14163         $w selection  $ordertri = ($element)
14164         $w yview $ordertri($element)
14165 
14166         switch $entity {
14167             COMPONENT    {
14168                 /*  Dans le futur, on pourra supprimer des composants : s'ils sont optionnels*/
14169                 .gui.pr.toolframe.buttonfrc.deleteb configure -state disabled
14170                 .gui.pr.toolframe.buttonfrc.editb configure -state normal
14171             }
14172             COUPLING_FIELD        {
14173                 /*  Ni edition ni suppression des champs*/
14174                 .gui.pr.toolframe.buttonfrc.deleteb configure -state disabled
14175                 .gui.pr.toolframe.buttonfrc.editb configure -state disabled
14176             }
14177             FILE   -
14178             CONNECTION {
14179                 .gui.pr.toolframe.buttonfrc.deleteb configure -state normal
14180                 .gui.pr.toolframe.buttonfrc.editb configure -state normal
14181             }
14182         }
14183     }
14184 }
14185 
14186 ret  {control_entity_selected} () {
14187 # Met a jour la liste des éléments dans la fenetre de gauche
14188 
14189     global entityselected objectselected tagselected
14190     if {![info exists entityselected]} {return}
14191     
14192     # Teste si l'element selectionne sur le graphe existe toujours
14193     set w .gui.pr.cpd22.03
14194     set selection [$w find withtag $tagselected]
14195     
14196     # Si l'élément selectionne sur le graphe existe toujours
14197     if { $selection != "" } {
14198         # Met a jour la liste en gardant l'élément sélectionné
14199         control_entity $entityselected $objectselected
14200     } else {
14201         # Met a jour la liste
14202         control_entity $entityselected 
14203     }
14204 }
14205 
14206 ret  {control_entity_selectionChanged} (type w , type args) {
14207 #
14208     global dico_units
14209     global entityselected objectselected tagselected oldcolor
14210     global ordertri
14211 
14212     if { [$w curselection] == "" } {
14213         # pass
14214     } else {
14215         switch $entityselected {
14216             COMPONENT    {
14217                 # Dans le futur, on pourra supprimer des composants : s'ils sont optionnels
14218                 .gui.pr.toolframe.buttonfrc.deleteb configure -state disabled
14219                 .gui.pr.toolframe.buttonfrc.editb configure -state normal
14220             }
14221             COUPLING_FIELD        {
14222                 # Ni edition ni suppression des champs
14223                 .gui.pr.toolframe.buttonfrc.deleteb configure -state disabled
14224                 .gui.pr.toolframe.buttonfrc.editb configure -state disabled
14225             }
14226             FILE -
14227             CONNECTION {
14228                 .gui.pr.toolframe.buttonfrc.deleteb configure -state normal
14229                 .gui.pr.toolframe.buttonfrc.editb configure -state normal
14230             }
14231         }
14232 
14233         # Recherche la connexion selectionnée
14234         set rang_selection [$w curselection]
14235 
14236         foreach {clef rang} [array get ordertri] {
14237             if { $rang == $rang_selection } { break }
14238         }
14239 
14240         # Met a jour la sélection sur le graphe
14241         set w .gui.pr.cpd22.03
14242         # Si il y avait un autre objet selectionne
14243         if {$tagselected != ""} {
14244             # Deselectionne ce composant sur le graphe
14245             $w itemconfigure $tagselected -fill $oldcolor
14246         }
14247         set tagselected ""
14248 
14249         # Selon le type d'objets listes
14250         switch $entityselected {
14251             # Applis et composants
14252             COMPONENT    {
14253                 # Si c'est un composant d'application
14254                 if { [info exists dico_units($clef)] } {
14255                     set tagselected unitpoint_color($clef)
14256                 } else {
14257                     # C'est une application : rien à sélectionner sur le graphe
14258                     set tagselected ""
14259                 }
14260                 set objectselected $clef
14261             }
14262             FILE   {
14263                 set tagselected unitpoint_color($clef)
14264                 set objectselected $clef
14265             }
14266             COUPLING_FIELD        {
14267             }
14268             CONNECTION {
14269                 set tagselected tag_fils_connexions($clef)
14270                 set objectselected $clef
14271             }
14272         }
14273 
14274         # Si un objet est selectionne sur le graphe
14275         if { $tagselected != "" } {
14276             # Change la couleur de l'objet sélectionné sur le graphe
14277             set oldcolor [$w itemcget $tagselected  -fill ]
14278             $w itemconfigure $tagselected -fill red
14279         }
14280     }
14281 }
14282 
14283 ret  {date_is_valid_date} (type nom_, type date) {
14284 # Teste si une date est valide
14285     # "nom_date" est le nom d'une var globale de type array
14286     upvar \#0 $nom_date date
14287     
14288     set msg ok
14289     # Recopie les infos de la date (a cause de l'instruction "trace" sur la var globale de nom "$nom_date")
14290     foreach {name value} [array get date] {
14291         set new_date($name) $value
14292     }
14293     
14294     if {$new_date(year)   == ""} {set new_date(year) 0}
14295     if {$new_date(month)  == ""} {set new_date(month) 1}
14296     if {$new_date(day)    == ""} {set new_date(day) 1}
14297     if {$new_date(hour)   == ""} {set new_date(hour) 0}
14298     if {$new_date(minute) == ""} {set new_date(minute) 0}
14299     if {$new_date(second) == ""} {set new_date(second) 0}
14300     
14301     switch [date_nb_days_in_year $new_date(year)] {
14302         366 {     
14303             set daylist [list 31 29 31 30 31 30 31 31 30 31 30 31]
14304         } 
14305         365 {
14306             set daylist [list 31 28 31 30 31 30 31 31 30 31 30 31]
14307         }
14308         360 {
14309             set daylist [list 30 30 30 30 30 30 30 30 30 30 30 30]
14310         }
14311     }
14312     if {$new_date(second) < 0 } {set msg "negative second"}
14313     if {$new_date(second) > 60} {set msg "second > 60"}
14314     if {$new_date(minute) < 0 } {set msg "negative minute"}
14315     if {$new_date(minute) > 60} {set msg "minute > 60"}
14316     if {$new_date(hour)   <  0} {set msg "negative hour"}
14317     if {$new_date(hour)   > 24} {set msg "hour > 24"}
14318     if {$new_date(month)  > 12} {set msg "month > 12"}
14319     if {$new_date(month)  < 1 } {set msg "negative month "}
14320     if {$new_date(year)   < 0 } {set msg "negative year "}
14321     
14322     set maxday [lindex $daylist [expr $new_date(month) -1 ]]
14323     if {$new_date(day) > $maxday} {set msg "invalid day "}
14324     
14325     return $msg
14326 }
14327 
14328 ret  {date_nb_days_in_year} (type year) {
14329 # Détermine le nombre de jours dans une année donnée
14330     set calendar STANDARD
14331     
14332     switch $calendar  {
14333     STANDARD {
14334         if {[expr $year/4.] == [expr int($year/4.)]} {
14335         set nb_days 366
14336         if {[expr $year/100.] == [expr int($year/100.)]}   {set nb_days 365}
14337         if {[expr $year/400.] == [expr int($year/400.)]}   {set nb_days 366}
14338         } else {
14339         set nb_days 365
14340         }
14341     }
14342     NOLEAP {
14343         set nb_days 365
14344     }
14345     360 {
14346         set nb_days 360
14347     }
14348     JULIAN {
14349         if {[expr $year/4.] == [expr int($year/4.)]} {
14350         set nb_days 366
14351         } else {
14352         set nb_days 365
14353         }
14354     }
14355     }
14356     return $nb_days
14357 }
14358 
14359 ret  {demi_chemin_1} (type x1 , type y1 , type xm , type ym , type cote , type clef_, type source) {
14360 # Calcule le chemin du composant source au point milieu (xm, ym)
14361     #   - cote :        vaut bas, droit ou gauche : il indique le côté par lequel on approche le point milieu
14362     #   - clef_source : clef d'acces au composant source
14363         global dico_units
14364         global DRAW
14365     
14366         #=========de dessous a dessus
14367         if { $cote == "bas" } {
14368         
14369             # Quitte le point source par le dessous
14370             set y10 [expr $y1 +17 ]
14371             # Fait le tour du coin inferieur du composant source
14372             set coin_y [expr $dico_units($clef_source.coor_y) + $DRAW(heigthunit) + 17]
14373             if { $coin_y < $ym + 17 } {
14374                 set coin_y [expr $ym + 17]
14375             }
14376 
14377             set milieu_x [expr ($x1 + $xm) / 2]
14378             # Si on va de gauche a droite
14379             if { $x1 < $xm } {
14380                 # Fait le tour du coin inferieur droit du composant source
14381                 set coin_x [expr $dico_units($clef_source.coor_x) + $dico_units($clef_source.largeur) + 17]
14382                 # if { $coin_x < $milieu_x } { set coin_x $milieu_x }
14383                 if { $coin_x < $xm } { set coin_x $xm }
14384             } else {
14385                 # Fait le tour du coin inferieur gauche du composant source
14386                 set coin_x [expr $dico_units($clef_source.coor_x) - 17]
14387                 # if { $coin_x > $milieu_x } { set coin_x $milieu_x }
14388                 if { $coin_x > $xm } { set coin_x $xm }
14389             }
14390             
14391             if { $coin_x == $xm } {
14392                 return "$x1 $y1 $x1 $y10 $coin_x $coin_y $xm $ym"
14393             } else {
14394                 # Approche le point milieu par le dessous
14395                 set xm1 $xm
14396                 set ym1 [expr $ym +20]
14397                 
14398                 return "$x1 $y1 $x1 $y10 $coin_x $coin_y $xm1 $ym1 $xm $ym"
14399             }
14400         
14401         #=========de dessus a dessous
14402         } else {
14403             # Approche le point milieu par le coté gauche ou droit
14404             if { $cote == "gauche" } {
14405                 # Approche le point milieu par la gauche
14406                 set xm1 [expr $xm -20]
14407             } else {
14408                 set xm1 [expr $xm +20]
14409             }
14410             set ym1 $ym
14411             # Quitte le point source par le dessous
14412             set y10 [expr $y1 +17 ]
14413             return "$x1 $y1 $x1 $y10 $xm1 $ym1 $xm $ym"
14414         }
14415 }
14416 
14417 ret  {demi_chemin_2} (type xm , type ym , type x2 , type y2 , type cote , type clef_, type cible) {
14418 # Calcule le chemin du point milieu (xm, ym) au composant cible
14419     #   - cote :        vaut bas, droit ou gauche : il indique le côté par lequel on approche le point milieu
14420     #                   == le côté opposé de celui par lequel on quitte
14421     #   - clef_cible : clef d'acces au composant cible
14422         global dico_units
14423         global DRAW
14424         #=========de dessous a dessus
14425         if { $cote == "bas" } {
14426         
14427             # Fait le tour du coin superieur du composant cible
14428             set coin_y [expr $dico_units($clef_cible.coor_y) - 17]
14429             if { $coin_y > $ym - 17 } {
14430                 set coin_y [expr $ym - 17]
14431             }
14432             
14433             set milieu_x [expr ($xm + $x2) / 2]
14434             # Si on va de gauche a droite
14435             if { $xm < $x2 } {
14436                 # Fait le tour du coin superieur gauche du composant cible
14437                 set coin_x [expr $dico_units($clef_cible.coor_x) - 17]
14438                 # if { $coin_x > $milieu_x } { set coin_x $milieu_x }
14439                 if { $coin_x > $xm } { set coin_x $xm }
14440             } else {
14441                 # Fait le tour du coin superieur droit du composant cible
14442                 set coin_x [expr $dico_units($clef_cible.coor_x) + $dico_units($clef_cible.largeur) + 17]
14443                 # if { $coin_x < $milieu_x } { set coin_x $milieu_x }
14444                 if { $coin_x < $xm } { set coin_x $xm }
14445             }
14446             #  Arrive à dest. par le dessus
14447             set y20 [expr $y2 - 17]
14448             
14449             if { $coin_x == $xm } {
14450                 return "$xm $ym $coin_x $coin_y $x2 $y20 $x2 $y2"
14451             } else {
14452                 # Quitte le point milieu par le dessus
14453                 set xm1 $xm
14454                 set ym1 [expr $ym -20]
14455                 return "$xm $ym $xm1 $ym1 $coin_x $coin_y $x2 $y20 $x2 $y2"
14456             }
14457             
14458         #=========de dessus a dessous
14459         } else {
14460             # Quitte le point milieu par le coté gauche ou droit
14461             if { $cote == "gauche" } {
14462                 # Quitte le point milieu par la droite
14463                 set xm1 [expr $xm +20]
14464             } else {
14465                 set xm1 [expr $xm -20]
14466             }
14467             set ym1 $ym
14468             #  Arrive à dest. par le dessus
14469             set y20 [expr $y2 - 17]
14470             return "$xm $ym $xm1 $ym1 $x2 $y20 $x2 $y2"
14471         }
14472 }
14473 
14474 ret  {dialog_controls} (type win) {
14475 return "$win.controls"
14476 }
14477 
14478 ret  {dialog_create} (type class , optional win =auto) {
14479 #
14480     if {$win == "auto"} {
14481         set count 0
14482         set win ".dialog[incr count]"
14483         while {[winfo exists $win]} {
14484             set win ".dialog[incr count]"
14485         }
14486     }
14487     catch {destroy $win}
14488     toplevel $win -class $class
14489 
14490     frame $win.info
14491     pack $win.info -expand yes -fill both -padx 4 -pady 4
14492 
14493     frame $win.sep -height 2 -borderwidth 1 -relief sunken
14494     pack $win.sep -fill x -pady 4
14495 
14496     frame $win.controls
14497     pack $win.controls -fill x -padx 4 -pady 4
14498 
14499     wm title $win $class
14500     wm group $win .
14501 
14502     after idle [format {
14503         update idletasks
14504         wm minsize %s [winfo reqwidth %s] [winfo reqheight %s]
14505     } $win $win $win]
14506 
14507     return $win
14508 }
14509 
14510 ret  {dialog_info} (type win) {
14511 return "$win.info"
14512 }
14513 
14514 ret  {dialog_safeguard} (type win) {
14515 if {[lsearch [bindtags $win] modalDialog] < 0} {
14516         bindtags $win [linsert [bindtags $win] 0 modalDialog]
14517     }
14518 }
14519 
14520 ret  {dialog_wait} (type win , type varName) {
14521 #
14522     dialog_safeguard $win
14523 
14524     set x [expr [winfo rootx .]+50]
14525     set y [expr [winfo rooty .]+50]
14526     wm geometry $win "+$x+$y"
14527 
14528     wm deiconify $win
14529     grab set $win
14530 
14531     vwait $varName
14532 
14533     grab release $win
14534     wm withdraw $win
14535 }
14536 
14537 ret  {distmin} (type x1 , type y1 , type x2 , type y2) {
14538 global DRAW
14539         set dist [expr sqrt(($x2-$x1)*($x2-$x1)+($y2-$y1)*($y2-$y1))]
14540         if {$dist < $DRAW(maxi_line_size)} {return 1} else {return 0}
14541 }
14542 
14543 ret  {distming} (type x1 , type y1 , type x2 , type y2) {
14544 global DRAW
14545         set dist [expr sqrt(($x2-$x1)*($x2-$x1)+($y2-$y1)*($y2-$y1))]
14546         if {$dist < $DRAW(grouped_maxi_line_size)} {return 1} else {return 0}
14547 }
14548 
14549 ret  {draw_print} () {
14550 #
14551 #
14552 #**** draw_print - print the canvas
14553 #
14554 #     Purpose:
14555 #     --------
14556 #     This tcl/tk script print the canvas directly to a pinter
14557 #     or to a postcripts file
14558 #
14559 #     Interface:
14560 #     ----------
14561 #         draw_print
14562 #
14563 #***  Externals:
14564 #     ----------
14565 #     This code uses Tcl/Tk
14566 #
14567 #     References:
14568 #     -----------
14569 #     Effective Tcl/Tk Programming, M. Harrison and M. McLennan,
14570 #     Addison-Wesley professional computing series, 1998
14571 #
14572 #     History:
14573 #     -------
14574 #       Version   Programmer     Date        Description
14575 #       -------   ----------     ----        -----------
14576 #       0.0       Julien         1999/08/05       created
14577 #*----------------------------------------------------------------
14578 #
14579 #** Print the canvas in a postcript file
14580 #
14581 #
14582     set win .gui.pr.cpd22.03
14583 
14584     set x0 0
14585     set y0 0
14586 
14587     set x1 [winfo width $win]
14588     set y1 [winfo height $win]
14589 
14590     foreach {x0 y0 x1 y1} [$win bbox all] {}
14591     set w [expr $x1-$x0]
14592     set h [expr $y1-$y0]
14593 
14594     return [$win postscript -x $x0 -y $y0 -width $w -height $h]
14595 }
14596 
14597 ret  {drawall} () {
14598 #
14599     global DRAW
14600     global zoomfactor
14601 
14602     .gui configure -cursor watch
14603 
14604     #-------------------------------------
14605     #** variables a calculer avant de tracer
14606     #-------------------------------------
14607     Draw_set_init_variables
14608 
14609     #-------------------------------------
14610     #** fenetre d'affichage ,clean
14611     #-------------------------------------
14612     set w $DRAW(window)
14613     $w delete all
14614     $w dtag all
14615 
14616     #-------------------------------------
14617     #** affichage des unites
14618     #-------------------------------------
14619     Draw_drawunit
14620 
14621     #-------------------------------------
14622     #** affichage des communications
14623     #-------------------------------------
14624     Draw_comm
14625 
14626     #-------------------------------------
14627     #** bulle aide objet
14628     #-------------------------------------
14629     set u .ballon_object
14630     catch {destroy $u}
14631     toplevel $u -borderwidth 1 -relief flat
14632     wm iconify $u
14633     label $u.text -text "" -background #fefed4 -foreground black -font {helvetica 10} -justify left
14634     pack $u.text -side left -anchor w
14635     wm overrideredirect $u 1
14636     wm withdraw $u
14637 
14638     .gui configure -cursor {}
14639 }
14640 
14641 ret  {dump_array} (type nom_, type dico , optional match_expression =*) {
14642 # Pour debug : dump le contenu d'un tableau
14643     upvar 1 $nom_dico dico
14644     puts "${nom_dico}($match_expression) :"
14645     foreach {n v} [array get dico $match_expression] {
14646         puts "$n : $v"
14647     }
14648 }
14649 
14650 ret  {entryLimit} (type entry , type traceProc , optional init ={)} {
14651 ## entryLimit - a convenience proc for managing traces on entry widgets
14652 ## Perhaps to make this even nicer, it could create a textvar for the
14653 ## user with the name the same as the entry?
14654 # ARGS: entry       - the entry widget to trace
14655 #   traceProc   - trace procedure
14656 #   init        - initial value to use, defaults to {}
14657 /*  If traceProc=={}, then all write traces on the entry's textvar are*/
14658 /*  deleted.  This doesn't delete array traces.*/
14659 /** 
14660  */
14661 
14662 
14663     if [string match {} [ var =  [$entry cget -textvariable]]] {
14664     return -code error "-textvariable not  for =  entry \"$entry\""
14665     }
14666 
14667     if {[string compare $traceProc {}]} {
14668     /*  TextVars are always considered global*/
14669     uplevel \/* 0 [list set $var $init]*/
14670     uplevel \/* 0 [list trace variable $var w $traceProc]*/
14671     if {[catch {uplevel \/* 0 [list set $var $init]} err]} {*/
14672         /*  If this errors out, the $init value was bad, or*/
14673         /*  something was wrong with the traceProc*/
14674         return -code error "an error was received ting =  the initial value of \"$var\" to \"$init\".  Make sure the value is valid and the traceProc is functional:\n$err"
14675     } else {
14676         /*  Do you really want to delete the trace when*/
14677         /*  destroying the entry?*/
14678         /* bind $entry <Destroy> [list + trace vdelete $var w $traceProc]*/
14679         return
14680     }
14681     }
14682     foreach p [uplevel \/* 0 [list trace vinfo $var]] {*/
14683     if {[string match w [lindex $p 0]]} {
14684         uplevel \/* 0 trace vdelete [list $var] $p*/
14685     }
14686     }
14687 }
14688 
14689 ret  {entry_forceAlpha} (type name , type el , type op) {
14690 global $name ${name}_alpha
14691     if [string comp {} $el] {
14692     set old  ${name}_alpha\($el)
14693     set name $name\($el)
14694     } else { set old ${name}_alpha }
14695     if ![regexp {^[a-zA-Z]*$} [set $name]] {
14696     set $name [set $old]
14697     bell; return
14698     }
14699     set $old [set $name]
14700 }
14701 
14702 ret  {entry_forceInt} (type name , type el , type op) {
14703 global $name ${name}_int
14704     if [string comp {} $el] {
14705     set old  ${name}_int\($el)
14706     set name $name\($el)
14707     } else { set old ${name}_int }
14708     if ![regexp {^[-+]?[0-9]*$} [set $name]] {
14709     set $name [set $old]
14710     bell; return
14711     }
14712     set $old [set $name]
14713 }
14714 
14715 ret  {entry_forceInt_min_max} (type min , type max , type name , type el , type op) {
14716 global $name ${name}_int
14717     if [string comp {} $el] {
14718     set old  ${name}_int\($el)
14719     set name $name\($el)
14720     } else { set old ${name}_int }
14721     if ![regexp {^[-+]?[0-9]*$} [set $name]] {
14722     set $name [set $old]
14723     bell; return
14724     }
14725     if {[set $name] != ""} {
14726        if {[set $name] > $max} {set $name $max}
14727        if {[set $name] < $min} {set $name $min}
14728     }
14729     set $old [set $name]
14730 }
14731 
14732 ret  {entry_forceLen} (type len , type name , type el , type op) {
14733 global $name ${name}_len
14734     if [string comp $el {}] {
14735     set old  ${name}_len\($el)
14736     set name $name\($el)
14737     } else { set old ${name}_len }
14738     if {[string length [set $name]] > $len} {
14739     set $name [set $old]
14740     bell; return
14741     }
14742     set $old [set $name]
14743 }
14744 
14745 ret  {entry_forceReal} (type name , type el , type op) {
14746 global $name ${name}_real
14747     if [string comp {} $el] {
14748     set old  ${name}_real\($el)
14749     set name $name\($el)
14750     } else { set old ${name}_real }
14751     if ![regexp {^[-+]?[0-9]*\.?[0-9]*([0-9]\.?e[-+]?[0-9]*)?$} [set $name]] {
14752     set $name [set $old]
14753     bell; return
14754     }
14755     set $old [set $name]
14756 }
14757 
14758 ret  {entry_forceReal_min_max} (type min , type max , type name , type el , type op) {
14759 global $name ${name}_int
14760 global $name ${name}_real
14761     if [string comp {} $el] {
14762     set old  ${name}_real\($el)
14763     set name $name\($el)
14764     } else { set old ${name}_real }
14765     if ![regexp {^[-+]?[0-9]*\.?[0-9]*([0-9]\.?e[-+]?[0-9]*)?$} [set $name]] {
14766     set $name [set $old]
14767     bell; return
14768     }
14769     if {[set $name] != ""} {
14770        if {[set $name] > $max} {set $name $max}
14771        if {[set $name] < $min} {set $name $min}
14772     }
14773     set $old [set $name]
14774 }
14775 
14776 ret  {entry_forceRegexp} (type regexp , type name , type el , type op) {
14777 global $name ${name}_regexp
14778     if [string comp {} $el] {
14779     set old  ${name}_regexp\($el)
14780     set name $name\($el)
14781     } else { set old ${name}_regexp }
14782     if ![regexp $regexp [set $name]] {
14783     set $name [set $old]
14784     bell; return
14785     }
14786     set $old [set $name]
14787 }
14788 
14789 ret  {entry_forceValue} (type default , type name , type el , type op) {
14790 global $name
14791     if [string comp {} $el] { set name $name\($el) }
14792     if ![info exists $name] { set $name $default }
14793 }
14794 
14795 ret  {experiment_settings} () {
14796 # boite de dialogue pour saisir les parametres de l'expérience et du run Oasis
14797 
14798     global dico_experiment dico_applis
14799 
14800     # Prépare les variables à saisir
14801     global local_name_m long_name_m start_mode_m drv_procs_m
14802     global expe_start_date expe_end_date
14803     global run_start_date run_end_date
14804     
14805     # Prépare les variables à saisir
14806     set local_name_m ""
14807     catch {set local_name_m $dico_experiment(name)}
14808     set long_name_m ""
14809     catch {set long_name_m $dico_experiment(long_name)}
14810     set start_mode_m ""
14811     catch {set start_mode_m $dico_experiment(start_mode)}
14812     set drv_procs_m ""
14813     catch {set drv_procs_m $dico_experiment(drv_procs)}
14814 
14815     # Initialisation des quatres dates
14816     foreach element { year month day hour minute second } {
14817         set expe_start_date($element) 1
14818         catch {set expe_start_date($element) $dico_experiment(start.$element)}
14819         set expe_end_date($element) 1
14820         catch {set expe_end_date($element) $dico_experiment(end.$element)}
14821         set run_start_date($element) 1
14822         catch {set run_start_date($element) $dico_experiment(run_start.$element)}
14823         set run_end_date($element) 1
14824         catch {set run_end_date($element) $dico_experiment(run_end.$element)}
14825     }
14826     
14827 
14828     # Liste des modes de démarrage autorisés
14829     # --------------------------------------
14830     
14831     set si_autorise_mode_spawn 1
14832     set si_autorise_mode_notspawn 1
14833     
14834     set liste_applis {}
14835     catch { set liste_applis $dico_applis(lapplis) }
14836     foreach nom_appli $liste_applis {
14837         set start_mode $dico_applis(app.$nom_appli.start_mode)
14838         switch $start_mode {
14839             # Si une appli ne marche que dans un mode donné, l'autre mode est interdit pour toutes les autres applis
14840             spawn     { set si_autorise_mode_notspawn 0 }
14841             notspawn  { set si_autorise_mode_spawn    0 }
14842         }
14843     }
14844     # Si il y a des restrictions incompatibles .... On n'y peut rien
14845     set options_start_mode {}
14846     if { $si_autorise_mode_notspawn } {
14847         lappend options_start_mode not_spawn
14848     }
14849     if { $si_autorise_mode_spawn } {
14850         lappend options_start_mode spawn
14851     }
14852     
14853     # Crée une boite de dialogue
14854     set u .set_experiment
14855     catch {destroy $u}
14856     toplevel $u
14857     wm title $u "Experiment settings"
14858     wm iconname $u "settings"
14859     window_position $u 200 80
14860 
14861     set w [frame $u.boutons]
14862     pack $w -side bottom -fill x -pady 2m -padx 2m
14863     button $w.dismiss -text Cancel -command "destroy $u"
14864     pack $w.dismiss  -side left -expand 1
14865     button $w.ok -text Ok -command "experiment_update $u \$local_name_m \$long_name_m \$start_mode_m \$drv_procs_m"
14866     pack $w.ok  -side left -expand 1
14867     
14868     frame $u.sep -width 100 -height 2 -borderwidth 1 -relief sunken
14869     pack $u.sep -side bottom
14870 
14871     set w [frame $u.char -borderwidth 1 -relief sunken]
14872     pack $w  -side top -expand 1 -pady 10 -padx 10 -ipadx 3 -ipady 5
14873     set r 1
14874 
14875 #== nom
14876     label $w.label1 -text "Experiment name: " -fg #ee3333
14877     entry $w.entry1  -textvariable local_name_m -width 15 -background white
14878     grid  $w.label1 $w.entry1 -row $r -sticky w
14879     incr r
14880 
14881     # Ajoute une bulle d'aide pour 'Experiment name'
14882     set help_text {This entry is mandatory.}
14883     entry_help_balloon $w.entry1 $help_text
14884 
14885 #== Long name
14886     label $w.label2 -text "Long name : "
14887     entry $w.entry2  -textvariable long_name_m -width 25 -background white
14888     grid  $w.label2 $w.entry2 -row $r -sticky w
14889     incr r
14890 
14891     # Ajoute une bulle d'aide pour 'Long name'
14892     set help_text {Enter here a description of the experiment.}
14893     entry_help_balloon $w.entry2 $help_text
14894 
14895 #== Start mode
14896     label $w.label3 -text "Start mode : " -fg #ee3333
14897     combobox_lim $w.cmb3 $options_start_mode {} start_mode_m 10 $start_mode_m
14898     grid  $w.label3 $w.cmb3 -row $r -sticky w
14899     incr r
14900 
14901     # Ajoute une bulle d'aide pour 'Start mode'
14902     set help_text {Choose the mode into which all applications of the coupled model will be started: either spawn or not spawn, (see User Guide section 3.1); this user’s choice, restricted by the possibilities given in the Application Descriptions, determines the way the applications should be started in the run script.}
14903     entry_help_balloon $w.cmb3 $help_text
14904 
14905 #== nombre de processus du driver
14906     trace variable drv_procs_m w {entry_forceRegexp {^[0-9]*$} }
14907     label $w.label4 -text "Number of processes for OASIS driver : " -fg #ee3333
14908     entry $w.entry4  -textvariable drv_procs_m -width 10 -background white
14909     grid  $w.label4 $w.entry4 -row $r -sticky w
14910     incr r
14911 
14912     # Ajoute une bulle d'aide pour 'process number'
14913     set help_text {Enter the number of processes used for the OASIS4 Driver/Transformer.}
14914     entry_help_balloon $w.entry4 $help_text
14915 
14916 
14917     # Start/End Experiment/Run dates dans un autre cadre en-dessous
14918     # -------------------------------------------------------------
14919     
14920     frame $u.sep2 -width 100 -height 2 -borderwidth 1 -relief sunken
14921     pack $u.sep2 -side top
14922 
14923     set w [frame $u.char2 -borderwidth 1 -relief sunken]
14924     pack $w  -side top -expand 1 -pady 10 -padx 10 -ipadx 3 -ipady 5
14925     set r 0
14926 
14927     # Limitation des valeurs possibles
14928     foreach date {expe_start_date expe_end_date run_start_date run_end_date} {
14929         trace variable ${date}(year) w   {entry_forceInt_min_max -500000 500000}
14930         trace variable ${date}(month) w  {entry_forceInt_min_max 1 12}
14931         trace variable ${date}(day) w    {entry_forceInt_min_max 1 31}
14932         trace variable ${date}(hour) w   {entry_forceInt_min_max 0 24}
14933         trace variable ${date}(minute) w {entry_forceInt_min_max 0 60}
14934         trace variable ${date}(second) w {entry_forceReal_min_max 0 60}
14935     }
14936 
14937     # Organise la saisie des quatre dates sur quatre lignes
14938     #    avec les années, mois, jours, etc... disposés en colonnes
14939     
14940     # Titre des colonnes        
14941     label $w.l11 -text "Year:"
14942     label $w.l12 -text "Month:"
14943     label $w.l13 -text "Day:"
14944     label $w.l14 -text "Hour:"
14945     label $w.l15 -text "Minute:"
14946     label $w.l16 -text "Second:"
14947     # Disposition des titres sur la première ligne
14948     for {set i 1} {$i <= 6} {incr i} {
14949         grid $w.l1$i  -row $r -column $i -pady 2 -padx 4 -sticky w
14950     }
14951     incr r
14952 
14953     # Crée les zones de saisie
14954     foreach date {expe_start_date expe_end_date run_start_date run_end_date} {
14955         foreach {rank time_unit} {1 year 2 month 3 day 4 hour 5 minute 6 second} {
14956             entry $w.${date}a$rank -textvariable ${date}($time_unit) -width 7 -background white -justify right
14957 
14958             frame $w.${date}fram$rank
14959             button $w.${date}fram$rank.plus  -text "+" -padx 2m -pady 0  -font "helvetica -10 bold" -command "increment_date ${date} $time_unit  1"
14960             button $w.${date}fram$rank.moins -text "-"  -padx 2m -pady 0 -font "helvetica -10 bold" -command "increment_date ${date} $time_unit -1"
14961         }
14962 
14963         # Disposition des champs en colonnes
14964         for {set i 1} {$i <= 6} {incr i} {
14965             grid $w.${date}a$i   -row $r -column $i -pady 0 -padx 4 -sticky w
14966         }
14967         incr r
14968         # Disposition des boutons "+" "-" en colonnes
14969         for {set i 1} {$i <= 6} {incr i} {
14970             grid $w.${date}fram$i -row $r -column $i 
14971             pack $w.${date}fram$i.moins -side left -pady 0 -padx 0 
14972             pack $w.${date}fram$i.plus -side right -pady 0 -padx 0 
14973         }
14974         incr r
14975         
14976         # Saute une ligne entre les dates de "Experiment" et les dates du "Run"
14977         if {$r == 5} {set r 6}
14978     }
14979     
14980     # La ligne 5 est vide : on lui donne une hauteur minimum
14981     grid rowconfigure $w 5 -minsize 40
14982     
14983     # Titre des lignes
14984     set r 1
14985     foreach {abbrev titre} { e_start "Experiment start date" e_end "Experiment end date" } {
14986         label $w.$abbrev -text $titre  -anchor w  -width 20
14987         # Disposé en colonne 0
14988         grid $w.$abbrev  -row $r -column 0
14989         incr r 2
14990     }
14991     incr r
14992     foreach {abbrev titre} { r_start "Run start date" r_end "Run end date" } {
14993         label $w.$abbrev -text $titre  -anchor w  -width 20
14994         # Disposé en colonne 0
14995         grid $w.$abbrev  -row $r -column 0
14996         incr r 2
14997     }
14998 
14999     # Ajoute une bulle d'aide pour 'Run start and end date'
15000     array set help_text_array {
15001         run_start_date
15002         {Enter the start date of the run; the start date has to correspond to the lower bound of the time interval which is represented by the first time step of the run.}
15003         run_end_date
15004         {Enter the end date of the run; the end date has to correspond to the upper bound of the time interval which is represented by the last time step of the run. Note that the end date of the current run has to be used as start date for the subsequent run.} 
15005     }
15006     
15007     foreach date {run_start_date run_end_date} {
15008         for {set i 1} {$i <= 6} {incr i} {
15009             entry_help_balloon $w.${date}a$i $help_text_array($date)
15010         }
15011     }
15012 }
15013 
15014 ret  {experiment_update} (type win , type local_, type name , type long_, type name , type start_, type mode , type drv_, type procs) {
15015 # Valide la saisie des paramètres de l'expérience et du run
15016     global dico_experiment dico_applis
15017     
15018     global expe_start_date expe_end_date
15019     global run_start_date run_end_date
15020 
15021     # Experiment name is required
15022     if {$local_name == ""} {
15023         notice_show "Experiment name is required !" error
15024         focus .set_experiment.entry1
15025         return error
15026     }
15027     
15028     # Teste les quatres dates saisies
15029     foreach {date_var_name date_long_name} {expe_start_date "Experiment start date" 
15030         expe_end_date "Experiment end date" run_start_date "Run start date" run_end_date "Run end date" } {
15031         set msg [date_is_valid_date $date_var_name]
15032         if { $msg != "ok" } {
15033             notice_show "$date_long_name is invalid !" error
15034             focus .set_experiment.char2.${date_var_name}a1
15035             return error
15036         }
15037     }
15038            
15039     # Sauve en mémoire toutes les valeurs saisies
15040     set dico_experiment(name) $local_name
15041     set dico_experiment(long_name) $long_name
15042     set dico_experiment(start_mode) $start_mode
15043     set dico_experiment(drv_procs) $drv_procs
15044 
15045     # Sauve en mémoire les quatres dates
15046     foreach element { year month day hour minute second } {
15047         set dico_experiment(start.$element)     $expe_start_date($element)
15048         set dico_experiment(end.$element)       $expe_end_date($element)
15049         set dico_experiment(run_start.$element) $run_start_date($element)
15050         set dico_experiment(run_end.$element)   $run_end_date($element)
15051     }
15052     
15053     destroy $win
15054 }
15055 
15056 ret  {font_view_update} (optional font ={) {dummy {}}} {
15057 global dxf
15058    cur = _font [get_cur_font]
15059   .choose_font.view configure -font "$cur_font"
15060   .choose_font.view configure  -foreground $dxf(choose_font_cb_color)
15061 }
15062 
15063 ret  {genere_SCC} () {
15064 # Génère le texte du fichier SCC.XML au format XML, bien entendu
15065 #
15066     global dico_experiment
15067     global dico_applis
15068     global dico_units
15069     global oasis4_version
15070 
15071     # Texte retourné par la procédure
15072     set texte_xml ""
15073 
15074     # Vérifie qu'il y a au moins un composant ou un fichier NetCDF chargé en mémoire
15075     set liste_comp [array names dico_units]
15076     if { [llength $liste_comp] > 0 } {
15077         # Si toutes les infos générales nécessaires sont renseignées
15078         if {   [info exists dico_experiment(name)]
15079             && [info exists dico_experiment(start_mode)]
15080             && [info exists dico_experiment(drv_procs)]
15081             && [info exists dico_experiment(start.year)]
15082             && $dico_experiment(name) != ""
15083             && $dico_experiment(drv_procs) != ""  } {
15084                         
15085             # Vérifie la compatibilité des applis avec le mode de lancement choisi
15086             # --------------------------------------------------------------------
15087             
15088             # Analyse le mode de démarrage requis par les applications
15089             set si_autorise_mode_notspawn 1
15090             set si_autorise_mode_spawn    1
15091             set liste_applis $dico_applis(lapplis)
15092             foreach nom_appli $liste_applis {
15093                 set start_mode $dico_applis(app.$nom_appli.start_mode)
15094                 switch $start_mode {
15095                     # Si une appli ne marche que dans un mode donné, l'autre mode est interdit pour toutes les autres applis
15096                     spawn     { set si_autorise_mode_notspawn 0 }
15097                     notspawn  { set si_autorise_mode_spawn    0 }
15098                 }
15099             }
15100             
15101             # Recherche une incompatibilité éventuelle
15102             set si_erreur 0
15103             switch $dico_experiment(start_mode) {
15104                 spawn {
15105                     if {$si_autorise_mode_spawn == 0} {set si_erreur 1}
15106                 }
15107                 not_spawn {
15108                     if {$si_autorise_mode_notspawn == 0} {set si_erreur 1}
15109                 }
15110             }
15111             
15112             # Si erreur, message d'erreur
15113             if {$si_erreur} {
15114                 notice_show "Experiment start mode ($dico_experiment(start_mode)) is incompatible with one or more application.
15115 Please, change experiment stat mode in menu 'Experiment settings' !"
15116                 return ""
15117             }
15118             
15119             # Recherche le N° de version d'OASIS4
15120             set nom_appli [lindex $liste_applis 0]
15121             set oasis4_version OASIS4_0_2
15122             if {[info exists dico_applis(app.$nom_appli.oasis4_version)] } {
15123                 set oasis4_version  $dico_applis(app.$nom_appli.oasis4_version)
15124             }
15125     
15126             # Crée le document XML
15127             set doc [dom::DOMImplementation create]
15128     
15129             # Crée l'élément SCC racine
15130             set scc_element [::dom::document createElement $doc scc]
15131             # Ses attributs
15132             dom::element setAttribute $scc_element xmlns "http://www.cerfacs.fr/PRISM/XML/1.1"
15133             dom::element setAttribute $scc_element "xmlns:xsi" "http://www.w3.org/2001/XMLSchema-instance"
15134             dom::element setAttribute $scc_element "xsi:schemaLocation" "http://www.cerfacs.fr/PRISM/XML/1.1"
15135             dom::element setAttribute $scc_element oasis4_version $oasis4_version
15136     
15137             # Crée l'élément "experiment"
15138             set experiment_elt [::dom::document createElement $scc_element experiment]
15139             # Ses attributs
15140             dom::element setAttribute $experiment_elt local_name $dico_experiment(name)
15141             if { [info exists dico_experiment(long_name)] } {
15142                 dom::element setAttribute $experiment_elt long_name $dico_experiment(long_name)
15143             }
15144             dom::element setAttribute $experiment_elt start_mode $dico_experiment(start_mode)
15145             
15146                 # Crée le sous-élément "driver"
15147                 set driver_elt [::dom::document createElement $experiment_elt driver]
15148                     set node [::dom::document createElement $driver_elt nbr_procs]
15149                     set nbr_procs $dico_experiment(drv_procs)
15150                     ::dom::document createTextNode $node $nbr_procs
15151     
15152                 # Crée le sous-élément "Experiment start date"
15153                 set start_date_elt [::dom::document createElement $experiment_elt start_date]
15154                 genere_element_date $start_date_elt "start"
15155 
15156                 # Crée le sous-élément "Experiment end date"
15157                 set end_date_elt   [::dom::document createElement $experiment_elt end_date]
15158                 genere_element_date $end_date_elt "end"
15159     
15160             set run_elt [::dom::document createElement $scc_element run]
15161                 # Crée le sous-élément "Run start date"
15162                 set start_date_elt [::dom::document createElement $run_elt start_date]
15163                 genere_element_date $start_date_elt "start"
15164                 # Crée le sous-élément "Run end date"
15165                 set end_date_elt   [::dom::document createElement $run_elt end_date]
15166                 genere_element_date $end_date_elt "end"
15167     
15168             # Pour toutes les applications chargées en mémoire
15169             foreach nom_appli $liste_applis {
15170                 set appli_elt [::dom::document createElement $scc_element application]
15171     
15172                 # Test si toutes les données sont renseignées
15173                 if {   [info exists dico_applis(app.$nom_appli.executable_name)] 
15174                     && [info exists dico_applis(app.$nom_appli.redirect)] } {
15175         
15176                     # Ses attributs
15177                     dom::element setAttribute $appli_elt local_name $nom_appli
15178                     set executable_name $dico_applis(app.$nom_appli.executable_name)
15179                     set redirect $dico_applis(app.$nom_appli.redirect)
15180                     dom::element setAttribute $appli_elt executable_name $executable_name
15181                     dom::element setAttribute $appli_elt redirect $redirect
15182         
15183                     # Sous-élément hosts
15184                     foreach id_host $dico_applis(app.$nom_appli.lhosts) {
15185                         set host_elt [::dom::document createElement $appli_elt host]
15186                         # Son attribut local_name
15187                         dom::element setAttribute $host_elt local_name $dico_applis(app.$nom_appli.host.$id_host.name)
15188                             set node [::dom::document createElement $host_elt nbr_procs]
15189                             # Le sous-element nbr_procs
15190                             set nbr_procs $dico_applis(app.$nom_appli.host.$id_host.nb_procs)
15191                             ::dom::document createTextNode $node $nbr_procs
15192                     }
15193                     # Pour tous les composants de cette application
15194                     foreach nom_composant $dico_applis(app.$nom_appli.lcomp) {
15195                         
15196                         # Test si toutes les données de ce composant sont renseignées
15197                         if { [info exists dico_applis(app.$nom_appli.comp.$nom_composant.min_rank)] } {
15198                             # élément component
15199                             set component_elt [::dom::document createElement $appli_elt component]
15200                             # Son attribut local_name
15201                             dom::element setAttribute $component_elt local_name $nom_composant
15202             
15203                                 # Sous-élément rank
15204                                 set rank_elt [::dom::document createElement $component_elt rank]
15205                                     # Sous-élément min_value
15206                                     set node [::dom::document createElement $rank_elt min_value]
15207                                     set min_value $dico_applis(app.$nom_appli.comp.$nom_composant.min_rank)
15208                                     ::dom::document createTextNode $node $min_value
15209                                     # Sous-élément max_value
15210                                     if { [info exists dico_applis(app.$nom_appli.comp.$nom_composant.max_rank)] } {
15211                                         set node [::dom::document createElement $rank_elt max_value]
15212                                         set max_value $dico_applis(app.$nom_appli.comp.$nom_composant.max_rank)
15213                                         ::dom::document createTextNode $node $max_value
15214                                     }
15215                         } else {
15216                             # Il manque des infos pour poursuivre
15217                             notice_show "Please, edit attributes of component \"$nom_appli/$nom_composant\" prior to generate SCC !"
15218                             set si_erreur 1
15219                             break
15220                         }
15221                     }
15222                 } else {
15223                     # Il manque des infos pour poursuivre
15224                     notice_show "Please, edit attributes of application \"$nom_appli\" prior to generate SCC !"
15225                     set si_erreur 1
15226                     break
15227                 }
15228             }
15229 
15230             if {!$si_erreur} {
15231                 # Traduit en texte à syntaxe XML
15232                 set texte_xml [::dom::DOMImplementation serialize $doc -indent 1]
15233             }
15234             # Libère la mémoire
15235             ::dom::DOMImplementation destroy $doc
15236         } else {
15237             # Il manque des infos pour poursuivre
15238             notice_show "Please, edit experiment settings prior to generate SCC !"
15239             return ""
15240         }
15241     }
15242     return $texte_xml
15243 }
15244 
15245 ret  {genere_SMIOC} (type nom_, type appli , type nom_, type composant) {
15246 #
15247     global dico_applis
15248     global dico_caract_fichiers
15249     global dico_caract_points
15250     global dico_cnx_out
15251     global dico_caract_cnx
15252 
15253     global oasis4_version
15254 
15255     # Cle d'acces du composant dans dico_applis
15256     set clef_composant app.$nom_appli.comp.$nom_composant
15257 
15258     # Crée le document XML
15259     set doc [dom::DOMImplementation create]
15260 
15261 
15262     # Crée l'élément "prismcomponent" racine
15263     set prismcomponent_elt [::dom::document createElement $doc prismcomponent]
15264     # Ses attributs
15265     dom::element setAttribute $prismcomponent_elt xmlns "http://www.cerfacs.fr/PRISM/XML/1.1"
15266     dom::element setAttribute $prismcomponent_elt "xmlns:xsi" "http://www.w3.org/2001/XMLSchema-instance"
15267     dom::element setAttribute $prismcomponent_elt "xsi:schemaLocation" "http://www.cerfacs.fr/PRISM/XML/1.1"
15268     dom::element setAttribute $prismcomponent_elt oasis4_version $oasis4_version
15269     dom::element setAttribute $prismcomponent_elt local_name $nom_composant
15270 
15271     # recopie la/les grille(s) depuis le PMIOD
15272     # 
15273     foreach grid_name $dico_applis($clef_composant.lgrids) {
15274     set grid_elt [::dom::document createElement $prismcomponent_elt grid]
15275         dom::element setAttribute $grid_elt local_name $grid_name
15276         # Indexing dimensions
15277         if { [info exists dico_applis($clef_composant.grid.$grid_name.periodi)] } { 
15278             set periodi_elt [::dom::document createElement $grid_elt indexing_dimension]
15279             dom::element setAttribute $periodi_elt index 1
15280             dom::element setAttribute $periodi_elt periodic $dico_applis($clef_composant.grid.$grid_name.periodi)
15281         }
15282         if { [info exists dico_applis($clef_composant.grid.$grid_name.periodj)] } { 
15283             set periodj_elt [::dom::document createElement $grid_elt indexing_dimension]
15284             dom::element setAttribute $periodj_elt index 2
15285             dom::element setAttribute $periodj_elt periodic $dico_applis($clef_composant.grid.$grid_name.periodj)
15286         }
15287         if { [info exists dico_applis($clef_composant.grid.$grid_name.periodk)] } { 
15288             set periodk_elt [::dom::document createElement $grid_elt indexing_dimension]
15289             dom::element setAttribute $periodk_elt index 3
15290             dom::element setAttribute $periodk_elt periodic $dico_applis($clef_composant.grid.$grid_name.periodk)
15291         }
15292     }    
15293 
15294     # Pour tous les noms des champs de couplage du composant
15295     # Rappel : un champ peut avoir deux points de couplage, un en entrée et un en sortie.
15296     foreach nom_champ $dico_applis($clef_composant.lchamps) {
15297         set lcnx_out ""
15298         catch { set lcnx_out $dico_caract_points($nom_appli.$nom_composant.$nom_champ.o.lcnx) }
15299         set lcnx_in ""
15300         catch { set lcnx_in $dico_caract_points($nom_appli.$nom_composant.$nom_champ.i.lcnx) }
15301         
15302         # Si le champ a des connexions (en entrée ou en sortie)
15303         if { $lcnx_out != "" || $lcnx_in != ""  } {
15304 
15305             # Elément "transient"
15306             set transient_elt [::dom::document createElement $prismcomponent_elt transient]
15307             # Son attribut "local_name"
15308             dom::element setAttribute $transient_elt local_name $nom_champ
15309 
15310             # Elément transient_standard_name
15311             set std_name_elt [::dom::document createElement $transient_elt transient_standard_name]
15312             ::dom::document createTextNode $std_name_elt $dico_applis($clef_composant.champ.$nom_champ.std_name)
15313 
15314             # Elément XML "physics"
15315             set physics_elt [::dom::document createElement $transient_elt physics]
15316             dom::element setAttribute $physics_elt transient_type $dico_applis($clef_composant.champ.$nom_champ.type)
15317 
15318                 # Sous-élément physical_units
15319                 set units_elt [::dom::document createElement $physics_elt physical_units]
15320                 ::dom::document createTextNode $units_elt $dico_applis($clef_composant.champ.$nom_champ.units)
15321                 # Sous-élément valid_min
15322                 if { [info exists dico_applis($clef_composant.champ.$nom_champ.min)] } {
15323                     set min_elt [::dom::document createElement $physics_elt valid_min]
15324                     ::dom::document createTextNode $min_elt $dico_applis($clef_composant.champ.$nom_champ.min)
15325                 }
15326                 # Sous-élément valid_max
15327                 if { [info exists dico_applis($clef_composant.champ.$nom_champ.max)] } {
15328                     set max_elt [::dom::document createElement $physics_elt valid_max]
15329                     ::dom::document createTextNode $max_elt $dico_applis($clef_composant.champ.$nom_champ.max)
15330                 }
15331 
15332             # Elément numerics
15333             set numerics_elt [::dom::document createElement $transient_elt numerics]
15334             set datatype $dico_applis($clef_composant.champ.$nom_champ.datatype)
15335             dom::element setAttribute $numerics_elt datatype "xs:$datatype"
15336 
15337             # Elément "intent"
15338             set intent_elt [::dom::document createElement $transient_elt intent]
15339 
15340             # Si le champ est en sortie
15341             if { $lcnx_out != "" } {
15342 
15343                 # Pour toutes les connexions sortantes
15344                 foreach num_output $lcnx_out {
15345                     # Elément "output"
15346                     set output_elt [::dom::document createElement $intent_elt output]
15347                     # Nom du point de connexion sortante ou "transi_out_name"
15348                     set numero [string range $num_output 1 end]
15349                     set nom_point_cnx $nom_champ\_out$numero
15350                     dom::element setAttribute $output_elt transi_out_name $nom_point_cnx
15351 
15352                     # Elément "minimal_period" du champ en sortie
15353                     genere_element_min_period $output_elt $clef_composant.champ.$nom_champ.o
15354 
15355                     # Retrouve le point cible
15356                     set clef_point_source $nom_appli.$nom_composant.$nom_champ.$num_output
15357                     set clef_point_cible  $dico_cnx_out($clef_point_source)
15358                     # Exchange period unit and value
15359                     set exch_period_unit  $dico_caract_cnx($clef_point_cible.exch_per_unit)
15360                     set exch_period_value $dico_caract_cnx($clef_point_cible.exch_per_val)
15361 
15362                     # Element "exchange_date"
15363                     set exchange_elt [::dom::document createElement $output_elt exchange_date]
15364                     set period_elt [::dom::document createElement $exchange_elt period]
15365                     set elt [::dom::document createElement $period_elt $exch_period_unit]
15366                     ::dom::document createTextNode $elt $exch_period_value
15367 
15368                     # Element "corresp_transi_in_name" : le nom du point cible
15369                     # Si la cible est un fichier
15370                     if { $dico_caract_cnx($clef_point_cible.app_cible) == "///file///" } {
15371                         # Identifiant du fichier
15372                         set id_fichier $dico_caract_cnx($clef_point_cible.comp_cible)
15373                         # Le nom du point cible est simplement le nom du champ du fichier
15374                         set nom_pt_cible $dico_caract_fichiers($id_fichier.field.name)
15375                     } else {
15376                         # Le nom du point cible est le nom du champ suivi du suffixe _in
15377                         set nom_pt_cible $dico_caract_cnx($clef_point_cible.champ_cible)\_in
15378                     }
15379                     set elt [::dom::document createElement $output_elt corresp_transi_in_name]
15380                     ::dom::document createTextNode $elt $nom_pt_cible
15381 
15382                     # Si la cible est un fichier
15383                     if { $dico_caract_cnx($clef_point_cible.app_cible) == "///file///" } {
15384                         # Element "file"
15385                         set file_elt [::dom::document createElement $output_elt file]
15386                             # Sous-élément "name" : le nom du fichier
15387                             set nom_fichier $dico_caract_fichiers($id_fichier.name)
15388                             # Si l'option "suffix" est "true"
15389                             if { $dico_caract_fichiers($id_fichier.suffix) == "true" } {
15390                                 # Enleve le suffixe .nc du nom du fichier
15391                                 if { [string match *.nc $nom_fichier] } {
15392                                     set nom_fichier [string range $nom_fichier 0 end-3]
15393                                 }
15394                             }
15395                             set elt [::dom::document createElement $file_elt name]
15396                             ::dom::document createTextNode $elt $nom_fichier
15397                             
15398                             # Sous-élément "suffix"
15399                             set elt [::dom::document createElement $file_elt suffix]
15400                             ::dom::document createTextNode $elt $dico_caract_fichiers($id_fichier.suffix)
15401                             # Sous-élément "format"
15402                             set elt [::dom::document createElement $file_elt format]
15403                             ::dom::document createTextNode $elt mpp_netcdf
15404                             # Sous-élément "io_mode"
15405                             set elt [::dom::document createElement $file_elt io_mode]
15406                             ::dom::document createTextNode $elt $dico_caract_fichiers($id_fichier.io_mode)
15407                             
15408                             # Sous-élément "packing" (attribut NetCDF)
15409                             set packing $dico_caract_fichiers($id_fichier.field.packing)
15410                             if { [string match "\[1248\]" $packing] } {
15411                                 set elt [::dom::document createElement $file_elt packing]
15412                                 ::dom::document createTextNode $elt $packing
15413                             }
15414                             # Sous-élément "scaling" (attribut NetCDF)
15415                             set scaling $dico_caract_fichiers($id_fichier.field.scaling)
15416                             if { $scaling != "" } {
15417                                 set elt [::dom::document createElement $file_elt scaling]
15418                                 ::dom::document createTextNode $elt $scaling
15419                             }
15420                             # Sous-élément "adding" (attribut NetCDF)
15421                             set adding $dico_caract_fichiers($id_fichier.field.adding)
15422                             if { $adding != "" } {
15423                                 set elt [::dom::document createElement $file_elt adding]
15424                                 ::dom::document createTextNode $elt $adding
15425                             }
15426                             # Sous-élément "fill_value" (attribut NetCDF)
15427                             set fill_value $dico_caract_fichiers($id_fichier.field.fill_value)
15428                             if { $fill_value != "" } {
15429                                 set elt [::dom::document createElement $file_elt fill_value]
15430                                 ::dom::document createTextNode $elt $fill_value
15431                             }
15432                     } else {
15433                         # Le nom du composant cible
15434                         set nom_comp_cible $dico_caract_cnx($clef_point_cible.comp_cible)
15435                         # Element "component_name"
15436                         set corresp_comp_elt [::dom::document createElement $output_elt component_name]
15437                         ::dom::document createTextNode $corresp_comp_elt $nom_comp_cible
15438                     }
15439 
15440                     # Element lag
15441                     set lag ""
15442                     catch {set lag $dico_caract_cnx($clef_point_cible.lag)}
15443                     if { $lag != ""} {
15444                         set elt [::dom::document createElement $output_elt lag]
15445                         ::dom::document createTextNode $elt $lag
15446                     }
15447 
15448                     # Element "source_transformation"
15449                     set transfo_elt [::dom::document createElement $output_elt source_transformation]
15450                         # Element "source_time_operation"
15451                         set src_time_op $dico_caract_cnx($clef_point_cible.src_time_op)
15452                         if { $src_time_op != "" } {
15453                             set elt [::dom::document createElement $transfo_elt source_time_operation]
15454                             ::dom::document createTextNode $elt $src_time_op
15455                         }
15456                         # Element "statistics" 
15457                         set stat_source_mask $dico_caract_cnx($clef_point_cible.stat_source_mask)
15458                         set stat_source_nmsk $dico_caract_cnx($clef_point_cible.stat_source_nmsk)
15459                         set stat_source_all  $dico_caract_cnx($clef_point_cible.stat_source_all)
15460                         if {$stat_source_mask == "on" || $stat_source_nmsk == "on" || $stat_source_all == "on"} {
15461                             set stat_elt [::dom::document createElement $transfo_elt statistics]
15462                                 set elt [::dom::document createElement $stat_elt masked_points]
15463                                 ::dom::document createTextNode $elt $stat_source_mask
15464                                 set elt [::dom::document createElement $stat_elt notmasked_points]
15465                                 ::dom::document createTextNode $elt $stat_source_nmsk
15466                                 set elt [::dom::document createElement $stat_elt all_points]
15467                                 ::dom::document createTextNode $elt $stat_source_all
15468                         }
15469 
15470                         # Element "source_local_transformation"
15471                         
15472                         # La transformation "scattering" est un attribut du point de couplage
15473                         set scatter 0
15474                         catch {set scatter $dico_applis($clef_composant.champ.$nom_champ.o.scatter)}
15475                         # Les autres transformations sont des attributs de la connexion
15476                         set src_add_val $dico_caract_cnx($clef_point_cible.src_add_val)
15477                         set src_mult_val $dico_caract_cnx($clef_point_cible.src_mult_val)
15478                         # Si il y a au moins une transformation a la source
15479                         if { $scatter == 1 || [expr $src_add_val != 0] || [expr $src_mult_val != 1] } {
15480                             # Creer l'element
15481                             set local_transfo_elt [::dom::document createElement $transfo_elt source_local_transformation]
15482                             # Element "scattering"
15483                             if { $scatter } {
15484                                 set elt [::dom::document createElement $local_transfo_elt scattering]
15485                             }
15486                             # Element "add_scalar"
15487                             if { $src_add_val != 0 } {
15488                                 set elt [::dom::document createElement $local_transfo_elt add_scalar]
15489                                 ::dom::document createTextNode $elt $src_add_val
15490                             }
15491                             # Element "mult_scalar"
15492                             if { $src_mult_val != 1 } {
15493                                 set elt [::dom::document createElement $local_transfo_elt mult_scalar]
15494                                 ::dom::document createTextNode $elt $src_mult_val
15495                             }
15496                         }
15497 
15498                     # Element "debug_mode" a la source
15499                     set debug_mode $dico_caract_cnx($clef_point_cible.dbg_source)
15500                     set elt [::dom::document createElement $output_elt debug_mode]
15501                     ::dom::document createTextNode $elt $debug_mode
15502                 }
15503             }
15504 
15505             # Si le champ est en entrée
15506             if { $lcnx_in != "" } {
15507 
15508                 # Elément "input"
15509                 set input_elt [::dom::document createElement $intent_elt input]
15510 
15511                 # Attribut "required_but_changeable" du champ
15512                 if { [info exists dico_applis($clef_composant.champ.$nom_champ.i.rbc)] } {
15513                     set rbc $dico_applis($clef_composant.champ.$nom_champ.i.rbc)
15514                     dom::element setAttribute $input_elt required_but_changeable $rbc
15515                 }
15516 
15517                 # Elément "minimal_period" du champ en entrée
15518                 genere_element_min_period $input_elt $clef_composant.champ.$nom_champ.i
15519 
15520                 # Exchange period unit and value
15521                 set clef_point_cible  $nom_appli.$nom_composant.$nom_champ.i
15522                 set exch_period_unit  $dico_caract_cnx($clef_point_cible.exch_per_unit)
15523                 set exch_period_value $dico_caract_cnx($clef_point_cible.exch_per_val)
15524 
15525                 # Element "exchange_date"
15526                 set exchange_elt [::dom::document createElement $input_elt exchange_date]
15527                 set period_elt [::dom::document createElement $exchange_elt period]
15528                 set elt [::dom::document createElement $period_elt $exch_period_unit]
15529                 ::dom::document createTextNode $elt $exch_period_value
15530 
15531                     # Elément "origin"
15532                     # Nom du point de connexion entrant ou "transi_in_name"
15533                     set nom_point_cnx $nom_champ\_in
15534                     set origin_elt [::dom::document createElement $input_elt origin]
15535                     dom::element setAttribute $origin_elt transi_in_name $nom_point_cnx
15536 
15537                     # Retrouve le point source
15538                     # L'appli du champ source ou ///file/// si c'est un fichier source
15539                     set appli_source $dico_caract_cnx($clef_point_cible.app_source)
15540                     # Le nom du composant source ou l'id du fichier source
15541                     set nom_comp_source $dico_caract_cnx($clef_point_cible.comp_source)
15542                     # Le nom du champ source
15543                     set nom_champ_source $dico_caract_cnx($clef_point_cible.champ_source)
15544                     # Le numéro du point source
15545                     set num_pt_source [string range $dico_caract_cnx($clef_point_cible.pt_source) 1 end]
15546                     
15547                     # Element "corresp_transi_out_name"
15548                     # Si la source est un fichier
15549                     if { $appli_source == "///file///" } {
15550                         # c'est simplement le nom du champ 
15551                         set corresp_transi_out_name $nom_champ_source
15552                     } else {
15553                         # c'est le nom du champ suivi du suffixe _out<numéro>
15554                         set corresp_transi_out_name $nom_champ_source\_out$num_pt_source
15555                     }
15556                     set elt [::dom::document createElement $origin_elt corresp_transi_out_name]
15557                     ::dom::document createTextNode $elt $corresp_transi_out_name
15558                     
15559                     # Si la source est un fichier
15560                     if { $appli_source == "///file///" } {
15561                         # Element "file"
15562                         set file_elt [::dom::document createElement $origin_elt file]
15563                             # Sous-élément "name" : le nom du fichier
15564                             set nom_fichier $nom_comp_source
15565                             # Si l'option "suffix" est "true"
15566                             if { $dico_caract_fichiers($nom_comp_source.suffix) == "true" } {
15567                                 # Enleve le suffixe .nc du nom du fichier
15568                                 if { [string match *.nc $nom_fichier] } {
15569                                     set nom_fichier [string range $nom_fichier 0 end-3]
15570                                 }
15571                             }
15572                             set elt [::dom::document createElement $file_elt name]
15573                             ::dom::document createTextNode $elt $nom_fichier
15574                             
15575                             # Sous-élément "suffix"
15576                             set elt [::dom::document createElement $file_elt suffix]
15577                             ::dom::document createTextNode $elt $dico_caract_fichiers($nom_comp_source.suffix)
15578                             # Sous-élément "format"
15579                             set elt [::dom::document createElement $file_elt format]
15580                             ::dom::document createTextNode $elt mpp_netcdf
15581                             # Sous-élément "io_mode"
15582                             set elt [::dom::document createElement $file_elt io_mode]
15583                             ::dom::document createTextNode $elt $dico_caract_fichiers($nom_comp_source.io_mode)
15584                     } else {
15585                         # Element "component_name"
15586                         set corresp_comp_elt [::dom::document createElement $origin_elt component_name]
15587                         ::dom::document createTextNode $corresp_comp_elt $nom_comp_source
15588                     }
15589 
15590                     # Element "middle_transformation"
15591                     set remaillage $dico_caract_cnx($clef_point_cible.remail)
15592                     if { $remaillage != "" } {
15593                         set transfo_elt [::dom::document createElement $origin_elt middle_transformation]
15594                         set interpol_elt [::dom::document createElement $transfo_elt interpolation]
15595 
15596                         # Test : interpolation 2D ou 3D ?
15597                         switch $remaillage {
15598                             nneighbour3D -
15599                             trilinear  {
15600                                 # Interpolation 3D
15601                                 set interp_xD_elt [::dom::document createElement $interpol_elt interp3D]
15602                             }
15603                             default {
15604                                 # Interpolation 2D
15605                                 set interp_xD_elt [::dom::document createElement $interpol_elt interp2D]
15606                                 # Element <Interpolation 1D> et sous-element <none> obligatoires bien qu'inutiles
15607                                 set interp_1D_elt [::dom::document createElement $interpol_elt interp1D]
15608                                 ::dom::document createElement $interp_1D_elt none
15609                             }
15610                         }
15611 
15612                         # Sous-element specifiant le type d'interpolation :
15613                         #   nneighbour3D, trilinear (3D), nneighbour2D, bilinear, bicubic ou conservativ2D
15614                         set interp_type_elt [::dom::document createElement $interp_xD_elt $remaillage]
15615 
15616                         # Element "parallel search"
15617                         set elt [::dom::document createElement $interp_type_elt para_search]
15618                         ::dom::document createTextNode $elt global
15619 
15620                         # Test : interpolation par plus proche voisin ?
15621                         if { $remaillage == "nneighbour3D" || $remaillage == "nneighbour2D" } {
15622                             # Element "nbr_neighbours"
15623                             set elt [::dom::document createElement $interp_type_elt nbr_neighbours]
15624                             ::dom::document createTextNode $elt $dico_caract_cnx($clef_point_cible.nbr_neighbours)
15625                         }
15626 
15627                         # Element "if masked"
15628                         if { $remaillage != "conservativ2D" } {
15629                             set elt [::dom::document createElement $interp_type_elt if_masked]
15630                             ::dom::document createTextNode $elt $dico_caract_cnx($clef_point_cible.if_masked)
15631                         }
15632 
15633                         # Si remaillage bicubique, Element "bicubic_method"
15634                         if { $remaillage == "bicubic" } {
15635                             set elt [::dom::document createElement $interp_type_elt bicubic_method]
15636                             ::dom::document createTextNode $elt $dico_caract_cnx($clef_point_cible.bi3_method)
15637                         }
15638 
15639                         # Si remaillage conservatif
15640                         if { $remaillage == "conservativ2D" } {
15641                             # Element "order"
15642                             set elt [::dom::document createElement $interp_type_elt order]
15643                             ::dom::document createTextNode $elt first
15644                             # Element "normalisation method"
15645                             set norm_elt [::dom::document createElement $interp_type_elt normalisation2D]
15646                                 # Element "methodnorm2D"
15647                                 set elt [::dom::document createElement $norm_elt methodnorm2D]
15648                                 ::dom::document createTextNode $elt $dico_caract_cnx($clef_point_cible.norm_method)
15649                                 # Element "nearnei"
15650                                 set elt [::dom::document createElement $norm_elt nearnei]
15651                                 ::dom::document createTextNode $elt $dico_caract_cnx($clef_point_cible.norm_near_nei)
15652                         }
15653                     }
15654 
15655                 # Element "target_transformation"
15656                 set transfo_elt [::dom::document createElement $input_elt target_transformation]
15657                     # Sous-element "target_local_transformation"
15658                     
15659                     # La transformation "gathering" est un attribut du point de couplage
15660                     set gather 0
15661                     catch {set gather $dico_applis($clef_composant.champ.$nom_champ.i.gather)}
15662                     # Les autres transformations sont des attributs de la connexion
15663                     set tgt_add_val $dico_caract_cnx($clef_point_cible.tgt_add_val)
15664                     set tgt_mult_val $dico_caract_cnx($clef_point_cible.tgt_mult_val)
15665                     # Si il y a au moins une transformation à la source
15666                     if { $gather == 1 || [expr $tgt_add_val != 0] || [expr $tgt_mult_val != 1] } {
15667                         # Creer l'element
15668                         set local_transfo_elt [::dom::document createElement $transfo_elt target_local_transformation]
15669                         # Element "gathering"
15670                         if { $gather } {
15671                             set elt [::dom::document createElement $local_transfo_elt gathering]
15672                         }
15673                         # Element "add_scalar"
15674                         if { $tgt_add_val != 0 } {
15675                             set elt [::dom::document createElement $local_transfo_elt add_scalar]
15676                             ::dom::document createTextNode $elt $tgt_add_val
15677                         }
15678                         # Element "mult_scalar"
15679                         if { $tgt_mult_val != 1 } {
15680                             set elt [::dom::document createElement $local_transfo_elt mult_scalar]
15681                             ::dom::document createTextNode $elt $tgt_mult_val
15682                         }
15683                     }
15684                     # Sous-element "target_time_operation"
15685                     set tgt_time_op $dico_caract_cnx($clef_point_cible.tgt_time_op)
15686                     if { $tgt_time_op != "" } {
15687                         set elt [::dom::document createElement $transfo_elt target_time_operation]
15688                         ::dom::document createTextNode $elt $tgt_time_op
15689                     }
15690                     # Element "statistics" 
15691                     set stat_cible_mask $dico_caract_cnx($clef_point_cible.stat_cible_mask)
15692                     set stat_cible_nmsk $dico_caract_cnx($clef_point_cible.stat_cible_nmsk)
15693                     set stat_cible_all  $dico_caract_cnx($clef_point_cible.stat_cible_all)
15694                     if {$stat_cible_mask == "on" || $stat_cible_nmsk == "on" || $stat_cible_all == "on"} {
15695                         set stat_elt [::dom::document createElement $transfo_elt statistics]
15696                             set elt [::dom::document createElement $stat_elt masked_points]
15697                             ::dom::document createTextNode $elt $stat_cible_mask
15698                             set elt [::dom::document createElement $stat_elt notmasked_points]
15699                             ::dom::document createTextNode $elt $stat_cible_nmsk
15700                             set elt [::dom::document createElement $stat_elt all_points]
15701                             ::dom::document createTextNode $elt $stat_cible_all
15702                     }
15703 
15704                 # Element "debug_mode"
15705                 set dbg_cible $dico_caract_cnx($clef_point_cible.dbg_cible)
15706                 set elt [::dom::document createElement $input_elt debug_mode]
15707                 ::dom::document createTextNode $elt $dbg_cible
15708             }
15709         }
15710     }
15711 
15712     # Traduit en texte à syntaxe XML
15713     set texte_xml [::dom::DOMImplementation serialize $doc -indent 1]
15714     # Libère la mémoire
15715     ::dom::DOMImplementation destroy $doc
15716 
15717     return $texte_xml
15718 }
15719 
15720 ret  {genere_element_date} (type element_, type pere , type prefixe) {
15721 # Génère un sous-élément de type "date" dans l'élément element_pere passé en paramètre
15722 # Le sous-élément "date" comprend les sous-éléments suivants :
15723 #   year month day hou minute second
15724 #
15725 # Paramètres d'entrée :
15726 #  - element_pere
15727 #  - prefixe : prefixe de la clef permettant d'acceder au valeurs dans le dictionnaire global "dico_experiment"
15728 #
15729     global dico_experiment
15730     
15731     # Crée un sous-élément "date"
15732     set date_elt [::dom::document createElement $element_pere date]
15733     # Crée un sous-élément pour chaque unité de durée : year, month, etc...
15734     foreach time_unit {year month day hour minute second} {
15735         set value $dico_experiment($prefixe.$time_unit)
15736         if {$value != ""} {
15737             set node [::dom::document createElement $date_elt $time_unit]
15738             ::dom::document createTextNode $node $value
15739         }
15740     }
15741 }
15742 
15743 ret  {genere_element_min_period} (type element_, type XML , type prefixe) {
15744 global dico_applis
15745 
15746     set period_elt [::dom::document createElement $element_XML minimal_period]
15747     # Pour toutes les unités possibles de temps
15748     foreach unit_temps {secs mins hours days months years}  {
15749 
15750         set name nbr_$unit_temps
15751         # Si une valeur est définie pour cette unité
15752         if { [info exists dico_applis($prefixe.$name)] } {
15753             # Crée un sous-élément correspondant
15754             set sous_element [::dom::document createElement $period_elt $name]
15755             ::dom::document createTextNode $sous_element $dico_applis($prefixe.$name)
15756         }
15757     }
15758 }
15759 
15760 ret  {genere_graphics_XML} () {
15761 # Génere le texte du fichier XML devant contenir
15762 # les parametres graphiques des composants du couplage 
15763 #
15764     global dico_applis
15765     global dico_units
15766     global liste_fichiers dico_caract_fichiers
15767 
15768     # Crée le document XML
15769     set doc [dom::DOMImplementation create]
15770 
15771     # Crée l'élément SCC racine
15772     set graphics_elt [::dom::document createElement $doc graphics]
15773 
15774     # Pour toutes les applications
15775     foreach nom_appli $dico_applis(lapplis) {
15776         # Pour tous les composants de cette application
15777         foreach nom_composant $dico_applis(app.$nom_appli.lcomp) {
15778             set clef_comp $nom_appli.$nom_composant
15779             
15780             # Si l'élément apparait sur le graphe
15781             if { [info exists dico_units($clef_comp)] } {
15782                 # Crée un élément "component"
15783                 set component_elt [::dom::document createElement $graphics_elt component]
15784                 # Ses attributs :
15785                 # name
15786                 dom::element setAttribute $component_elt name $nom_composant
15787                 # appli
15788                 dom::element setAttribute $component_elt application $nom_appli
15789                 # Position en x et y, couleur et mode d'affichage
15790                 foreach attrib {coor_x coor_y colour expand} {
15791                     dom::element setAttribute $component_elt $attrib $dico_units($clef_comp.$attrib)
15792                 }                        
15793             }
15794         }
15795     }
15796     
15797     # Pour tous les fichiers NetCDF
15798     foreach id_fichier $liste_fichiers {
15799         set clef_fichier ///file///.$id_fichier
15800         
15801         # Si l'élément apparait sur le graphe
15802         if { [info exists dico_units($clef_fichier)] } {
15803             # Crée un élément "file"
15804             set file_elt [::dom::document createElement $graphics_elt file]
15805             # Ses attributs :
15806             # name
15807             dom::element setAttribute $file_elt name $dico_caract_fichiers($id_fichier.name)
15808             # Position en x et y, couleur et mode d'affichage
15809             foreach attrib {coor_x coor_y colour expand} {
15810                 dom::element setAttribute $file_elt $attrib $dico_units($clef_fichier.$attrib)
15811             }                        
15812         }
15813     }
15814 
15815     # Traduit en texte à syntaxe XML
15816     set texte_xml [::dom::DOMImplementation serialize $doc -indent 1]
15817     # Libère la mémoire
15818     ::dom::DOMImplementation destroy $doc
15819     
15820     return $texte_xml
15821 }
15822 
15823 ret  {get_actual_font} (type font) {
15824 global dxf
15825   set dxf(choose_font_cb_name) ""
15826   set dxf(choose_font_cb_size) ""
15827   set dxf(choose_font_c_bold) ""
15828   set dxf(choose_font_c_italic) ""
15829   set dxf(choose_font_c_underline) ""
15830 
15831   set new_font ""
15832   set lfont_opt [font actual $font]
15833   for { set i 1 } { $i < 10 } { incr i 2 } {
15834     set opt_val [string trim [lindex $lfont_opt $i]]
15835     if {$i == 1 } { set dxf(choose_font_cb_name) $opt_val }
15836     if {$i == 3 } { set dxf(choose_font_cb_size) $opt_val }
15837     if { $i == 5 } {
15838         if { $opt_val == "normal" } {
15839             set opt_val ""
15840                  } else {
15841             set dxf(choose_font_c_bold) $opt_val
15842                }
15843        }
15844     
15845     if { $i == 7 } {
15846         if { $opt_val == "roman" }  {
15847             set opt_val ""
15848                  } else {
15849             set dxf(choose_font_c_italic) $opt_val
15850                 }
15851        }
15852     if { $i == 9 } {
15853         if { $opt_val == "0" }  {
15854             set opt_val ""
15855             } else {
15856                set opt_val "underline"
15857             set dxf(choose_font_c_underline) $opt_val
15858            }
15859            }
15860 
15861     if { [llength [split $opt_val " "]] <= 1 } {
15862         append new_font " $opt_val"
15863          } else {
15864         append new_font " \{$opt_val\}"
15865        }
15866 
15867       }
15868   return [string trim $new_font]
15869 }
15870 
15871 ret  {get_cur_font} () {
15872 global dxf
15873   set cur_font ""
15874   foreach el [list $dxf(choose_font_cb_name) $dxf(choose_font_cb_size)  $dxf(choose_font_c_bold) $dxf(choose_font_c_italic) $dxf(choose_font_c_underline)] {
15875     if { $el != "" } {
15876         set trim_el [string trim $el]
15877         if { [llength [split $trim_el " "]] <= 1 } {
15878             append cur_font " $trim_el"
15879             } else {
15880             append cur_font " \{$trim_el\}"
15881            }
15882         }
15883     }
15884 
15885   return [string trim $cur_font]
15886 }
15887 
15888 ret  {help_communication} (type x , type y , type i) {
15889 #
15890     global dico_caract_cnx dico_caract_fichiers
15891 
15892     set champ_source $dico_caract_cnx($i.champ_source)
15893     
15894     # Si la cible est un fichier NetCDF
15895     if { $dico_caract_cnx($i.app_cible) == "///file///" } {
15896         # Le nom est mémorisé comme un attribut du champ "field"
15897         set id_fichier $dico_caract_cnx($i.comp_cible)
15898         set champ_cible $dico_caract_fichiers($id_fichier.field.name)
15899     } else {
15900         set champ_cible $dico_caract_cnx($i.champ_cible)
15901     }
15902 
15903     set exch_per_val  $dico_caract_cnx($i.exch_per_val)
15904     set exch_per_unit $dico_caract_cnx($i.exch_per_unit)
15905     if {$exch_per_val != 1} {
15906         # Met les unités au pluriel
15907         set exch_per_unit ${exch_per_unit}s
15908     }
15909     set text "From: $champ_source\nTo: $champ_cible\nPeriod: $exch_per_val $exch_per_unit"
15910     help_object .null $text label $x $y #e0fee6
15911 }
15912 
15913 ret  {help_object} (type w , type text , type type , optional x ={) {y {}} {color /* fefed4}} {*/
15914 /*  Affiche une fenetre ballon*/
15915 /*  Parametre d'entree :*/
15916 /*    - w :     fenetre pres de laquelle doit s'afficher le message*/
15917 /*    - text :  texte du message a afficher*/
15918 /*    - type :  label ou message*/
15919 /*    - x, y :  coordonnees a l'ecran*/
15920 /*    - color : couleur du fond*/
15921 /* */
15922     if {$x == ""} {
15923         /*  Place la bulle d'aide au dessus de la zone de saisie*/
15924          x =  [expr [winfo rootx $w] + [winfo width $w]]
15925          y =  [expr [winfo rooty $w] + [winfo height $w] - 5]
15926     } else {
15927         incr y 15
15928         incr x 5
15929     }
15930 
15931     /*   after 500*/
15932      u =  .ballon_object
15933     wm geometry $u +$x+$y
15934     destroy $u.text
15935     switch $type {
15936         message {message $u.text -text $text -padx 15 -pady 10 -justify left}
15937         label   {label $u.text -text $text -padx 15 -pady 10 -wraplength 140  -justify left}
15938     }
15939     $u.text configure -foreground black -font {helvetica 10} -background $color
15940     pack $u.text -side left
15941     wm deiconify $u
15942     raise $u
15943 }
15944 
15945 ret  {help_object_hide} () {
15946 catch {wm withdraw .ballon_object}
15947 }
15948 
15949 ret  {host_boutons_delete} (type form , type host) {
15950 # Supprime une ligne du tableau des ordinateurs "hosts" pour l'appli
15951 # avant que la ligne ait été supprimée en mémoire de la variable hosts_m
15952 #
15953     global hosts_m
15954     
15955     array set info_host [grid info $form.n$host]
15956     set num_ligne $info_host(-row)
15957     
15958     destroy $form.n$host; destroy $form.p$host; destroy $form.delete$host
15959 
15960     set hosts_suivants [lrange $hosts_m(lhosts) [expr $num_ligne + 1] end]
15961     # Pour toutes les lignes suivantes
15962     set i $num_ligne
15963     foreach host_suivant $hosts_suivants {
15964         # Change le no de lignes des trois widgets
15965         grid configure $form.n$host_suivant $form.p$host_suivant $form.delete$host_suivant -row $i
15966         incr i
15967     }
15968 }
15969 
15970 ret  {host_boutons_insert} (type form , type host , type nb_, type hosts) {
15971 # Ajoute une ligne au formulaire de saisie des ordinateurs hosts
15972     global hosts_m
15973     global OASIS_GUI_DIR
15974     
15975     # Ajoute une ligne
15976     set no_ligne $nb_hosts
15977     
15978     grid [entry $form.n$host -textvariable hosts_m(host.$host.name) -width 20 -relief groove -background white -borderwidth 1] -row $no_ligne -column 0
15979     grid [entry $form.p$host -textvariable hosts_m(host.$host.nb_procs) -width 15 -relief groove -background white -borderwidth 1] -row $no_ligne -column 1
15980     
15981     set file delete1.xbm
15982     grid [button  $form.delete$host -bitmap  @[file join $OASIS_GUI_DIR IMAGES $file] -command "host_boutons_delete $form $host; host_delete $host" -background white -foreground red -relief groove  -borderwidth 1] -row $no_ligne -column 2
15983     focus $form.n$host
15984     bind $form.p$host <KeyPress-Return> "host_boutons_next $form $host"
15985     bind $form.p$host <KeyPress-Tab> "host_boutons_next $form $host"
15986 }
15987 
15988 ret  {host_boutons_next} (type form , type host) {
15989 global hosts_m
15990     
15991     # Determine le no de ligne du bouton
15992     array set info_host [grid info $form.n$host]
15993     set num_ligne $info_host(-row)
15994     
15995     # Si le bouton est en derniere ligne de la table
15996     if { [lindex [grid size $form] 1] == [expr $num_ligne + 1] } {
15997         host_insert $form
15998     } else {
15999         set host_suivant [lindex $hosts_m(lhosts) [expr $num_ligne + 1]]
16000         focus $form.n$host_suivant
16001     }
16002 }
16003 
16004 ret  {host_delete} (type host) {
16005 # Supprime un ordinateur host de la liste pour une application
16006     global hosts_m
16007     
16008     # Supprime de la liste
16009     set hosts_m(lhosts) [lsearch -not -all -inline $hosts_m(lhosts) $host]
16010     # Oublie ses parametres
16011     array unset hosts_m host.$host.*
16012 }
16013 
16014 ret  {host_insert} (type form) {
16015 # Ajoute une nouvel ordinateur a la liste des ordinateurs hosts
16016     global hosts_m
16017     
16018     set nb_hosts [llength $hosts_m(lhosts)]
16019     # Si déja des ordinateurs hosts sont listés
16020     if { $nb_hosts != 0 } {
16021         # Détermine le no du dernier identifiant de host (id = numéro)
16022         set dernier_host [lindex $hosts_m(lhosts) end]
16023         # Crée un nouvel identifiant de host
16024         set nouvel_host [expr $dernier_host + 1]
16025     } else {
16026         set nouvel_host 1
16027     }
16028     # Ajoute a la liste
16029     lappend hosts_m(lhosts) $nouvel_host
16030     set hosts_m(host.$nouvel_host.name) ""
16031     set hosts_m(host.$nouvel_host.nb_procs) ""
16032 
16033     # Ajoute une ligne au formulaire
16034     host_boutons_insert $form $nouvel_host $nb_hosts
16035 }
16036 
16037 ret  {incr_font} (type object , type incr_, type val) {
16038 #
16039     set font [lindex [$object configure -font] 4]
16040     set size [lindex $font 1]
16041     if { $size != "" && [regexp {^[0-9]+$} $size]  && [regexp {^[+-]?[0-9]+$} $incr_val]} {
16042         set font [lreplace $font 1 1 [incr size $incr_val]]
16043     }
16044     return $font
16045 }
16046 
16047 ret  {increment_date} (type nom_, type date , type champ_, type date , type offset) {
16048 # Incrémente un champ de la variable globale nom_date de la quantité "offset"
16049 # Le nom du champ est donné par "champ_date"
16050 #
16051     global $nom_date
16052     
16053     set xval [set ${nom_date}($champ_date)]
16054     if { $xval == ""} {set xval 0}
16055     set xval [expr $xval + $offset]
16056     set ${nom_date}($champ_date) $xval
16057     # Pour forcer la verification des limites
16058     set ${nom_date}($champ_date) [set ${nom_date}($champ_date)]
16059 }
16060 
16061 ret  {liste_points_de_couplage} () {
16062 global liste_points_i liste_points_o
16063     global dico_applis dico_caract_points
16064     global dico_caract_fichiers
16065 
16066     set liste {}
16067 
16068     # Pour tous les points de couplage en entrée ou en sortie
16069     foreach clef_point [concat $liste_points_i $liste_points_o] {
16070         set nom_appli $dico_caract_points($clef_point.appli)
16071         set nom_comp  $dico_caract_points($clef_point.comp)
16072         set nom_champ $dico_caract_points($clef_point.champ)
16073 
16074         # Sens I/O
16075         set I_O [string range $clef_point end end]
16076         if { $I_O == "i" } {
16077             set I_O input
16078         } else {
16079             set I_O output
16080         }
16081         
16082         # Si le point de couplage appartient à un fichier NetCDF
16083         if { $nom_appli == "///file///" } {
16084             set id_fichier $nom_comp
16085             set id_champ   $nom_champ
16086             
16087             if { $I_O == "input" } {
16088                 # Le nom du fichier
16089                 set nom_comp $dico_caract_fichiers($id_fichier.name)
16090                 # C'est un fichier de sortie : normalement $id_champ vaut "field"
16091                 # Le vrai nom du champ
16092                 set nom_champ $dico_caract_fichiers($id_fichier.field.name)
16093             }
16094             
16095             set nom_appli "file"
16096             set type single
16097             set datatype $dico_caract_fichiers($id_fichier.$id_champ.type)
16098             if { [catch {set long_name $dico_caract_fichiers($id_fichier.$id_champ.long_name)} ] } {
16099                 set long_name ""
16100             }
16101 
16102         } else {
16103             set clef_champ app.$nom_appli.comp.$nom_comp.champ.$nom_champ
16104             if { [catch {set long_name $dico_applis($clef_champ.long_name)} ] } {
16105                 set long_name ""
16106             }
16107             set type $dico_applis($clef_champ.type)
16108             set datatype $dico_applis($clef_champ.datatype)
16109         }
16110 
16111         # Ajoute dans la liste une ligne par point de couplage
16112         # name "long name" application component type datatype I/O
16113         # Une ligne se termine par une clef d'acces a cette connexion
16114         set line [list $nom_champ $long_name $nom_appli $nom_comp $type $datatype $I_O $clef_point]
16115         lappend liste $line
16116     }
16117     return $liste
16118 }
16119 
16120 ret  {liste_tous_composants} () {
16121 #
16122     global dico_applis
16123 
16124     set liste {}
16125 
16126     set liste_applis {}
16127     catch { set liste_applis $dico_applis(lapplis) }
16128 
16129     # Pour toutes les applications chargees en memoire
16130     foreach nom_appli $liste_applis {
16131         set long_name ""
16132         if {[info exists dico_applis(app.$nom_appli.long_name)]} {
16133             set long_name $dico_applis(app.$nom_appli.long_name)
16134         }
16135         set start_mode $dico_applis(app.$nom_appli.start_mode)
16136         set coupling_mode $dico_applis(app.$nom_appli.coupling_mode)
16137         set erreur [catch {
16138             set min_value $dico_applis(app.$nom_appli.proc.min_value)
16139             set max_value $dico_applis(app.$nom_appli.proc.max_value)
16140             set increment $dico_applis(app.$nom_appli.proc.increment)
16141             set nb_proc "\[$min_value:$max_value:$increment\]"
16142         }]
16143         if {$erreur} { set nb_proc "" }
16144 
16145         # Ajoute dans la liste une ligne par appli
16146         # Une ligne se termine par une clef d'acces a cette ligne
16147         set line [list $nom_appli "" $long_name $start_mode $coupling_mode $nb_proc $nom_appli]
16148         lappend liste $line
16149 
16150         # Pour tous les composants de cette application
16151         foreach nom_composant $dico_applis(app.$nom_appli.lcomp) {
16152             set clef_comp app.$nom_appli.comp.$nom_composant
16153             if {[catch {set long_name $dico_applis($clef_comp.long_name)}] } {
16154                 set long_name ""
16155             }
16156             set erreur [catch {
16157                 set min_value $dico_applis($clef_comp.proc.min_value)
16158                 set max_value $dico_applis($clef_comp.proc.max_value)
16159                 set increment $dico_applis($clef_comp.proc.increment)
16160                 set nb_proc "\[$min_value):$max_value:$increment\]"
16161             }]
16162             if {$erreur} { set nb_proc "" }
16163 
16164             # Ajoute dans la liste une ligne par composant
16165             # Une ligne se termine par une clef d'acces a cd composant
16166             set line [list $nom_appli $nom_composant $long_name $start_mode $coupling_mode $nb_proc $nom_appli.$nom_composant]
16167             lappend liste $line
16168         }
16169     }
16170     return $liste
16171 }
16172 
16173 ret  {liste_tous_fichiers} () {
16174 #
16175     global liste_fichiers dico_caract_fichiers
16176 
16177     set liste {}
16178 
16179     foreach file_id $liste_fichiers {
16180         set io_direction $dico_caract_fichiers($file_id.dir)
16181         set io_mode $dico_caract_fichiers($file_id.io_mode)
16182         set suffix $dico_caract_fichiers($file_id.suffix)
16183         set filename $dico_caract_fichiers($file_id.name)
16184         
16185         # Ajoute dans la liste une ligne par fichier énumérant ceci :
16186         #     nom,  sens (sortie ou entrée), mode d'acces (single ou parallele), avec ou sans suffixe
16187         # Une ligne se termine par une clef d'acces a ce fichier : ///file/// suivi de son identifiant
16188         set line [list $filename $io_direction $io_mode $suffix "///file///.$file_id"]
16189         lappend liste $line
16190     }
16191     return $liste
16192 }
16193 
16194 ret  {liste_toutes_connexions} () {
16195 #
16196     global liste_cnx_in
16197     global dico_applis dico_caract_cnx dico_caract_fichiers
16198 
16199     set liste {}
16200 
16201     # Pour tous les points de couplage en entrée ou en sortie
16202     foreach clef_cnx $liste_cnx_in {
16203         set appli_source $dico_caract_cnx($clef_cnx.app_source)
16204         set appli_cible $dico_caract_cnx($clef_cnx.app_cible)
16205         set champ_source $dico_caract_cnx($clef_cnx.champ_source)
16206         set champ_cible  $dico_caract_cnx($clef_cnx.champ_cible)
16207         set comp_source $dico_caract_cnx($clef_cnx.comp_source)
16208         set comp_cible  $dico_caract_cnx($clef_cnx.comp_cible)
16209 
16210         # Si la cible est un fichier NetCDF
16211         if { $appli_cible == "///file///" } {
16212             # "comp_cible" contient l'identifiant du fichier
16213             # Son nom
16214             set nom_comp_cible $dico_caract_fichiers($comp_cible.name)
16215             # "champ_cible" contient "field"
16216             # le nom du champ
16217             set nom_champ_cible $dico_caract_fichiers($comp_cible.field.name)
16218             # Détermine les attributs du champ d'après le champ source
16219             set clef_champ app.$appli_source.comp.$comp_source.champ.$champ_source
16220         } else {
16221             set nom_comp_cible $comp_cible
16222             set nom_champ_cible $champ_cible 
16223             # Détermine les attributs du champ d'après le champ cible
16224             set clef_champ app.$appli_cible.comp.$comp_cible.champ.$champ_cible
16225         }
16226         
16227         # Détermine le type du champ
16228         set type $dico_applis($clef_champ.type)
16229         set datatype $dico_applis($clef_champ.datatype)
16230 
16231         # Ajoute dans la liste une ligne par connexion
16232         # from "from component" to "to component" type datatype
16233         # Une ligne se termine par une clef d'acces a cette connexion
16234         set line [list $champ_source $comp_source $nom_champ_cible $nom_comp_cible $type $datatype $clef_cnx]
16235         lappend liste $line
16236     }
16237     return $liste
16238 }
16239 
16240 ret  {lit_1er_sous_element} (type element) {
16241 # Retourne la ref. du premier sous-élément XML d'un élément XML donné
16242 #   Balaie les noeuds enfants jusqu'au premier noeud de type "élément"
16243 #   En effet, il peut y avoir des noeuds de type "texte" ou "commentaires"
16244 #   avant le premier sous-élément.
16245 #
16246     set sous_elt [dom::node configure $element -firstChild]
16247     set sous_elt_type [dom::node configure $sous_elt -nodeType]
16248     while {$sous_elt_type != "element"} {
16249         set sous_elt [dom::node configure $sous_elt -nextSibling]
16250         set sous_elt_type [dom::node configure $sous_elt -nodeType]
16251     }
16252     return $sous_elt
16253 }
16254 
16255 ret  {lit_XML_texte} (type element_, type XML) {
16256 set texte ""
16257    # Lit les sous-éléments de l'élément XML
16258    set ss_elem_list [dom::node children $element_XML]
16259    foreach element $ss_elem_list  {
16260       set name [dom::node configure $element -nodeName]
16261       if {$name == "#text"} {
16262          set value [dom::node configure $element -nodeValue]
16263          if { $value != "" } {
16264             set texte "$texte $value"
16265          }
16266       }
16267    }
16268    return [string trim $texte]
16269 }
16270 
16271 ret  {lit_element_application} (type appli_, type elt , type dir_, type name) {
16272 # Lit dans un fichier SCC l'élément <application> donné en parametre
16273 # Ouvre aussi le fichier PMIOD de tous les composants de l'application listés dans
16274 # l'élément <application> ou à défaut le fichier SMIOC.
16275 #
16276 # Paramètres d'entrée :
16277 #  - element : element XML de type "application"
16278 #  - dir_name : nom du repertoire contenant le fichier SCC.xml
16279 #
16280 # Valeur de retour :
16281 #      0 si pas d'erreur
16282 #      1 s'il manque un fichier PMIOD ou SMIOC
16283 #
16284     global dico_applis dico_units
16285     global liste_points_i liste_points_o dico_caract_points
16286     
16287     # Lit les attributs local_name, executable_name, redirect
16288     set att [dom::node configure $appli_elt -attributes]
16289     set nom_appli [load_XML_attributes $att dico_applis "app"]
16290     
16291     # Lit le fichier AD.xml de l'application, si présent dans le même répertoire
16292     # et lit aussi les fichiers PMIOD des composants de l'appli si ceux ci sont aussi présents
16293     # ----------------------------------------------------------------------------
16294     set liste_comp_ajoutes {}
16295     set nom_fichier_AD [file join $dir_name "$nom_appli\_ad.xml"]
16296     if {! [file exists $nom_fichier_AD]} {
16297         # Essaie avec des lettres majuscules
16298         set nom_fichier_AD [file join $dir_name "$nom_appli\_AD.xml"]
16299     }
16300     # Si le fichierAD existe
16301     if {[file exists $nom_fichier_AD]} {
16302         lit_fichier_AD  $nom_fichier_AD dico_applis liste_comp_ajoutes
16303     } else {
16304         # erreur
16305         notice_show  "Cannot find Application Description file for \n$nom_appli\nYou have to locate and load it manually" error
16306         # Ajoute le nom de l'appli à la liste
16307         lappend dico_applis(lapplis) $nom_appli
16308         # Initialise arbitrairement des champs pas renseignés qui sont en fait obligatoires
16309         set dico_applis(app.$nom_appli.start_mode) notspawn_or_spawn
16310         set dico_applis(app.$nom_appli.coupling_mode) coupled_or_standalone
16311         set dico_applis(app.$nom_appli.proc.min_value) 0
16312         set dico_applis(app.$nom_appli.proc.max_value) 0
16313         set dico_applis(app.$nom_appli.proc.increment) 0
16314     }
16315         
16316     # Lit les sous-éléments de l'élément <application>
16317     set current_host_id 0
16318     set ss_elem_list [dom::node children $appli_elt]
16319     foreach element $ss_elem_list  {
16320         set name [dom::node configure $element -nodeName]
16321         if {$name != "#text" && $name != "#comment"} {
16322             switch $name {
16323                 host {
16324                     # Lit l'attribut "local_name" qui est le nom du host
16325                     set att [dom::node configure $element -attributes]
16326                     set host_name [load_XML_attributes $att dico_applis "app.$nom_appli.host"]
16327                     # L'identifiant du host est une numéro
16328                     set dico_applis(app.$nom_appli.host.$current_host_id.name) $host_name
16329                     # Ajoute a la liste
16330                     lappend dico_applis(app.$nom_appli.lhosts) $current_host_id
16331                     # Lit le sous-element "nbr_procs" qui est unique
16332                     set nbr_procs_elt [lit_1er_sous_element $element]
16333                     # Lit la valeur du sous-élément "nbr_procs"
16334                     set txt_elem [dom::node configure $nbr_procs_elt -firstChild]
16335                     set value [dom::node configure $txt_elem -nodeValue]
16336                     set dico_applis(app.$nom_appli.host.$current_host_id.nb_procs) $value
16337                     incr current_host_id
16338                 }
16339                 component {
16340                     # Lit l'attribut "local_name" qui est le nom du composant
16341                     set att [dom::node configure $element -attributes]
16342                     set nom_comp [load_XML_attributes $att dico_applis "app.$nom_appli.comp"]
16343 
16344                     # Lit les sous-éléments de l'élément <component> (qui sont tous de type "rank")
16345                     set ss_elem_list [dom::node children $element]
16346                     foreach element $ss_elem_list  {
16347                         set name [dom::node configure $element -nodeName]
16348                         if {$name == "rank"} {
16349                             lit_element_rank $element app.$nom_appli.comp.$nom_comp
16350                             # Pour l'instant, on ne lit que le premier
16351                             break
16352                         }
16353                     }
16354 
16355                     # Si le fichier PMIOD du composant n'a pas été lu en même temps que le fichier AD de l'appli
16356                     if {[lsearch $liste_comp_ajoutes $nom_appli.$nom_comp] == -1} {
16357                         # Si le fichier PMIOD du composant est présent dans le répertoire
16358                         set erreur 1
16359                         set nom_fichier_PMIOD [file join $dir_name "$nom_appli\_$nom_comp\_pmiod.xml"]
16360                         if { [file isfile $nom_fichier_PMIOD] } {
16361                             # Lit le fichier PMIOD du composant
16362                             set erreur [lit_fichier_component $nom_fichier_PMIOD dico_applis $nom_appli $nom_comp PMIOD]
16363                         }
16364                         # Si pas possible de lire le fichier PMIOD
16365                         if { $erreur } {
16366                             # En remplacement, ouvre et lit une première fois le fichier SMIOC.xml du composant
16367                             set nom_fichier_SMIOC [file join $dir_name "$nom_appli\_$nom_comp\_smioc.xml"]
16368                             set erreur [lit_fichier_component $nom_fichier_SMIOC dico_applis $nom_appli $nom_comp PMIOD]
16369                             # Initialise arbitrairement des champs pas renseignés qui sont en fait obligatoires
16370                             set dico_applis(app.$nom_appli.comp.$nom_comp.proc.min_value) 0
16371                             set dico_applis(app.$nom_appli.comp.$nom_comp.proc.max_value) 0
16372                             set dico_applis(app.$nom_appli.comp.$nom_comp.proc.increment) 0
16373                         }
16374                         
16375                         # Si erreur aussi a la lecture du fichier SMIOC
16376                         if { $erreur } {
16377                             notice_show  "Both files $nom_fichier_PMIOD and $nom_fichier_SMIOC are absent or invalid !" error
16378                             return 1
16379                         } else {
16380                             # Ajoute le composant à la liste
16381                             lappend dico_applis(app.$nom_appli.lcomp) $nom_comp
16382                         }
16383                     }
16384 
16385                     # Ajoute le composant à la liste des unités graphiques
16386                     set dico_units($nom_appli.$nom_comp) $nom_comp
16387                 
16388                     # Ajoute tous les points d'échange (de couplage) de ce composant
16389                     # à la liste générale des points de couplage
16390                     if { ![info exists dico_applis(app.$nom_appli.comp.$nom_comp.lchamps)] } {
16391                         set dico_applis(app.$nom_appli.comp.$nom_comp.lchamps) {}
16392                     }
16393                     set liste_champs $dico_applis(app.$nom_appli.comp.$nom_comp.lchamps)
16394                     foreach nom_champ $liste_champs  {
16395                         set clef_base "$nom_appli.$nom_comp.$nom_champ"
16396             
16397                         # Si le champ de couplage est en entrée
16398                         if { [info exists dico_applis(app.$nom_appli.comp.$nom_comp.champ.$nom_champ.input)] } {
16399                             set clef "$clef_base.i"
16400                             lappend liste_points_i $clef
16401                             set dico_caract_points($clef.appli) $nom_appli
16402                             set dico_caract_points($clef.comp) $nom_comp
16403                             set dico_caract_points($clef.champ) $nom_champ
16404                         }
16405             
16406                         # Si le champ de couplage est en sortie
16407                         if { [info exists dico_applis(app.$nom_appli.comp.$nom_comp.champ.$nom_champ.output)] } {
16408                             set clef "$clef_base.o"
16409                             lappend liste_points_o $clef
16410                             set dico_caract_points($clef.appli) $nom_appli
16411                             set dico_caract_points($clef.comp) $nom_comp
16412                             set dico_caract_points($clef.champ) $nom_champ
16413                         }
16414                     }
16415                 }
16416             }
16417         }
16418     }
16419     return 0
16420 }
16421 
16422 ret  {lit_element_date} (type elt_, type parent , type prefixe) {
16423 # Lit dans le fichier SCC.XML la date de "start" ou "end" de "experiment" ou "run"
16424 #
16425 # Paramètres d'entrée :
16426 #  - elt_parent :   element XML qui contient le sous-element "date"
16427 #  - prefixe : préfixe de la clef permettant d'accéder au valeurs dans le dictionnaire global "dico_experiment"
16428 #
16429     global dico_experiment
16430     
16431     # Initialisation des champs de la date
16432     foreach champ { year month day hour minute second } {
16433         set dico_experiment($prefixe.$champ) ""
16434     }
16435     
16436     # Accède au sous-element XML "date"
16437     set date_elt [lit_1er_sous_element $elt_parent]
16438     # Lit les sous-éléments de l'élément <date>
16439     set ss_elem_list [dom::node children $date_elt]
16440     foreach element $ss_elem_list  {
16441         set name [dom::node configure $element -nodeName]
16442         if {$name != "#text" && $name != "#comment"} {
16443             # La var. $name contient une des valeurs suivantes : year month day hour minute second 
16444             # Extrait la valeur du sous-element year month day hour minute ou second 
16445             set ss_elem [dom::node configure $element -firstChild]
16446             set value [dom::node configure $ss_elem -nodeValue]
16447             set dico_experiment($prefixe.$name) $value
16448         }
16449     }
16450 }
16451 
16452 ret  {lit_element_exchange_date} (type element_, type XML) {
16453 # Lit un élément XML de type <exchange_date> (période des échanges)
16454 #
16455 # Retourne une liste contenant l'unité de temps de la période des échanges (year, month, day,...)
16456 # et le nombre d'unités.
16457 #
16458 # On suppose qu'il n'y a qu'une seule unité de temps. En fait, la période peut etre définie
16459 # par plusieurs unités de temps, par exemple : 2 heures 30 minutes
16460 # Mais ce cas n'est pas pris en charge.
16461     
16462     # Accède au sous-élément <period> : le premier sous-element de <exchange_date>
16463     set period_elt [lit_1er_sous_element $element_XML]
16464     
16465     # Accède au premier sous-element de <period> : year, month, day ou autre
16466     set ss_elt_unite [lit_1er_sous_element $period_elt]
16467     set unite [dom::node configure $ss_elt_unite -nodeName]
16468     
16469     # Lit la valeur du sous-élément
16470     set txt_elem [dom::node configure $ss_elt_unite -firstChild]
16471     set valeur [dom::node configure $txt_elem -nodeValue]
16472 
16473     return [list $unite $valeur]
16474 }
16475 
16476 ret  {lit_element_file} (type element_, type XML , type nom_, type dico) {
16477 # Lit un élément XML de type <file>
16478 #
16479 # Remplit un dictionnaire contenant tous les parametres du fichier
16480 # Retourne le nom du fichier.
16481 #
16482     upvar 1 $nom_dico dico_fichier
16483 
16484     # Initialise la structure de donnees
16485     set dico_fichier(suffix) false
16486     set dico_fichier(io_mode) iosingle
16487 
16488     # Lit les sous-éléments de l'élément <file>
16489     set ss_elem_list [dom::node children $element_XML]
16490     foreach element $ss_elem_list  {
16491         set name [dom::node configure $element -nodeName]
16492         if {$name != "#text" && $name != "#comment"} {
16493             set value [lit_XML_texte $element]
16494             set dico_fichier($name) $value
16495         }
16496     }
16497     return $dico_fichier(name)
16498 }
16499 
16500 ret  {lit_element_input} (type element_, type XML , type nom_, type appli , type nom_, type comp , type nom_, type champ , type PMIOD_, type ou_, type SMIOC) {
16501 # Lit l'élément input d'un fichier SMIOC.xml
16502 # en particulier les éléments XML concernant la connexion arrivant a ce point couplage
16503 #
16504 # Parametres d'entrée :
16505 #   - element_XML :      élément XML de type <output>
16506 #   - nom_appli :        nom de l'application
16507 #   - nom_comp :         nom du composant
16508 #   - nom_champ :        nom du champ de couplage
16509 #   - PMIOD_ou_SMIOC :   vaut PMIOD ou SMIOC
16510 #
16511 
16512     global dico_applis
16513     
16514     global liste_points_i liste_points_o dico_caract_points
16515     global liste_cnx_in dico_cnx_out dico_caract_cnx
16516     
16517     global liste_fichiers dico_caract_fichiers
16518     global dico_units
16519     
16520     # Lit les sous-éléments de l'élément <input>
16521     # ------------------------------------------
16522     
16523     set ss_elem_list [dom::node children $element_XML]
16524     foreach element $ss_elem_list  {
16525         set name [dom::node configure $element -nodeName]
16526         switch $name {
16527             exchange_date {
16528                 # Si on lit un fichier SMIOC
16529                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
16530                     set exch_date [lit_element_exchange_date $element]
16531                 }
16532             }
16533             
16534             origin {
16535                 # Si on lit un fichier SMIOC
16536                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
16537                     array set cnx_attribute {}
16538                     array set dico_fichier {}
16539                     set origin [lit_element_origin $element cnx_attribute dico_fichier]
16540                     set nom_comp_source [lindex $origin 0]
16541                     set nom_corresp [lindex $origin 1]
16542                 }
16543             }
16544             
16545             target_transformation {
16546                 array set cnx_attribute {}
16547                 lit_element_src_tgt_transfo $element cnx_attribute target $PMIOD_ou_SMIOC
16548             }
16549             
16550             debug_mode {
16551                 # Si on lit un fichier SMIOC
16552                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
16553                     # Lit la valeur du sous-élément <debug_mode> : a priori = "true"
16554                     set txt_elem [dom::node configure $element -firstChild]
16555                     set cnx_attribute(dbg_cible) [dom::node configure $txt_elem -nodeValue]
16556                 }
16557             }
16558         }
16559     }
16560    
16561     # Si on lit un fichier SMIOC
16562     if {$PMIOD_ou_SMIOC == "SMIOC"} {
16563         
16564         # Crée une nouvelle connexion
16565         # ---------------------------
16566         
16567         # Préfixe de la clef des éléments à mettre dans les dictionnaires dico_caract_points et dico_caract_cnx
16568         set prefixe "$nom_appli.$nom_comp.$nom_champ"
16569         # Mémorise que le point de couplage entrant est connecté
16570         lappend dico_caract_points($prefixe.i.lcnx) i
16571     
16572         # Si la cible est un composant d'une application
16573         if {$nom_comp_source != ""} {
16574             # Recherche le nom de l'application à partir du nom du composant
16575             foreach nom_appli_source $dico_applis(lapplis) {
16576                 if {[lsearch $dico_applis(app.$nom_appli_source.lcomp) $nom_comp_source] != -1} {
16577                     break
16578                 }
16579             }
16580             # La source de la connexion, extraite du SMIOC et mémorisée dans la variable "nom_corresp" 
16581             #    est décrite ainsi :  <nom-champ>_out<numero>
16582             # Il faut extraire de cette chaine de caractères le nom du champ et le numéro de la connexion sortante
16583             regexp {^(.*)_out([0-9]*)$} $nom_corresp match_str nom_champ_source numero
16584         } else {
16585             # La cible de la connexion est un fichier NetCDF
16586             set nom_appli_source ///file///
16587             set nom_comp_source $dico_fichier(name)
16588             # Champ source
16589             set nom_champ_source $nom_corresp
16590             # Numéro de connexion sortant du point
16591             set numero 1
16592         }
16593     
16594         # Clef (index) de la connexion : celle de la connexion entrante, construite sur celle du point cible
16595         set clef_cnx_in $prefixe.i
16596         # Ajoute à la liste des connexions entrantes
16597         if {[lsearch $liste_cnx_in $clef_cnx_in] == -1} {
16598             lappend liste_cnx_in $clef_cnx_in
16599         }
16600         # Clef de la connexion sortante : construite d'apres celle du point source
16601         set clef_cnx_out $nom_appli_source.$nom_comp_source.$nom_champ_source.o$numero
16602         # Ajoute au dico des connexions sortantes
16603         set dico_cnx_out($clef_cnx_out) $clef_cnx_in
16604     
16605         # Renseigne les caractéristiques de la nouvelle connexion
16606         # -------------------------------------------------------
16607     
16608         # Source et cible
16609         set dico_caract_cnx($clef_cnx_in.app_source)   $nom_appli_source
16610         set dico_caract_cnx($clef_cnx_in.app_cible)    $nom_appli
16611         set dico_caract_cnx($clef_cnx_in.comp_source)  $nom_comp_source
16612         set dico_caract_cnx($clef_cnx_in.comp_cible)   $nom_comp
16613         set dico_caract_cnx($clef_cnx_in.champ_source) $nom_champ_source
16614         set dico_caract_cnx($clef_cnx_in.champ_cible)  $nom_champ
16615         set dico_caract_cnx($clef_cnx_in.pt_source)    o$numero
16616     
16617         # Période des échanges
16618         set dico_caract_cnx($clef_cnx_in.exch_per_unit)  [lindex $exch_date 0]
16619         set dico_caract_cnx($clef_cnx_in.exch_per_val)   [lindex $exch_date 1]
16620     
16621         # Initialise les autres caractéristiques
16622         foreach {caract default_value} {
16623             dbg_cible false
16624             stat_cible_mask false
16625             stat_cible_nmsk false
16626             stat_cible_all false
16627             tgt_time_op  {}
16628             tgt_add_val  0
16629             tgt_mult_val 1
16630             remail {}   } {
16631             set dico_caract_cnx($clef_cnx_in.$caract) $default_value
16632         }
16633     
16634         # Renseigne les vraies valeurs pour ces autres caractéristiques
16635         foreach {name value} [array get cnx_attribute] {
16636             set dico_caract_cnx($clef_cnx_in.$name) $value
16637         }
16638 
16639         # Si la source est un fichier NetCDF
16640         if {$nom_appli_source == "///file///"} {
16641             # Met a zéro des caractéristiques qui ne sont pas significatives 
16642             # pour une connexion depuis un fichier
16643             foreach {caract default_value} {
16644                 lag {} 
16645                 dbg_source false
16646                 stat_source_mask false
16647                 stat_source_nmsk false
16648                 stat_source_all false
16649                 src_time_op  {}
16650                 src_add_val  0
16651                 src_mult_val 1 } {
16652                 set dico_caract_cnx($clef_cnx_in.$caract) $default_value
16653             }
16654         }
16655         
16656         # Mémorise la source si c'est un fichier
16657         # --------------------------------------
16658     
16659         # Si la source est un fichier NetCDF
16660         if {$nom_appli_source == "///file///"} {
16661             # Ajoute le fichier a la liste
16662             set id_fichier $nom_comp_source
16663             lappend liste_fichiers $id_fichier
16664     
16665             # Renseigne les caractéristiques du fichier
16666             set dico_caract_fichiers($id_fichier.dir) input
16667             set dico_caract_fichiers($id_fichier.name) $nom_comp_source
16668             set dico_caract_fichiers($id_fichier.io_mode) $dico_fichier(io_mode)
16669             set dico_caract_fichiers($id_fichier.suffix) $dico_fichier(suffix)
16670             set dico_caract_fichiers($id_fichier.lchamps) $nom_champ_source
16671     
16672             # Renseigne les caractéristiques du champ du fichier
16673             if {[info exists dico_fichier(fill_value)]} {
16674                 set dico_caract_fichiers($id_fichier.$nom_champ_source.fill_value) $dico_fichier(fill_value)
16675             } else {
16676                 set dico_caract_fichiers($id_fichier.$nom_champ_source.fill_value) ""
16677             }
16678             # type numérique est celui du champ cible
16679             set dico_caract_fichiers($id_fichier.$nom_champ_source.type) $dico_applis(app.$nom_appli.comp.$nom_comp.champ.$nom_champ.datatype)
16680     
16681             # Crée le point de couplage du fichier
16682             set clef_pt_source $nom_appli_source.$nom_comp_source.$nom_champ_source.o
16683             lappend liste_points_o $clef_pt_source
16684             # Renseigne les caractéristiques du point de couplage
16685             set dico_caract_points($clef_pt_source.appli) $nom_appli_source
16686             set dico_caract_points($clef_pt_source.comp)  $id_fichier
16687             set dico_caract_points($clef_pt_source.champ) $nom_champ_source
16688             set dico_caract_points($clef_pt_source.lcnx)  o1
16689     
16690             # Crée une nouvelle unité sur le graphe de couplage
16691             set dico_units(///file///.$id_fichier) $id_fichier
16692         }
16693     }
16694 }
16695 
16696 ret  {lit_element_intent} (type element_, type XML , type nom_, type dico , type nom_, type appli , type nom_, type comp , type nom_, type champ , type PMIOD_, type ou_, type SMIOC) {
16697 # Lit dans un fichier XML (PMIOD ou SMIOC) d'un composant un élément <intent>
16698 #
16699 # Parametres d'entrée :
16700 #   - element_XML :      élément XML de type <transient>
16701 #   - nom_dico  :        nom du dictionnaire à remplir (habituellement dico_applis)
16702 #   - nom_appli :        nom de l'application
16703 #   - nom_comp :         nom du composant
16704 #   - nom_champ :        nom du champ de couplage
16705 #   - PMIOD_ou_SMIOC :   vaut PMIOD ou SMIOC
16706 #
16707     upvar 1 $nom_dico dict_vars
16708 
16709    # Préfixe de la clef des éléments à mettre dans le dictionnaire
16710    set prefixe "app.$nom_appli.comp.$nom_comp.champ.$nom_champ"
16711    
16712    # Lit les sous-éléments de l'élément <transient>
16713    set ss_elem_list [dom::node children $element_XML]
16714    set nb_output 0
16715    foreach element $ss_elem_list  {
16716       set name [dom::node configure $element -nodeName]
16717       switch $name {
16718          output {
16719             # Compte le nombre de sorties (output) du même champ
16720             incr nb_output
16721             # Si ce point de couplage n'est pas encore mémorisé
16722             if {! [info exists dict_vars($prefixe.output)]} {
16723                # Cree un nouveau point de couplage
16724                # Mémorise que le point de couplage est "output"
16725                set dict_vars($prefixe.output) 1
16726                # Lit le sous-élément <minimal_period> de l'élément <output>
16727                set ss_elem_list [dom::node children $element]
16728                foreach sous_element $ss_elem_list  {
16729                   set name [dom::node configure $sous_element -nodeName]
16730                   if { $name == "minimal_period" } {
16731                      lit_element_min_period $sous_element dict_vars "$prefixe.o"
16732                      break
16733                   }
16734                }
16735             }
16736             # Si on s'interesse au point de couplage (PMIOD) 
16737             if {$PMIOD_ou_SMIOC == "PMIOD"} {
16738                 # Si c'est la premiere sortie du champ
16739                 if {$nb_output == 1} {
16740                     # Lit les infos concernant l'element scatter du point de couplage
16741                     set scatter [lit_element_output $element $nom_appli $nom_comp $nom_champ $PMIOD_ou_SMIOC]
16742                     set dict_vars($prefixe.o.scatter) $scatter
16743                 }
16744             } else {
16745                 # Lit les infos concernant la connexion partant de ce point de couplage
16746                 lit_element_output $element $nom_appli $nom_comp $nom_champ $PMIOD_ou_SMIOC
16747             }
16748          }
16749          input {
16750             # Si ce point de couplage n'est pas encore mémorisé
16751             if {! [info exists dict_vars($prefixe.input)]} {
16752                # Mémorise que le point de couplage est "input"
16753                set dict_vars($prefixe.input) 1
16754                # Lit l'attribut "required_but_changeable" si présent
16755                set value [dom::element getAttribute $element "required_but_changeable"]
16756                set dict_vars($prefixe.i.rbc) $value
16757                # Lit le sous-élément <minimal_period> de l'élément <output>
16758                set ss_elem_list [dom::node children $element]
16759                foreach sous_element $ss_elem_list  {
16760                   set name [dom::node configure $sous_element -nodeName]
16761                   if { $name == "minimal_period" } {
16762                      lit_element_min_period $sous_element dict_vars "$prefixe.i"
16763                   }
16764                }
16765             }
16766             # Si on s'interesse au point de couplage (PMIOD) 
16767             if {$PMIOD_ou_SMIOC == "PMIOD"} {
16768                 # Lit les infos concernant l'element gather du point de couplage
16769                 set gather [lit_element_input $element $nom_appli $nom_comp $nom_champ $PMIOD_ou_SMIOC]
16770                 set dict_vars($prefixe.i.gather) $gather
16771             } else {
16772                # Lit les infos concernant la connexion arrivant à ce point de couplage
16773                lit_element_input $element $nom_appli $nom_comp $nom_champ $PMIOD_ou_SMIOC
16774             }
16775          }
16776       }
16777    }
16778 }
16779 
16780 ret  {lit_element_liste_ss_elts} (type element_, type XML , type nom_, type dico , type nom_, type traduction) {
16781 # Lit un élément XML donné composé d'une liste de sous-éléments simples
16782 #
16783 # Sauve en mémoire dans un dictionnaire les valeurs des sous-éléments.
16784 # 
16785 # Paramètres d'entrée :
16786 #   - element_XML :       élément à lire
16787 #   - nom_dico :          nom du dictionnaire à remplir
16788 #   - nom_traduction :    nom du dictionnaire de traduction
16789 #                         Ce dictionnaire donne la traduction des nom des sous-éléments en clefs du dictionnaire a remplir
16790 #
16791     upvar 1 $nom_dico dico
16792     upvar 1 $nom_traduction traduction
16793     
16794     # Lit les sous-éléments de l'élément
16795     set ss_elem_list [dom::node children $element_XML]
16796     foreach element $ss_elem_list  {
16797         set name [dom::node configure $element -nodeName]
16798         if {$name != "#text" && $name != "#comment"} {
16799             # Convertit le nom du sous-élément en clef
16800             set clef $traduction($name)
16801             # Lit le contenu du sous-élément
16802             set txt_elem [dom::node configure $element -firstChild]
16803             # S'il a un contenu
16804             if {$txt_elem != ""} {
16805                 # Lit la valeur du contenu
16806                 set value  [dom::node configure $txt_elem -nodeValue]
16807             } else {
16808                 # Prend une valeur arbitraire de 1
16809                 set value 1
16810             }
16811             # Mémorise le contenu du sous-élément
16812             set dico($clef) $value
16813         }
16814     }
16815 }
16816 
16817 ret  {lit_element_mid_transfo} (type element_, type XML , type nom_, type dico) {
16818 # Lit un élément XML de type <middle_transformation>
16819 #
16820 # Remplit un dictionnaire contenant tous les parametres lus dans l'élément XML.
16821 #
16822 # Parametres d'entrée :
16823 #   - element_XML :       élément XML
16824 #   - nom_dico :          nom du dictionnaire a remplir
16825 #
16826     upvar 1 $nom_dico cnx_attribute
16827     
16828     # Accède au sous-élément <interpolation> : le premier sous-élément
16829     set interpolation_elt [lit_1er_sous_element $element_XML]
16830     
16831     # Cherche le sous-élément <interp3D> ou <interp2D> de l'élément <interpolation>
16832     set ss_elem_list [dom::node children $interpolation_elt]
16833     foreach interp_elt $ss_elem_list  {
16834         set name [dom::node configure $interp_elt -nodeName]
16835         if {$name  == "interp3D" || $name == "interp2D"} { break }
16836     }
16837     
16838     # Initialise les attributs de la méthode de remaillage
16839     set cnx_attribute(nbr_neighbours)  0
16840     set cnx_attribute(if_masked)       no_value
16841     set cnx_attribute(bi3_method)      gradient
16842     set cnx_attribute(norm_method)     None
16843     set cnx_attribute(norm_near_nei)   false
16844 
16845 
16846     # Accède au premier et seul sous-élément de <interpxD>
16847     # qui est de type <nneighbour3D>, <trilinear>, <nneighbour2D>, <bilinear>, <bicubic> ou <conservativ2D>
16848     set method_elt [lit_1er_sous_element $interp_elt]
16849     # Mémorise la methode de remaillage
16850     set cnx_attribute(remail) [dom::node configure $method_elt -nodeName]
16851 
16852     # Lit les sous-éléments de l'élément "method_elt"
16853     set ss_elem_list [dom::node children $method_elt]
16854     foreach element $ss_elem_list  {
16855         set name [dom::node configure $element -nodeName]
16856         switch $name {
16857             if_masked {
16858                 # Lit la valeur du sous-élément
16859                 set txt_elem [dom::node configure $element -firstChild]
16860                 set cnx_attribute(if_masked) [dom::node configure $txt_elem -nodeValue]
16861             }
16862             bicubic_method {
16863                 # Lit la valeur du sous-élément
16864                 set txt_elem [dom::node configure $element -firstChild]
16865                 set cnx_attribute(bi3_method) [dom::node configure $txt_elem -nodeValue]
16866             }
16867             nbr_neighbours {
16868                 # Lit la valeur du sous-élément
16869                 set txt_elem [dom::node configure $element -firstChild]
16870                 set cnx_attribute(nbr_neighbours) [dom::node configure $txt_elem -nodeValue]
16871             }
16872             normalisation2D {
16873                 # Lit les sous-éléments de l'élément <normalisation2D>
16874                 set ss_elem_list [dom::node children $element]
16875                 foreach element $ss_elem_list  {
16876                     set name [dom::node configure $element -nodeName]
16877                     switch $name {
16878                         methodnorm2D {
16879                             # Lit la valeur du sous-élément
16880                             set txt_elem [dom::node configure $element -firstChild]
16881                             set cnx_attribute(norm_method) [dom::node configure $txt_elem -nodeValue]
16882                         }
16883                         nearnei {
16884                             # Lit la valeur du sous-élément
16885                             set txt_elem [dom::node configure $element -firstChild]
16886                             set cnx_attribute(norm_near_nei) [dom::node configure $txt_elem -nodeValue]
16887                         }
16888                     }
16889                 }
16890             }
16891         }
16892     }
16893 }
16894 
16895 ret  {lit_element_min_period} (type element_, type XML , type nom_, type dico , type prefixe) {
16896 upvar 1 $nom_dico dict_vars
16897 
16898     # Lit les sous-éléments de l'élément <minimal_period>
16899     set ss_elem_list [dom::node children $element_XML]
16900     foreach element $ss_elem_list  {
16901         set name [dom::node configure $element -nodeName]
16902         set value [lit_XML_texte $element]
16903         set dict_vars($prefixe.$name) $value
16904     }
16905 }
16906 
16907 ret  {lit_element_nbr_proc} (type element_, type XML , type nom_, type dico , type prefixe) {
16908 # Lit un element XML de type <nbr_proc>
16909     
16910     upvar 1 $nom_dico dict_vars
16911     # Lit les sous-éléments de l'élément <nbr_proc>
16912     set ss_elem_list [dom::node children $element_XML]
16913     foreach element $ss_elem_list  {
16914         set name [dom::node configure $element -nodeName]
16915         if {$name != "#text" && $name != "#comment"} {
16916             set value [lit_XML_texte $element]
16917             set dict_vars($prefixe.$name) $value
16918         }
16919     }
16920     # Complete les informations si necessaire
16921     if { ![info exists dict_vars($prefixe.max_value)] } {
16922          catch {set dict_vars($prefixe.max_value) $dict_vars($prefixe.min_value)}
16923     }
16924     if { [info exists dict_vars($prefixe.min_value)] && ![info exists dict_vars($prefixe.increment)] } {
16925          set dict_vars($prefixe.increment) 1
16926     }
16927 }
16928 
16929 ret  {lit_element_origin} (type element_, type XML , type nom_, type dico_, type cnx , type nom_, type dico_, type fichier) {
16930 # Lit un élément XML de type <origin>, qui est sous-élément de <input>
16931 # et qui décrit l'origine d'une connexion avec un champ de couplage.
16932 #
16933 # Retourne une liste contenant le nom du composant source et le nom du champ source.
16934 # Renseigne un dictionnaire avec les caractéristiques de la connexion
16935 # Si la source de la connexion est un fichier, renseigne un dictionnaire
16936 # avec les caractéristiques du fichier.
16937 #
16938     upvar 1 $nom_dico_cnx dico_cnx
16939     upvar 1 $nom_dico_fichier dico_fichier
16940     set nom_comp_source ""
16941     set nom_corresp ""
16942     
16943     # Lit les sous-éléments de l'élément <origin>
16944     # -------------------------------------------
16945     
16946     set ss_elem_list [dom::node children $element_XML]
16947     foreach element $ss_elem_list  {
16948         set name [dom::node configure $element -nodeName]
16949         switch $name {
16950             corresp_transi_out_name {
16951                 # Lit la valeur du sous-élément <corresp_transi_out_name>
16952                 set txt_elem [dom::node configure $element -firstChild]
16953                 set nom_corresp [dom::node configure $txt_elem -nodeValue]
16954             }
16955 
16956             component_name {
16957                 # Lit la valeur du sous-élément <component_name>
16958                 set txt_elem [dom::node configure $element -firstChild]
16959                 set nom_comp_source [dom::node configure $txt_elem -nodeValue]
16960             }
16961             
16962             middle_transformation {
16963                 lit_element_mid_transfo $element dico_cnx
16964             }
16965 
16966             file {
16967                 lit_element_file $element dico_fichier
16968             }
16969         }
16970     }
16971     return [list $nom_comp_source $nom_corresp]
16972 }
16973 
16974 ret  {lit_element_output} (type element_, type XML , type nom_, type appli , type nom_, type comp , type nom_, type champ , type PMIOD_, type ou_, type SMIOC) {
16975 # Lit l'élément output d'un fichier PMIOD.xml ou SMIOC.xml
16976 # Pour un fichier PMIOD :
16977 #    cherche l'info <scatter> du point de couplage en question
16978 #
16979 # Pour un fichier SMIOC :
16980 #    lit les éléments XML concernant la connexion partant de ce point couplage
16981 #
16982 # Parametres d'entrée :
16983 #   - element_XML :      élément XML de type <output>
16984 #   - nom_appli :        nom de l'application
16985 #   - nom_comp :         nom du composant
16986 #   - nom_champ :        nom du champ de couplage
16987 #   - PMIOD_ou_SMIOC :   vaut PMIOD ou SMIOC
16988 #
16989 # Valeur de retour
16990 #   - Pour un fichier PMIOD, 1 si l'info <scatter> est presente, 0 sinon
16991 #   - Pour un fichier SMIOC, rien
16992 #
16993     global dico_applis
16994     
16995     global liste_points_i liste_points_o dico_caract_points
16996     global liste_cnx_in dico_cnx_out dico_caract_cnx
16997     
16998     global liste_fichiers dico_caract_fichiers
16999     global dico_units
17000     
17001     # Lit les sous-éléments de l'élément <output>
17002     # -------------------------------------------
17003     
17004     set ss_elem_list [dom::node children $element_XML]
17005     foreach element $ss_elem_list  {
17006         set name [dom::node configure $element -nodeName]
17007         switch $name {
17008             exchange_date {
17009                 # Si on lit un fichier SMIOC
17010                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
17011                     set exch_date [lit_element_exchange_date $element]
17012                 }
17013             }
17014             
17015             corresp_transi_in_name {
17016                 # Si on lit un fichier SMIOC
17017                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
17018                     # Lit la valeur du sous-élément <corresp_transi_in_name>
17019                     set txt_elem [dom::node configure $element -firstChild]
17020                     set corresp_name [dom::node configure $txt_elem -nodeValue]
17021                 }
17022             }
17023             
17024             file {
17025                 # Si on lit un fichier SMIOC
17026                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
17027                     array set dico_fichier {}
17028                     set output_file [lit_element_file $element dico_fichier]
17029                 }
17030             }
17031 
17032             component_name {
17033                 # Si on lit un fichier SMIOC
17034                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
17035                     # Lit la valeur du sous-élément <component_name>
17036                     set txt_elem [dom::node configure $element -firstChild]
17037                     set nom_comp_cible [dom::node configure $txt_elem -nodeValue]
17038                 }
17039             }
17040 
17041             lag {
17042                 # Si on lit un fichier SMIOC
17043                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
17044                     # Lit la valeur du sous-élément <lag>
17045                     set txt_elem [dom::node configure $element -firstChild]
17046                     set cnx_attribute(lag) [dom::node configure $txt_elem -nodeValue]
17047                 }
17048             }
17049 
17050             source_transformation {
17051                 array set cnx_attribute {}
17052                 lit_element_src_tgt_transfo $element cnx_attribute source $PMIOD_ou_SMIOC
17053             }
17054             
17055             debug_mode {
17056                 # Si on lit un fichier SMIOC
17057                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
17058                     # Lit la valeur du sous-élément <debug_mode> : a priori = "true"
17059                     set txt_elem [dom::node configure $element -firstChild]
17060                     set cnx_attribute(dbg_source) [dom::node configure $txt_elem -nodeValue]
17061                 }
17062             }
17063         }
17064     }
17065    
17066     # Si on lit un fichier PMIOD
17067     if {$PMIOD_ou_SMIOC == "PMIOD"} {
17068         # Retourne l'attribut <scattering> du point de couplage
17069         return [info exists cnx_attribute(scatter)]
17070     
17071     # Si on lit un fichier SMIOC
17072     } else {
17073 
17074         # Crée une nouvelle connexion
17075         # ---------------------------
17076         
17077         # Préfixe de la clef des éléments à mettre dans les dictionnaires dico_caract_points et dico_caract_cnx
17078         set prefixe "$nom_appli.$nom_comp.$nom_champ"
17079         
17080         # Lit l'attribut "transi_out_name"
17081         set value [dom::element getAttribute $element_XML "transi_out_name"]
17082     
17083         # L'élément <output> que l'on lit décrit une connexion sortante
17084         # Le numéro de la cnx est celui de l'attribut "transi_out_name"
17085         # "transi_out_name" a la forme <nom_champ>_out*** ou *** est un nombre
17086         set num_output [string range $value [string length "${nom_champ}_out"] end]
17087 
17088         # Ajoute la connexion a la liste
17089         lappend dico_caract_points($prefixe.o.lcnx) o$num_output
17090         
17091         # Clef de la connexion sortante : construite d'apres celle du point source
17092         set clef_cnx_out $prefixe.o$num_output
17093         
17094         # Si la cible est un composant d'une application
17095         if {[info exists nom_comp_cible]} {
17096             # Recherche le nom de l'application a partir du nom du composant
17097             foreach nom_appli_cible $dico_applis(lapplis) {
17098                 if {[lsearch $dico_applis(app.$nom_appli_cible.lcomp) $nom_comp_cible] != -1} {
17099                     break
17100                 }
17101             }
17102             # Nom du champ cible :  <nom-champ>_in<numero> ou  <numero> est normalement absent
17103             # Il faut extraire de cette chaine de caractères le nom du champ
17104             regexp {^(.*)_in[0-9]*$} $corresp_name match_str nom_champ_cible
17105         } else {
17106             # La cible de la connexion est un fichier NetCDF
17107             set nom_appli_cible ///file///
17108             set nom_comp_cible [NetCDF_file_cree_nouvel_id]
17109             # Champ cible = "field"; le nom reel du champ sera indiqué ailleurs
17110             set nom_champ_cible field
17111         }
17112     
17113         # Clef (index) de la connexion : celle de la connexion entrante, construite sur celle du point cible
17114         set clef_cnx_in $nom_appli_cible.$nom_comp_cible.$nom_champ_cible.i
17115         # Ajoute à la liste des connexions entrantes
17116         if {[lsearch $liste_cnx_in $clef_cnx_in] == -1} {
17117             lappend liste_cnx_in $clef_cnx_in
17118         }
17119         # Ajoute au dico des connexions sortantes
17120         set dico_cnx_out($clef_cnx_out) $clef_cnx_in
17121     
17122         # Renseigne les caractéristiques de la nouvelle connexion
17123         # -------------------------------------------------------
17124         
17125         # Source et cible
17126         set dico_caract_cnx($clef_cnx_in.app_source)   $nom_appli
17127         set dico_caract_cnx($clef_cnx_in.app_cible)    $nom_appli_cible
17128         set dico_caract_cnx($clef_cnx_in.comp_source)  $nom_comp
17129         set dico_caract_cnx($clef_cnx_in.comp_cible)   $nom_comp_cible
17130         set dico_caract_cnx($clef_cnx_in.champ_source) $nom_champ
17131         set dico_caract_cnx($clef_cnx_in.champ_cible)  $nom_champ_cible
17132         set dico_caract_cnx($clef_cnx_in.pt_source)    o$num_output
17133     
17134         # Période des échanges
17135         set dico_caract_cnx($clef_cnx_in.exch_per_unit)  [lindex $exch_date 0]
17136         set dico_caract_cnx($clef_cnx_in.exch_per_val)   [lindex $exch_date 1]
17137     
17138         # Initialise les autres caractéristiques
17139         foreach {caract default_value} {
17140             lag {} 
17141             dbg_source false
17142             stat_source_mask false
17143             stat_source_nmsk false
17144             stat_source_all false
17145             src_time_op  {}
17146             src_add_val  0
17147             src_mult_val 1 } {
17148             set dico_caract_cnx($clef_cnx_in.$caract) $default_value
17149         }
17150     
17151         # Renseigne les vraies valeurs pour ces autres caractéristiques
17152         foreach {name value} [array get cnx_attribute] {
17153             set dico_caract_cnx($clef_cnx_in.$name) $value
17154         }
17155     
17156         # Si la cible est un fichier NetCDF
17157         if {[info exists output_file]} {
17158             # Met a zéro des caractéristiques qui ne sont pas significatives 
17159             # pour une connexion vers un fichier
17160             foreach {caract default_value} {
17161                 dbg_cible false
17162                 stat_cible_mask false
17163                 stat_cible_nmsk false
17164                 stat_cible_all false
17165                 tgt_time_op  {}
17166                 tgt_add_val  0
17167                 tgt_mult_val 1
17168                 gather  false
17169                 remail {}   } {
17170                 set dico_caract_cnx($clef_cnx_in.$caract) $default_value
17171             }
17172         }
17173         
17174         # Mémorise la cible si c'est un fichier
17175         # -------------------------------------
17176         
17177         # Si la cible est un fichier NetCDF
17178         if {[info exists output_file]} {
17179             # Ajoute le fichier à la liste
17180             set id_fichier $nom_comp_cible
17181             lappend liste_fichiers $id_fichier
17182     
17183             # Renseigne les caractéristiques du fichier
17184             set dico_caract_fichiers($id_fichier.dir) output
17185             set dico_caract_fichiers($id_fichier.name) $output_file
17186             set dico_caract_fichiers($id_fichier.io_mode) $dico_fichier(io_mode)
17187             set dico_caract_fichiers($id_fichier.suffix) $dico_fichier(suffix)
17188             set dico_caract_fichiers($id_fichier.lchamps) field
17189     
17190             # Renseigne les caractéristiques du champ du fichier
17191             # Nom exact du champ
17192             set dico_caract_fichiers($id_fichier.field.name) $corresp_name
17193             # Autres caractéristiques 
17194             foreach caractere {packing scaling adding fill_value} {
17195                 if {[info exists dico_fichier($caractere)]} {
17196                     set dico_caract_fichiers($id_fichier.field.$caractere) $dico_fichier($caractere)
17197                 } else {
17198                     set dico_caract_fichiers($id_fichier.field.$caractere) ""
17199                 }
17200             }
17201             # type numérique est celui du champ source
17202             set dico_caract_fichiers($id_fichier.field.type) $dico_applis(app.$nom_appli.comp.$nom_comp.champ.$nom_champ.datatype)
17203     
17204             # Crée le point de couplage du fichier
17205             lappend liste_points_i $clef_cnx_in
17206             # Renseigne les caractéristiques du point de couplage
17207             set dico_caract_points($clef_cnx_in.appli) $nom_appli_cible
17208             set dico_caract_points($clef_cnx_in.comp)  $id_fichier
17209             set dico_caract_points($clef_cnx_in.champ) field
17210             set dico_caract_points($clef_cnx_in.lcnx)  i
17211             
17212             # Crée une nouvelle unité sur le graphe de couplage
17213             set dico_units(///file///.$id_fichier) $id_fichier
17214         }
17215     }
17216 }
17217 
17218 ret  {lit_element_rank} (type element , type prefixe) {
17219 # Lit dans un fichier SCC.xml le contenu d'un élément de type <rank>
17220 #  qui peut contenir trois sous-éléments : min_value, max_value et increment
17221 # min_value est toujours présent.
17222 #
17223 # Paramètres d'entrée :
17224 #  - element : element XML de type "rank"
17225 #  - prefixe : préfixe de la clef permettant d'accéder au valeurs dans le dictionnaire global "dico_applis"
17226 #
17227     global dico_applis
17228 
17229     # Lit les sous-éléments de l'élément <rank>
17230     set ss_elem_list [dom::node children $element]
17231     foreach element $ss_elem_list  {
17232         set name [dom::node configure $element -nodeName]
17233         if {$name != "#text" && $name != "#comment"} {
17234             switch $name {
17235                 min_value {
17236                     # Lit la valeur du sous-élément "min_value"
17237                     set txt_elem [dom::node configure $element -firstChild]
17238                     set value [dom::node configure $txt_elem -nodeValue]
17239                     set dico_applis($prefixe.min_rank) $value
17240                 }
17241                 max_value {
17242                     # Lit la valeur du sous-élément "max_value"
17243                     set txt_elem [dom::node configure $element -firstChild]
17244                     set value [dom::node configure $txt_elem -nodeValue]
17245                     set dico_applis($prefixe.max_rank) $value
17246                 }
17247                 increment {
17248                     # Pour l'instant, valeur ignorée
17249                 }
17250             }
17251         }
17252     }
17253 }
17254 
17255 ret  {lit_element_src_tgt_transfo} (type element_, type XML , type nom_, type dico , type source_, type ou_, type target , type PMIOD_, type ou_, type SMIOC) {
17256 # Lit un élément XML de type <source_transformation> ou <target_transformation>
17257 #
17258 # Remplit un dictionnaire contenant tous les parametres lus dans l'élément XML.
17259 #
17260 # Parametres d'entrée :
17261 #   - element_XML :       élément XML
17262 #   - nom_dico :          nom du dictionnaire a remplir
17263 #   - source_ou_target :  source ou target selon le type d'élément XML
17264 #   - PMIOD_ou_SMIOC :    vaut PMIOD ou SMIOC
17265 #
17266     upvar 1 $nom_dico cnx_attribute
17267 
17268     # Lit les sous-éléments de l'élément <source_transformation>
17269     set ss_elem_list [dom::node children $element_XML]
17270     foreach element $ss_elem_list  {
17271         set name [dom::node configure $element -nodeName]
17272         switch $name {
17273             source_time_operation -
17274             target_time_operation {
17275                 # Si on lit un fichier SMIOC
17276                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
17277                     # Opération sur le temps à la source ou à la cible
17278                     # Lit la valeur du sous-élément
17279                     set txt_elem [dom::node configure $element -firstChild]
17280                     if { $source_ou_target == "source" } {set clef src} else {set clef tgt}
17281                     set cnx_attribute(${clef}_time_op) [dom::node configure $txt_elem -nodeValue]
17282                 }
17283             }
17284             statistics {
17285                 # Si on lit un fichier SMIOC
17286                 if {$PMIOD_ou_SMIOC == "SMIOC"} {
17287                     # Lit les sous-éléments de l'élément <statistics>
17288                     # Traduit les sous-éléments XML (masked_points, notmasked_points, all_points) en autres mots-clefs
17289                     if { $source_ou_target == "source" } {set clef source} else {set clef cible}
17290                     array unset traduction
17291                     array set traduction "
17292                         masked_points stat_${clef}_mask 
17293                         notmasked_points stat_${clef}_nmsk 
17294                         all_points stat_${clef}_all
17295                         "
17296                     lit_element_liste_ss_elts $element cnx_attribute traduction
17297                 }
17298             }
17299             source_local_transformation -
17300             target_local_transformation {
17301                 # Lit les sous-éléments de l'élément <...._local_transformation>
17302                 # Traduit les sous-éléments XML (scattering, add_scalar, mult_scalar) en autres mots-clefs
17303                 if { $source_ou_target == "source" } {set clef src} else {set clef tgt}
17304                 array unset traductionert
17305                 array set traduction "
17306                     gathering gather
17307                     scattering scatter 
17308                     add_scalar ${clef}_add_val 
17309                     mult_scalar ${clef}_mult_val
17310                     "
17311                 lit_element_liste_ss_elts $element cnx_attribute traduction
17312             }
17313         }
17314     }
17315 }
17316 
17317 ret  {lit_fichier_AD} (type nom_, type fichier , type nom_, type dico , type nom_, type lcomp_, type ajout) {
17318 # Lit et décode un fichier XML contenant une AD (Application Description)
17319 # Lit aussi tous les fichiers PMIOD des composants listés dans ce fichier AD.
17320 # Suppose que les fichiers PMIOD sont dans le même répertoire que le fichier AD.
17321 #
17322 
17323     # Accède à la variable dont le nom est $nom_dico
17324     upvar 1 $nom_dico dict_vars
17325     # Accède à la liste des composants ajoutés
17326     upvar 1 $nom_lcomp_ajout liste_comp_ajoutes
17327     
17328     # Déclare toutes les variables globales
17329     global dico_caract_points
17330     global dico_units
17331     
17332     # Lit le document à manipuler
17333     set text [read [open $nom_fichier]]
17334     if { [string trim $text] != "" } {
17335         set code_erreur [catch {set doc [dom::parse $text]} msg_erreur]
17336         if {$code_erreur} {
17337             notice_show "Error parsing file \"$nom_fichier\" :\n $msg_erreur" error
17338             return 1
17339         }
17340     } else {
17341         # Fichier vide
17342         notice_show "Error parsing file \"$nom_fichier\" :\n file is empty" error
17343         return 1
17344     }
17345     
17346     # Extrait le nom de l'appli du nom du fichier : exemple "ocea4" extrait de "/chemin/de/oceoa4_ad.xml"
17347     if {[string match "*_ad.xml" $nom_fichier]} {
17348         set file_name [file tail $nom_fichier]
17349         set dir_name [file dirname $nom_fichier]
17350         set nom_appli [string range $file_name 0 end-7]
17351     } else {
17352         return 1
17353     }
17354     # Fait quelques vérifications
17355     if {! [dom::node hasChildNodes $doc] } {
17356         return 1
17357     }
17358     
17359     # Récupère le premier élément du document
17360     # C'est l'élément principal (qui est de type <Application>)
17361     set element1 [dom::document configure $doc -documentElement]
17362     if { [dom::node configure $element1 -nodeType] != "element" } {
17363         return 1
17364     }
17365     if { [dom::node configure $element1 -nodeName] != "application" } {
17366         return 1
17367     }
17368     
17369     # Lit les attributs de l'élément <Application>
17370     set att [dom::node configure $element1 -attributes]
17371     foreach {name value} [array get $att] {
17372         set dict_vars(app.$nom_appli.$name) $value
17373     }
17374     
17375     # Vérifie que l'attribut "local_name" correspond au nom de l'appli
17376     set local_name $dict_vars(app.$nom_appli.local_name)
17377     if {$local_name != $nom_appli} {
17378         notice_show "Local name of application \"$local_name\" is not consistent with file name \"$nom_fichier\" !" error
17379     }
17380     
17381     # A ce stade, l'appli est OK : on l'enregistre dans la liste
17382     lappend dict_vars(lapplis) $nom_appli
17383     
17384     # Lit les sous-éléments de l'élément <application>
17385     set ss_elem_list [dom::node children $element1]
17386     foreach element $ss_elem_list  {
17387         set name [dom::node configure $element -nodeName]
17388         if {$name != "#text" && $name != "#comment"} {
17389             switch $name {
17390                 nbr_procs {
17391                     lit_element_nbr_proc $element dict_vars "app.$nom_appli.proc"
17392                 }
17393     
17394                 platform {
17395                     set ss_elem [dom::node configure $element -firstChild]
17396                     set value [dom::node configure $ss_elem -nodeValue]
17397                     # Ajoute la plate-forme à la liste des plate-formes
17398                     if { [info exists dict_vars(app.$nom_appli.lplatforms)] } {
17399                         lappend dict_vars(app.$nom_appli.lplatforms) $value
17400                     } else {
17401                         set dict_vars(app.$nom_appli.lplatforms) $value
17402                     }
17403                 }
17404     
17405                 component {
17406                     # Lit les attributs local_name, long_name, simulated, etc..
17407                     set att [dom::node configure $element -attributes]
17408                     set component_name [load_XML_attributes $att dict_vars "app.$nom_appli.comp"]
17409                     # Ajoute le composants à la liste des composants
17410                     if { [info exists dict_vars(app.$nom_appli.lcomp)] } {
17411                         lappend dict_vars(app.$nom_appli.lcomp) $component_name
17412                     } else {
17413                         set dict_vars(app.$nom_appli.lcomp) $component_name
17414                     }
17415         
17416                     # Lit le sous-élément nbr_procs
17417                     set ss_elem_list [dom::node children $element]
17418                     foreach element $ss_elem_list  {
17419                         set name [dom::node configure $element -nodeName]
17420                         if {$name == "nbr_procs"} {
17421                             lit_element_nbr_proc $element dict_vars "app.$nom_appli.comp.$component_name.proc"
17422                             break
17423                         }
17424                     }
17425                     # Lit le fichier PMIOD du composant
17426                     set nom_fichier_PMIOD [file join $dir_name "$nom_appli\_$component_name\_pmiod.xml"]
17427                     set result [lit_fichier_component $nom_fichier_PMIOD dict_vars $nom_appli $component_name PMIOD]
17428         
17429                     # Si erreur
17430                     if { $result != 0 } {
17431                         notice_show  "Pb à la lecture du fichier $nom_fichier_PMIOD" error 
17432                     } else {
17433                         # Ajoute le composant à la liste
17434                         lappend liste_comp_ajoutes $nom_appli.$component_name
17435                         set dico_units($nom_appli.$component_name) $component_name
17436                     }
17437                 }
17438             }
17439         }
17440     }
17441     
17442     # Ajoute le nom de l'appli à la liste
17443     if { [info exists dict_vars(liste-applis)] } {
17444         lappend dict_vars(liste-applis) $nom_appli
17445     } else {
17446         set dict_vars(liste-applis) $nom_appli
17447     }
17448     # Libère la mémoire
17449     ::dom::DOMImplementation destroy $doc
17450     return 0
17451 }
17452     
17453 ret  {lit_fichier_Graphics} (type nom_, type fichier) {
17454 # Lit le fichier Graphics.xml contenant les attributs graphiques des unités du graphe de couplage
17455 
17456     global dico_applis liste_fichiers dico_units 
17457     global dico_caract_fichiers
17458     
17459     if {! [file isfile $nom_fichier]} {
17460         return 1
17461     }
17462     
17463     # Lit le document à manipuler
17464     set text [read [open $nom_fichier]]
17465     set doc [dom::parse $text]
17466     
17467     # Récupère le premier élément du document
17468     # C'est l'élément principal (qui est de type <scc>)
17469     set element1 [dom::document configure $doc -documentElement]
17470     if { [dom::node configure $element1 -nodeType] != "element" } {
17471         return 1
17472     }
17473     if { [dom::node configure $element1 -nodeName] != "graphics" } {
17474         return 1
17475     }
17476     
17477     # Lit les sous-éléments de l'élément <graphics>
17478     set ss_elem_list [dom::node children $element1]
17479     foreach element $ss_elem_list  {
17480         set name [dom::node configure $element -nodeName]
17481         if {$name == "component" || $name == "file"} {
17482             # Lit les attributs : name, application, coor_x, coor_y, colour, expand
17483             set att [dom::node configure $element -attributes]
17484 
17485             # Nom de l'application
17486             switch $name {
17487                 component {
17488                     # Nom de l'application
17489                     set nom_appli [set ${att}(application)]
17490                     # Nom du composant
17491                     set nom_comp [set ${att}(name)]
17492                     # Clef de l'unité dans le dictionnaire dico_units
17493                     set clef_unit $nom_appli.$nom_comp
17494                     set si_trouve 1
17495                 }
17496                 file {
17497                     # Nom du fichier
17498                     set nom_fichier [set ${att}(name)]
17499                     # Recherche l'id du fichier qui a ce nom
17500                     set si_trouve 0
17501                     foreach {attrib value} [array get dico_caract_fichiers "*.name"] {
17502                         if {$value == $nom_fichier} {
17503                             set si_trouve 1
17504                             # Identifiant du fichier dans le dictionnaire dico_caract_fichiers :
17505                             #   c'est la même que $attrib moins l'extension .name
17506                             set id_fichier [string range $attrib 0 end-5]
17507                             # Clef de l'unité dans le dictionnaire dico_units
17508                             set clef_unit ///file///.$id_fichier
17509                             break 
17510                         }
17511                     }
17512                 }
17513             }
17514             if { $si_trouve } {
17515                 # Mémorise les attributs graphiques de l'unité
17516                 foreach attribut {coor_x coor_y colour expand} {
17517                     set dico_units($clef_unit.$attribut) [set ${att}($attribut)]
17518                 }
17519             }
17520         }
17521     }
17522     
17523     # Libère la mémoire
17524     ::dom::DOMImplementation destroy $doc
17525     
17526     return 0
17527 }
17528 
17529 ret  {lit_fichier_SCC} (type nom_, type fichier) {
17530 # Lit et décode un fichier XML contenant un SCC (Coupling Configuration) auparavant généré par ce programme
17531 # Lit aussi tous les fichiers AD des application listés dans ce fichier SCC, si ils existent.
17532 # Lit aussi tous les fichiers PMIOD des composants listés dans les fichiers AD, si ils existent.
17533 #
17534 # Lit aussi tous les fichiers SMIOC des composants listés dans ce fichier SCC
17535 # et recrée toutes les connexions qui sont décrites dans ces fichiers.
17536 # Suppose que les fichiers SMIOC sont dans le même répertoire que le fichier SCC.
17537 #
17538 # Si un fichier SMIOC est manquant, ça peut être une erreur grave 
17539 # car il peut alors manquer une extrémité cible ou source d'une connexion sur le graphe de couplage.
17540 #
17541 # Si un fichier AD ou PMIOD est manquant, on compense avec les fichiers respectifs SCC et SMIOC.
17542 # Au pire, il manquera des points de couplage de composants mais qui ne seront pas connectés de toute façon.
17543 #
17544     global dico_applis
17545     global liste_fichiers dico_caract_fichiers
17546     global dico_experiment
17547 
17548     # Repertoire du fichier a lire
17549     set dir_name [file dirname $nom_fichier]
17550 
17551     # Lit le document à interprêter
17552     set text [read [open $nom_fichier]]
17553     if { [string trim $text] != "" } {
17554         set code_erreur [catch {set doc [dom::parse $text]} msg_erreur]
17555         if {$code_erreur} {
17556             notice_show "Error parsing file \"$nom_fichier\" :\n $msg_erreur" error
17557             return 1
17558         }
17559     } else {
17560         # Fichier vide
17561         notice_show "Error parsing file \"$nom_fichier\" :\n file is empty" error
17562         return 1
17563     }
17564 
17565     # Récupère le premier élément du document
17566     # C'est l'élément principal (qui est de type <scc>)
17567     set element1 [dom::document configure $doc -documentElement]
17568     if { [dom::node configure $element1 -nodeType] != "element" } {
17569         return 1
17570     }
17571     if { [dom::node configure $element1 -nodeName] != "scc" } {
17572         return 1
17573     }
17574     
17575     # Lit les sous-éléments de l'élément <scc>
17576     set ss_elem_list [dom::node children $element1]
17577     foreach element $ss_elem_list  {
17578         set name [dom::node configure $element -nodeName]
17579         if {$name != "#text" && $name != "#comment"} {
17580             switch $name {
17581                 experiment {
17582                     # Lit les attributs local_name, long_name, start_mode
17583                     set att [dom::node configure $element -attributes]
17584                     set dico_experiment(name)       [set ${att}(local_name)]
17585                     set dico_experiment(long_name)  [set ${att}(long_name)]
17586                     set dico_experiment(start_mode) [set ${att}(start_mode)]
17587 
17588                     # Accède aux sous-elements
17589                     set ss_elem_list [dom::node children $element]
17590                     foreach element $ss_elem_list  {
17591                         set name [dom::node configure $element -nodeName]
17592                         if {$name != "#text" && $name != "#comment"} {
17593                             switch $name {
17594                                 driver {
17595                                     # Accède au sous-élément "nbr_procs" : le premier sous-element de "driver"
17596                                     set nbr_procs_elt [lit_1er_sous_element $element]
17597                                     # Lit la valeur du sous-élément "nbr_procs"
17598                                     set txt_elem [dom::node configure $nbr_procs_elt -firstChild]
17599                                     set value [dom::node configure $txt_elem -nodeValue]
17600                                     set dico_experiment(drv_procs) $value
17601                                 }
17602                                 start_date {
17603                                     lit_element_date $element start
17604                                 }
17605                                 end_date {
17606                                     lit_element_date $element end
17607                                 }
17608                             }
17609                         }
17610                     }
17611                 }                
17612                 run {
17613                     # Accède aux sous-elements
17614                     set ss_elem_list [dom::node children $element]
17615                     foreach element $ss_elem_list  {
17616                         set name [dom::node configure $element -nodeName]
17617                         if {$name != "#text" && $name != "#comment"} {
17618                             switch $name {
17619                                 start_date {
17620                                     lit_element_date $element run_start
17621                                 }
17622                                 end_date {
17623                                     lit_element_date $element run_end
17624                                 }
17625                             }
17626                         }
17627                     }
17628                 }
17629                 application {
17630                     set erreur [lit_element_application $element $dir_name]
17631                     if {$erreur} {return 1}
17632                 }
17633             }
17634         }
17635     }
17636 
17637     # Libère la mémoire
17638     ::dom::DOMImplementation destroy $doc
17639 
17640     # Lit les connexions décrites dans les fichiers SMIOC
17641     # ---------------------------------------------------
17642     
17643     # Liste des applications
17644     set lapplis {}
17645     catch {set lapplis $dico_applis(lapplis)}
17646     
17647     # Pour toutes les applications
17648     foreach nom_appli $lapplis {
17649         # Pour tous les composants de cette application
17650         foreach nom_comp $dico_applis(app.$nom_appli.lcomp) {
17651             # Détermine le nom du fichier SMIOC (dans le même répertoire que le fichier SCC)
17652             set nom_fichier_SMIOC [file join $dir_name $nom_appli\_$nom_comp\_smioc.xml]
17653             # Lit les connexions du couplage dans le fichier SMIOC, s'il existe
17654             lit_fichier_component $nom_fichier_SMIOC dico_applis $nom_appli $nom_comp SMIOC
17655         }
17656     }
17657 
17658     # Vérifie la coherénce des connexions lues
17659     # ----------------------------------------
17660     global liste_cnx_in dico_cnx_out dico_caract_cnx
17661     global dico_caract_points
17662     
17663     # Pour toutes les connexions lues
17664     foreach clef_cnx_in $liste_cnx_in {
17665         # Si la source et la cible sont des composant d'application
17666         if {   $dico_caract_cnx($clef_cnx_in.app_source) != "///file///" 
17667             && $dico_caract_cnx($clef_cnx_in.app_cible) != "///file///"  } {
17668 
17669             # Source            
17670             set app_source   $dico_caract_cnx($clef_cnx_in.app_source)
17671             set comp_source  $dico_caract_cnx($clef_cnx_in.comp_source)
17672             set champ_source $dico_caract_cnx($clef_cnx_in.champ_source)
17673             set pt_source    $dico_caract_cnx($clef_cnx_in.pt_source)
17674             # Clef du point de couplage source
17675             set clef_pt_out $app_source.$comp_source.$champ_source.o
17676             # Liste des connexions au point de couplage source
17677             set lcnx_out ""
17678             catch {set lcnx_out $dico_caract_points($clef_pt_out.lcnx)}
17679             
17680             # Vérifie que la connexion est bien listée au niveau du point de couplage source
17681             set erreur 0
17682             if { [lsearch $lcnx_out $pt_source] == -1 } {
17683                 set erreur 1
17684             }
17685 
17686             # Liste des connexions au point de couplage cible
17687             set lcnx_in ""
17688             catch {set lcnx_in $dico_caract_points($clef_cnx_in.lcnx)}
17689             # Vérifie que la connexion est bien listée au niveau du point de couplage cible
17690             if { $lcnx_in == "" } {
17691                 set erreur 1
17692             }
17693 
17694             if {$erreur} {
17695                 set app_cible   $dico_caract_cnx($clef_cnx_in.app_cible)
17696                 set comp_cible  $dico_caract_cnx($clef_cnx_in.comp_cible)
17697                 set champ_cible  $dico_caract_cnx($clef_cnx_in.champ_cible)
17698                 notice_show  "Connection not consistently declared between field $app_source-$comp_source-$champ_source and field $app_cible-$comp_cible-$champ_cible !" error
17699                 return 1
17700             }
17701         }
17702     }
17703 
17704     # Lit les fichiers NetCDF qui sont en entrée
17705     # ------------------------------------------
17706     
17707     # Dictionnaire des fichiers lus
17708     array set dico_ajout {}
17709     # Init de la liste des fichiers lus
17710     set liste_fic_ajoutes {}
17711 
17712     # Pour tous les fichiers du graphe    
17713     foreach id_fichier $liste_fichiers {
17714         # Si le fichier est en entrée
17715         if { $dico_caract_fichiers($id_fichier.dir) == "input" } {
17716             # Détermine le nom du fichier (dans le même répertoire que le fichier SCC)
17717             set nom_fichier [file join $dir_name $dico_caract_fichiers($id_fichier.name)]
17718 
17719             # Si le fichier est présent dans le répertoire du SCC
17720             if { [file isfile $nom_fichier]} {
17721 
17722                 set fic_ajoute [NetCDF_file_read dico_ajout $nom_fichier]
17723                 if {$fic_ajoute != ""} {
17724                     lappend liste_fic_ajoutes $fic_ajoute
17725                 }
17726             }
17727         }
17728     }
17729     if { $liste_fic_ajoutes != ""} {
17730         # Met à jour les données avec les fichiers lus
17731         NetCDF_file_maj $liste_fic_ajoutes dico_ajout 0
17732     }
17733         
17734     # Lit les données graphiques du couplage dans le fichier Graphics.xml
17735     # -------------------------------------------------------------------
17736     
17737     # Initialise les données graphiques arbitrairement
17738     unit_init
17739 
17740     # Nom et chemin du fichier :  dans le même répertoire que le SCC
17741     set nom_fichier_Graphics [file join $dir_name "Graphics.xml"]
17742     set erreur [lit_fichier_Graphics $nom_fichier_Graphics]
17743     
17744     return 0
17745 }
17746 
17747 ret  {lit_fichier_component} (type nom_, type fichier , type nom_, type dico , type nom_, type appli , type nom_, type comp , type PMIOD_, type ou_, type SMIOC) {
17748 # Lit un fichier PMIOD ou SMIOC d'un composant
17749 #
17750 # Parametres d'entrée :
17751 #   - nom_fichier :      nom du fichier
17752 #   - nom_dico  :        nom du dictionnaire à remplir (habituellement dico_applis)
17753 #   - nom_appli :        nom de l'application
17754 #   - nom_comp :         nom du composant
17755 #   - PMIOD_ou_SMIOC :   vaut PMIOD ou SMIOC
17756 #
17757     upvar 1 $nom_dico dict_vars
17758     # Préfixe de la clef des éléments à mettre dans le dictionnaire
17759     set prefixe "app.$nom_appli.comp.$nom_comp"
17760     
17761     if { ![file isfile $nom_fichier] } {
17762         return 1
17763     }
17764     # Lit le document à manipuler
17765     set text [read [open $nom_fichier]]
17766     if { [string trim $text] == "" } {
17767         # Fichier vide
17768         notice_show "Error parsing file \"$nom_fichier\" :\n file is empty" error
17769         return 1
17770     }
17771        
17772     set doc [dom::parse $text]
17773     
17774     # Récupère le premier élément du document
17775     # C'est l'élément principal (qui est de type <prismcomponent>)
17776     set element1 [dom::document configure $doc -documentElement]
17777     
17778     # Lit les attributs de l'élément <prismcomponent>
17779     set att [dom::node configure $element1 -attributes]
17780     foreach {name value} [array get $att] {
17781         if { $name != "local_name" } {
17782             set dict_vars($prefixe.$name) $value
17783         }
17784     }
17785     
17786     # Lit les sous-éléments de l'élément <prismcomponent>
17787     set ss_elem_list [dom::node children $element1]
17788     foreach element $ss_elem_list  {
17789         set name [dom::node configure $element -nodeName]
17790         if {$name != "#text" && $name != "#comment"} {
17791             switch $name {
17792                 code {
17793                     # Pour l'instant, on ignore cet élément
17794                 }
17795     
17796                 grid {
17797                     # Lit les attributs local_name
17798                     set att [dom::node configure $element -attributes]
17799                     set grid_name [load_XML_attributes $att dict_vars "$prefixe.grid"]
17800                     # Ajoute la grille a la liste des grilles
17801                     if { [info exists dict_vars($prefixe.lgrids)] } {
17802                         # Si la grille n'existe pas
17803                         if {[lsearch $dict_vars($prefixe.lgrids) $grid_name] == -1 } { 
17804                             lappend dict_vars($prefixe.lgrids) $grid_name
17805                         }
17806                     } else {
17807                         set dict_vars($prefixe.lgrids) $grid_name
17808                     }
17809                     # Lit les sous-éléments
17810                     lit_element_grid $element dict_vars $nom_appli $nom_comp $grid_name
17811                 }
17812     
17813                 transient {
17814                     # Lit les attributs local_name, long_name, simulated, etc..
17815                     set att [dom::node configure $element -attributes]
17816                     set transient_name [load_XML_attributes $att dict_vars "$prefixe.champ"]
17817                     # Ajoute le point de couplage à la liste des points de couplage
17818                     if { [info exists dict_vars($prefixe.lchamps)] } {
17819                         # Si le point de couplage n'est pas déjà listé
17820                         if {[lsearch $dict_vars($prefixe.lchamps) $transient_name] == -1 } { 
17821                             lappend dict_vars($prefixe.lchamps) $transient_name
17822                         }
17823                     } else {
17824                         set dict_vars($prefixe.lchamps) $transient_name
17825                     }
17826                     # Lit les sous-éléments
17827                     lit_point_echange $element dict_vars $nom_appli $nom_comp $transient_name $PMIOD_ou_SMIOC
17828                 }
17829             }
17830         }
17831     }
17832     # Libère la mémoire
17833     ::dom::DOMImplementation destroy $doc
17834     return 0
17835 }
17836 
17837 ret  {lit_element_grid} (type element_, type XML , type nom_, type dico , type nom_, type appli , type nom_, type comp , type nom_, type grid) {
17838 # Lit dans un fichier XML (PMIOD ou SMIOC) d'un composant un élément <grid>
17839 #
17840 # Parametres d'entrée :
17841 #   - element_XML :      élément XML de type <grid>
17842 #   - nom_dico  :        nom du dictionnaire à remplir (habituellement dico_applis)
17843 #   - nom_appli :        nom de l'application
17844 #   - nom_comp :         nom du composant
17845 #   - nom_grid :         nom de la grille 
17846 #
17847     upvar 1 $nom_dico dict_vars
17848 
17849    # Préfixe de la clef des éléments à mettre dans le dictionnaire
17850    set prefixe "app.$nom_appli.comp.$nom_comp.grid.$nom_grid"
17851 
17852    # Lit les sous-éléments de l'élément <grid>
17853    set ss_elem_list [dom::node children $element_XML]
17854    foreach element $ss_elem_list  {
17855       set name [dom::node configure $element -nodeName]
17856       if {$name == "indexing_dimension"} {
17857             # Lit l'attribut "index" (1 ou 2 ou 3)
17858             set index [dom::element getAttribute $element "index"]
17859             switch $index {
17860                1 { set key $prefixe.periodi }
17861                2 { set key $prefixe.periodj }
17862                3 { set key $prefixe.periodk }
17863             }
17864             # Lit l'attribut "periodic" (true ou false)
17865             set value [dom::element getAttribute $element "periodic"]
17866             set dict_vars($key) $value
17867           }
17868    }
17869 }
17870 
17871 ret  {lit_point_echange} (type element_, type XML , type nom_, type dico , type nom_, type appli , type nom_, type comp , type nom_, type champ , type PMIOD_, type ou_, type SMIOC) {
17872 # Lit dans un fichier XML (PMIOD ou SMIOC) d'un composant un élément <transient>
17873 #
17874 # Parametres d'entrée :
17875 #   - element_XML :      élément XML de type <transient>
17876 #   - nom_dico  :        nom du dictionnaire à remplir (habituellement dico_applis)
17877 #   - nom_appli :        nom de l'application
17878 #   - nom_comp :         nom du composant
17879 #   - nom_champ :        nom du champ de couplage
17880 #   - PMIOD_ou_SMIOC :   vaut PMIOD ou SMIOC
17881 #
17882     upvar 1 $nom_dico dict_vars
17883 
17884    # Préfixe de la clef des éléments à mettre dans le dictionnaire
17885    set prefixe "app.$nom_appli.comp.$nom_comp.champ.$nom_champ"
17886 
17887    # Lit les sous-éléments de l'élément <transient>
17888    set ss_elem_list [dom::node children $element_XML]
17889    foreach element $ss_elem_list  {
17890       set name [dom::node configure $element -nodeName]
17891       switch $name {
17892          transient_standard_name {
17893             set value [lit_XML_texte $element]
17894             set dict_vars($prefixe.std_name) $value
17895          }
17896          physics {
17897             # Lit l'attribut "transient_type" (single ou vector)
17898             set value [dom::element getAttribute $element "transient_type"]
17899             set dict_vars($prefixe.type) $value
17900             # Lit les sous-éléments physical_units, valid_min et valid_max
17901             set ss_elem_list [dom::node children $element]
17902             foreach element $ss_elem_list  {
17903                set name [dom::node configure $element -nodeName]
17904                switch $name {
17905                   physical_units {
17906                      set value [lit_XML_texte $element]
17907                      set dict_vars($prefixe.units) $value
17908                   }
17909                   valid_min {
17910                      set value [lit_XML_texte $element]
17911                      set dict_vars($prefixe.min) $value
17912                   }
17913                   valid_max {
17914                      set value [lit_XML_texte $element]
17915                      set dict_vars($prefixe.max) $value
17916                   }
17917                }
17918             }
17919          }
17920          numerics {
17921             # Lit l'attribut "datatype" (xs:real, xs:double ou xs:integer)
17922             set value [dom::element getAttribute $element "datatype"]
17923             # Extrait le type (enlève "xs:")
17924             set value [string range $value 3 end]
17925             set dict_vars($prefixe.datatype) $value
17926          }
17927          computation {
17928          }
17929          intent {
17930             # Peut contenir un seul <input> et/ou plusieurs <output>
17931             lit_element_intent $element dict_vars $nom_appli $nom_comp $nom_champ $PMIOD_ou_SMIOC
17932          }
17933          transient_dependency {
17934          }
17935       }
17936    }
17937 }
17938 
17939 ret  {load_NetCDF_files} () {
17940 load_dir_files "Load NetCDF files" "*.nc" NetCDF_file_read_files NetCDF_file_add_new
17941 }
17942 
17943 ret  {load_XML_ad_files} () {
17944 load_dir_files "Load Application Description files" "*_ad.xml" read_AD_files
17945 }
17946 
17947 ret  {load_XML_attributes} (type nom_, type attr , type nom_, type dico , type prefixe) {
17948 upvar 1 $nom_attr attributs
17949    upvar 1 $nom_dico dict_vars
17950 
17951    set local_name $attributs(local_name)
17952    foreach {name value} [array get attributs] {
17953       if { $name != "local_name" } {
17954          set dict_vars($prefixe.$local_name.$name) $value
17955       }
17956    }
17957 
17958    return $local_name
17959 }
17960 
17961 ret  {load_dir_files} (type title , type initial_, type mask , type on_, type OK , optional on_new ={)} {
17962 # Permet de selectionner des fichiers d'un type particulier dans un repertoire
17963 #
17964 # Permet de creer un nouveau fichier si le parametre on_new est specifie.
17965 #
17966 # Paramètres d'entrée :
17967 #   - dirname (global)      : repertoire initial
17968 #   - title                 : titre de la boite de dialogue
17969 #   - initial_mask          : masque initial d'extension des fichiers
17970 #   - on_OK                 : commande a executer si appui sur le bouton OK
17971 #   - on_new                : commande a executer si appui sur le bouton New
17972 
17973     global dirname
17974     global masque
17975 
17976     if {![info exists dirname]} { dirname =  [pwd]}
17977     /*  if { $title == "Load Application Description files" } {*/
17978     /*      set dirname /localhome/epitalon/tr-OASIS4/oasis4/util/running/toyoa4/input*/
17979     /*  } else {*/
17980     /*      set dirname /localhome/epitalon/tr-OASIS4/oasis4/util/running/toyoa4/data*/
17981     /*  }*/
17982 
17983      u =  .
17984     /*  permet de choisir n fichiers dans un repertoire*/
17985 
17986     catch {destroy $u}
17987     toplevel $u
17988     wm title $u $title
17989     wm iconname $u "Select files"
17990 
17991     frame $u.fr1
17992     pack $u.fr1 -side top -fill x -padx 10 -pady 10
17993     label $u.fr1.lab1 -text "Directory :"
17994     pack $u.fr1.lab1 -side left
17995     label $u.fr1.lab2 -text $dirname -relief sunken -background grey97 -anchor w
17996     pack $u.fr1.lab2 -side left -expand yes -fill x  -padx 10
17997     button $u.fr1.b1 -text "Browse" -command {
17998                 global dirname
17999                  dir =  [tk_chooseDirectory -title "directory to 
18000                 if {$dir != ""} {
18001                      dirname =  $dir}
18002                     .
18003                     
18004                 }
18005     pack $u.fr1.b1 -side left
18006 
18007     frame $u.fr2
18008     pack $u.fr2 -side top -expand yes -fill x
18009     
18010     frame $u.fr2.f2
18011     pack $u.fr2.f2 -side left -fill both -padx 10 -pady 10 -expand yes
18012     pack [label $u.fr2.f2.lab1 -text "Select file list:"] -side top -anchor w
18013     listbox $u.fr2.f2.lb -background white  -yscrollc "$u.fr2.f2.sb " -selectmode =  extended
18014     scrollbar $u.fr2.f2.sb -bd 1 -command "$u.fr2.f2.lb yview"
18015     pack $u.fr2.f2.lb -side left -expand yes -fill both
18016     pack $u.fr2.f2.sb -side left -fill y
18017 
18018     frame $u.fr2.f3
18019      masque =  $initial_mask
18020     entry $u.fr2.f3.mask -textvariable masque -background white
18021     pack $u.fr2.f3 -side left -fill y -padx 10 -pady 10
18022     pack [label $u.fr2.f3.lab2 -text "File Mask:"] -side top -anchor w
18023     pack  $u.fr2.f3.mask  -expand yes -fill x -side top -anchor n
18024 
18025     frame $u.fr2.f3.bu
18026     pack $u.fr2.f3.bu -side bottom -fill x
18027     button $u.fr2.f3.bu.ok -text "OK" -command "
18028     button $u.fr2.f3.bu.cancel -text "Cancel" -command "destroy $u"
18029     pack $u.fr2.f3.bu.cancel  -side left
18030     pack  $u.fr2.f3.bu.ok -side right
18031 
18032     bind $u.fr2.f3.mask <Return> "
18033     
18034 
18035     if { $on_new != "" } {
18036         frame $u.fr3
18037         pack $u.fr3 -side bottom -expand yes -fill x -pady 10
18038         
18039         label $u.fr3.lab1 -text "Or enter new file name :"
18040         pack $u.fr3.lab1 -side top -anchor w -padx 10
18041         
18042         frame $u.fr3.f1
18043         pack $u.fr3.f1 -side top -expand yes -fill x
18044         
18045         global new_filename_m;  new = _filename_m ""
18046         entry $u.fr3.f1.e1 -textvariable new_filename_m -background white
18047         pack $u.fr3.f1.e1 -side left -fill x -padx 10 -expand yes
18048         button $u.fr3.f1.new -text "New" -command "global new_filename_m; $on_new \$new_filename_m; un new = _filename_m; destroy $u"
18049         pack  $u.fr3.f1.new -side right  -padx 10 
18050     }
18051 }
18052 
18053 ret  {load_files} (type on_, type OK) {
18054 #
18055     global dirname
18056     set lst ""
18057     catch {set lst [selection get -displayof .load_dir.fr2.f2.lb]}
18058     set file_list ""
18059     foreach elm $lst {
18060         set file [file join $dirname $elm]
18061         lappend file_list $file
18062     }
18063     if {$file_list != ""} {$on_OK $file_list}
18064 }
18065 
18066 ret  {load_maj_listfile} () {
18067 #**** load_maj_listfile
18068 #
18069 #     Purpose:
18070 #     --------
18071 #     update file list if the user enters a new mask file
18072 #
18073 #     Interface:
18074 #     ----------
18075 #     load_maj_listfile
18076 #
18077 #
18078 #***  History:
18079 #     -------
18080 #       Version   Programmer     Date        Description
18081 #       -------   ----------     ----        -----------
18082 #       0.0       Thierry M.   2000/10/04     created
18083 #
18084 #*----------------------------------------------------------------
18085     global dirname
18086     global masque
18087     .load_dir.fr2.f2.lb delete 0 end
18088     foreach elm $masque {
18089         set loclist {}
18090         catch {set loclist [glob -directory $dirname $elm]}
18091         foreach filename $loclist {
18092             if {[file isfile $filename] } {.load_dir.fr2.f2.lb insert end [file tail $filename]}
18093         }
18094     }
18095 }
18096 
18097 ret  {maj_composants_ajoutes} (type nom_, type dico_, type ajout , type liste_, type comp_, type ajoutes) {
18098 #
18099     # Accède à la variable dont le nom est $nom_dico_ajout
18100     upvar 1 $nom_dico_ajout dico_ajout
18101     
18102     global dico_applis
18103     global dico_units
18104     global liste_points_i  liste_points_o
18105     global dico_caract_points
18106     global dico_cnx_out dico_caract_cnx
18107     global oasis4_version
18108 
18109     # Position initiale du composant sur le graphe : en haut à gauche
18110     set position_x 80
18111     set position_y 25
18112 
18113     # Si besoinm initialise la liste des applications déjà chargées en mémoire
18114     if { ! [info exists dico_applis(lapplis)] } {
18115         set dico_applis(lapplis) {}
18116     }
18117     
18118     # Pour toutes les applis ajoutées
18119     foreach nom_appli $dico_ajout(lapplis) {
18120         # Si l'appli est chargée en mémoire pour la première fois
18121         if { [lsearch $dico_applis(lapplis) $nom_appli] == -1 } { 
18122             # Ajoute l'appli a la liste
18123             lappend dico_applis(lapplis) $nom_appli
18124             # Initialise la liste des composants de l'appli
18125             set dico_applis(app.$nom_appli.lcomp) {}
18126         } else {
18127         
18128             # Si le nombre de processus de l'appli a changé
18129             if {  [array get dico_ajout "app.$nom_appli.proc.*"] != [array get dico_applis "app.$nom_appli.proc.*"] } {
18130             
18131                 # Vérifie la répartition des proc sur les ordinateurs hosts de l'application
18132                 # --------------------------------------------------------------------------
18133             
18134                 # Si la liste des ordinateurs hosts existe déja
18135                 if {   [info exists dico_applis(app.$nom_appli.lhosts)]
18136                     && [llength $dico_applis(app.$nom_appli.lhosts)] != 0 } {
18137                     
18138                     set nb_proc_total 0
18139                     foreach id_host $dico_applis(app.$nom_appli.lhosts) {
18140                         # Additionne au total
18141                         incr nb_proc_total $dico_applis(app.$nom_appli.host.$id_host.nb_procs)
18142                     }
18143                     
18144                     # Vérifie que le nombre de processus est dans la fourchette acceptable pour l'appli
18145                     set si_OK 1
18146                     set min_proc $dico_applis(app.$nom_appli.proc.min_value)
18147                     set max_proc $dico_applis(app.$nom_appli.proc.max_value)
18148                     set increment $dico_applis(app.$nom_appli.proc.increment)
18149                     if { $nb_proc_total >= $min_proc && $nb_proc_total <= $max_proc } {
18150                     
18151                         # Vérifie que le nombre est un multiple attendu
18152                         if {[expr ($nb_proc_total - $min_proc) % $increment] == 0} {
18153                             # OK
18154                         } else {
18155                             set si_OK 0
18156                         }
18157                     } else {
18158                         set si_OK 0
18159                     }
18160                     
18161                     # Si pas OK
18162                     if { ! $si_OK } {
18163                         # Annule la répartition des proc. sur les ordinateurs hosts
18164                         foreach id_host $dico_applis(app.$nom_appli.lhosts) {
18165                             set dico_applis(app.$nom_appli.host.$id_host.nb_procs) ""
18166                         }
18167                     }
18168                 }
18169             }
18170         }
18171         
18172         # Recopie les attributs de l'appli
18173         foreach attrib {oasis4_version start_mode coupling_mode proc.min_value proc.max_value proc.increment} {
18174             set dico_applis(app.$nom_appli.$attrib) $dico_ajout(app.$nom_appli.$attrib)
18175         }
18176         catch { set dico_applis(app.$nom_appli.long_name) $dico_ajout(app.$nom_appli.long_name) }
18177 
18178         # Met a jour la var globale 'oasis4_version'
18179         if {![info exists oasis4_version] && [info exists dico_applis(app.$nom_appli.oasis4_version)]} {
18180             set oasis4_version $dico_applis(app.$nom_appli.oasis4_version)
18181         }
18182     }
18183     
18184     # Pour tous les composants ajoutés
18185     foreach clef_comp $liste_comp_ajoutes {
18186 
18187         # Nom du composant
18188         set nom_composant $dico_units($clef_comp)
18189         # Extrait de la clef le nom de l'appli
18190         set len_comp [string length $nom_composant]
18191         incr len_comp
18192         set nom_appli [string range $clef_comp 0 end-$len_comp]
18193 
18194         # Si le composant est chargé en mémoire pour la première fois
18195         if { [lsearch $dico_applis(app.$nom_appli.lcomp) $nom_composant] == -1 } {
18196 
18197             # Ajoute le composant a la liste
18198             lappend dico_applis(app.$nom_appli.lcomp) $nom_composant
18199             # Ajoute toutes ses infos au dictionnaire global des applis et composants
18200             array set dico_applis [array get dico_ajout "app.$nom_appli.comp.$nom_composant.*"]
18201             
18202             # Place le composant sur le graphe
18203             set dico_units($clef_comp.coor_x) $position_x
18204             set dico_units($clef_comp.coor_y) $position_y
18205             incr position_y 80
18206             # Met le composant dans l'état "connexions visibles"
18207             set dico_units($clef_comp.expand) 1
18208             # Choisit une couleur pour le composant
18209             set dico_units($clef_comp.colour) [color_rgb [expr int(rand()*254)] [expr int(rand()*254)] [expr int(rand()*254)]]
18210     
18211             # Ajoute tous les points d'échange (de couplage) de ce composant
18212             # à la liste générale des points de couplage
18213             set liste_champs $dico_applis(app.$nom_appli.comp.$nom_composant.lchamps)
18214             foreach nom_champ $liste_champs  {
18215                 set clef_base "$clef_comp.$nom_champ"
18216     
18217                 # Si le champ de couplage est en entrée
18218                 if { [info exists dico_applis(app.$nom_appli.comp.$nom_composant.champ.$nom_champ.input)] } {
18219                     set clef "$clef_base.i"
18220                     lappend liste_points_i $clef
18221                     set dico_caract_points($clef.appli) $nom_appli
18222                     set dico_caract_points($clef.comp) $nom_composant
18223                     set dico_caract_points($clef.champ) $nom_champ
18224                 }
18225     
18226                 # Si le champ de couplage est en sortie
18227                 if { [info exists dico_applis(app.$nom_appli.comp.$nom_composant.champ.$nom_champ.output)] } {
18228                     set clef "$clef_base.o"
18229                     lappend liste_points_o $clef
18230                     set dico_caract_points($clef.appli) $nom_appli
18231                     set dico_caract_points($clef.comp) $nom_composant
18232                     set dico_caract_points($clef.champ) $nom_champ
18233                 }
18234             }
18235             Draw_one_unit $clef_comp
18236 
18237         } else {
18238 
18239             # Etablit la liste des points de couplage du nouveau composant
18240             set liste_pts_couplage {}
18241             # Pour tous les champs du composant chargé en mémoire
18242             foreach nom_champ $dico_ajout(app.$nom_appli.comp.$nom_composant.lchamps) {
18243                 # Ajoute un ou deux points de couplage
18244                 set clef_base "$clef_comp.$nom_champ"
18245                 
18246                 # Si le champ de couplage est en entrée
18247                 if { [info exists dico_ajout(app.$nom_appli.comp.$nom_composant.champ.$nom_champ.input)] } {
18248                     set clef "$clef_base.i"
18249                     lappend liste_pts_couplage $clef
18250                 }
18251     
18252                 # Si le champ de couplage est en sortie
18253                 if { [info exists dico_ajout(app.$nom_appli.comp.$nom_composant.champ.$nom_champ.output)] } {
18254                     set clef "$clef_base.o"
18255                     lappend liste_pts_couplage $clef
18256                 }
18257             }
18258 
18259             # Liste des points de couplage du composant existant auparavant
18260             set liste_clefs_points_i [lsearch -glob -all -inline $liste_points_i "$clef_comp.*"]
18261             set liste_clefs_points_o [lsearch -glob -all -inline $liste_points_o "$clef_comp.*"]
18262             
18263             # Vérifie la liste des points de couplage et cherche des points supprimés ou ajoutés
18264             # ----------------------------------------------------------------------------------
18265 
18266             # Pour tous les points de couplage du composant chargé en mémoire
18267             foreach clef_point $liste_pts_couplage {
18268                 # Si c'est un point de couplage en sortie
18269                 if { [string match *.o $clef_point] } { 
18270                     # Si le point de couplage n'existait pas auparavant
18271                     if { [lsearch $liste_clefs_points_o $clef_point] == -1} {
18272                         # Ajoute un point de couplage
18273                         lappend liste_points_o $clef_point
18274                         set dico_caract_points($clef_point.appli) $nom_appli
18275                         set dico_caract_points($clef_point.comp) $nom_composant
18276                         # Retrouve le nom du champ
18277                         set pos [string length "$clef_comp." ]
18278                         set nom_champ [string range $clef_point $pos end-2]
18279                         set dico_caract_points($clef_point.champ) $nom_champ
18280                     }
18281                 # Si le point de couplage est en entrée
18282                 } else {
18283                     
18284                     # Si le point de couplage n'existait pas auparavant
18285                     if { [lsearch $liste_clefs_points_i $clef_point] == -1} {
18286                         # Ajoute un point de couplage
18287                         lappend liste_points_i $clef_point
18288                         set dico_caract_points($clef_point.appli) $nom_appli
18289                         set dico_caract_points($clef_point.comp) $nom_composant
18290                         # Retrouve le nom du champ
18291                         set pos [string length "$clef_comp." ]
18292                         set nom_champ [string range $clef_point $pos end-2]
18293                         set dico_caract_points($clef_point.champ) $nom_champ
18294                     }
18295                 }
18296             }
18297             
18298             # Init liste des composants ou fichiers touchés par les connexions supprimées
18299             set liste_comp_touches {}
18300             # Pour tous les points de couplage qui existaient auparavant
18301             foreach clef_point [concat $liste_clefs_points_i $liste_clefs_points_o] {
18302                 # Si le point de couplage n'existe plus
18303                 if { [lsearch $liste_pts_couplage $clef_point] == -1} {
18304                     # Teste si le champ est connecté
18305                     set lcnx ""
18306                     catch {set lcnx $dico_caract_points($clef_point.lcnx)}
18307                     # Pour toutes les connexions entrant ou sortant de ce point
18308                     foreach num_cnx $lcnx {
18309                         # Si c'est une connexion entrante
18310                         if { $num_cnx == "i" } {
18311                             set clef_cnx_in $clef_point
18312                             # Memorise le composant source de la connexion
18313                             set appli_source $dico_caract_cnx($clef_cnx_in.app_source)
18314                             set comp_source  $dico_caract_cnx($clef_cnx_in.comp_source)
18315                             set comp_touche $appli_source.$comp_source
18316                         } else {
18317                             # C'est une connexion sortante : le numéro $num_cnx vaut "o1", "o2" ou "o#"....
18318                             # Enlève la lettre "o" qui est en tête
18319                             set num [string range $num_cnx 1 end]
18320                             # Clef de la cnx sortante
18321                             set clef_cnx_out "$clef_point$num"
18322                             # Clef de la connexion : point de couplage cible de la connexion
18323                             set clef_cnx_in $dico_cnx_out($clef_cnx_out)
18324                             # Memorise le composant cible de la connexion
18325                             set appli_cible $dico_caract_cnx($clef_cnx_in.app_cible)
18326                             set comp_cible  $dico_caract_cnx($clef_cnx_in.comp_cible)
18327                             set comp_touche $appli_cible.$comp_cible
18328                         }
18329                         # Met a jour la liste des composants touchés
18330                         lappend liste_comp_touches $comp_touche
18331                         # Supprime la connexion
18332                         comm_remove $clef_cnx_in
18333                     }
18334                     # Si c'est une point de couplage en entrée
18335                     if { [string range $clef_point end end] == "i" } {
18336                         # Met a jour la liste des points en entrée
18337                         set liste_points_i [lsearch -not -all -inline -glob $liste_points_i "$clef_point"]
18338                     } else {
18339                         # Met a jour la liste des points en sortie
18340                         set liste_points_o [lsearch -not -all -inline -glob $liste_points_o "$clef_point"]
18341                     }
18342                     # Supprime les caractéristiques du point
18343                     array unset dico_caract_points "$clef_point.*"
18344                 }
18345             }
18346             
18347             # Met à jour en mémoire les champs du composant et leurs attributs
18348             array unset dico_applis "app.$nom_appli.comp.$nom_composant.champ.*"
18349             array set dico_applis [array get dico_ajout "app.$nom_appli.comp.$nom_composant.champ.*"]
18350             
18351             # Met à jour en mémoire la liste des champs du composant
18352             set dico_applis(app.$nom_appli.comp.$nom_composant.lchamps) $dico_ajout(app.$nom_appli.comp.$nom_composant.lchamps)
18353 
18354             # Met a jour les attributs du composant
18355             foreach attrib {long_name oasis4_version simulated proc.min_value proc.max_value proc.increment} {
18356                 set dico_applis(app.$nom_appli.comp.$nom_composant.$attrib) $dico_ajout(app.$nom_appli.comp.$nom_composant.$attrib)
18357             }
18358             
18359             # Pour tous les composants touchés
18360             foreach composant $liste_comp_touches {
18361                 # Redessine le composant
18362                 Draw_one_unit $composant
18363             }
18364 
18365             # Affiche le nouveau composant sur le graphe
18366             Draw_one_unit $clef_comp
18367             # Redessine les connexions du composant
18368             Draw_comm $clef_comp
18369 
18370         }
18371     }
18372 }
18373 
18374 ret  {minimenu_comm} (type i) {
18375 #
18376     global entityselected objectselected tagselected oldcolor
18377 
18378     # Deselectionne les points de couplage
18379     comm_new menage -1
18380 
18381     set w .gui.pr.cpd22.03
18382 
18383     # Si il y avait un autre objet selectionne
18384     if {$objectselected != ""} {
18385         # Deselectionne ce composant sur le graphe
18386         $w itemconfigure $tagselected -fill $oldcolor
18387     }
18388 
18389     # Change la couleur de l'element sélectionné sur le graphe
18390     set objectselected $i
18391     set tagselected tag_fils_connexions($i)
18392     set oldcolor [$w itemcget $tagselected  -fill ]
18393     $w itemconfigure $tagselected -fill red
18394 
18395     # Changer le type d'éléments listes et refaire la liste
18396     # et sélectionner le bon élément dans la liste
18397     set entityselected CONNECTION
18398     control_entity $entityselected $i
18399 }
18400 
18401 ret  {minimenu_unit} (type i) {
18402 #
18403     global entityselected objectselected tagselected oldcolor
18404 
18405     # Désélectionne les points de couplage
18406     comm_new menage -1
18407 
18408     set w .gui.pr.cpd22.03
18409 
18410     # Si il y avait un autre composant sélectionné
18411     if {$tagselected != ""} {
18412         # Désélectionne ce composant sur le graphe
18413         $w itemconfigure $tagselected -fill $oldcolor
18414     }
18415 
18416     # Change la couleur du composant sélectionné sur le graphe
18417     set tagselected unitpoint_color($i)
18418     set oldcolor [$w itemcget $tagselected  -fill ]
18419     $w itemconfigure $tagselected -fill red
18420 
18421     # Changer le type d'éléments listes et refaire la liste
18422     # et sélectionner le bon élément dans la liste
18423     
18424     # Si il s'agit d'un fichier NetCDF
18425     if { [string match ///file///.* $i] } {
18426         set entityselected FILE
18427     } else {
18428         set entityselected COMPONENT
18429     }
18430     control_entity $entityselected $i
18431     set objectselected $i
18432 }
18433 
18434 ret  {notice_show} (type mesg , optional icon =info) {
18435 #
18436 #**** notice_show - notice show
18437 #
18438 #     Purpose:
18439 #     --------
18440 #     Show a dialog box with a message and chosen icon
18441 #
18442 #     Interface:
18443 #     ----------
18444 #         notice_show $mesg info
18445 #
18446 #         Inputs :
18447 #         --------
18448 #           mesg : variable containing the message to be written
18449 #           info : the chosen icon (info, error)
18450 #
18451 #***  Externals:
18452 #     ----------
18453 #     This code is using Tcl/Tk
18454 #
18455 #     References:
18456 #     -----------
18457 #     Effective Tcl/Tk Programming, M. Harrison and M. McLennan,
18458 #     Addison-Wesley professional computing series, 1998
18459 #
18460 #     History:
18461 #     -------
18462 #       Version   Programmer     Date        Description
18463 #       -------   ----------     ----        -----------
18464 #       0.0       Anne         1999/01/11       created
18465 #*----------------------------------------------------------------
18466 #
18467 #** Declaration
18468 #
18469 
18470   set top [dialog_create Notice]
18471   set x [expr [winfo rootx .gui]+200]
18472   set y [expr [winfo rooty .gui]+200]
18473   wm geometry $top "+$x+$y"
18474   set info [dialog_info $top]
18475   label $info.icon -bitmap $icon
18476   pack $info.icon -side left -padx 8 -pady 8
18477   label $info.mesg -text $mesg -wraplength 4i
18478   pack $info.mesg -side right -expand yes -fill both  -padx 8 -pady 8
18479   set cntls [dialog_controls $top]
18480   button $cntls.dismiss -command "destroy $top" -text OK
18481   pack $cntls.dismiss -pady 4
18482   return $top
18483 }
18484 
18485 ret  {open_file} (type file , type mode) {
18486 set f [open $file $mode]
18487     if {$mode == "w" || $mode == "a"} {
18488       fconfigure $f -translation lf
18489     }
18490     return $f
18491 }
18492 
18493 ret  {printer_create} (type top , type filename) {
18494 global prInfo
18495     global global_font
18496 
18497     set top [dialog_create Printer $top]
18498 
18499     set info [dialog_info $top]
18500     radiobutton $info.printer  -text "Send to printer with command: "  -variable prInfo($top-where) -value "printer" -font $global_font
18501     entry $info.printerCmd -font $global_font -background white
18502     radiobutton $info.file  -text "Save output in file: "  -variable prInfo($top-where) -value "file" -font $global_font
18503 
18504     entry $info.fileName -font $global_font -background white
18505 
18506     grid $info.printer    -row 0 -sticky w
18507     grid $info.printerCmd -row 1 -sticky ew
18508     grid rowconfigure $info 2 -minsize 6
18509     grid $info.file       -row 3 -sticky w
18510     grid $info.fileName   -row 4 -sticky ew
18511 
18512     $info.printerCmd insert 0 "lpr"
18513     $info.fileName insert 0 "$filename.eps"
18514 
18515     $info.printer invoke
18516 
18517     bind $info.printerCmd <FocusIn> "$info.printer invoke"
18518     bind $info.fileName <FocusIn> "$info.file invoke"
18519 
18520     set cntls [dialog_controls $top]
18521     button $cntls.ok -command "set prInfo($top-status) 1" -font $global_font
18522     pack $cntls.ok -side left -expand yes
18523     focus $cntls.ok
18524 
18525     button $cntls.cancel -command "set prInfo($top-status) 0" -font $global_font
18526     pack $cntls.cancel -side left -expand yes
18527 
18528     wm protocol $top WM_DELETE_WINDOW "$cntls.cancel invoke"
18529     wm withdraw $top
18530 
18531 
18532     return $top
18533 }
18534 
18535 ret  {printer_print} (type top , type cmd , optional ok =Print , optional cancel =Cancel) {
18536 global prInfo
18537 global file_lue
18538 
18539     set cntls [dialog_controls $top]
18540     $cntls.ok configure -text $ok
18541     $cntls.cancel configure -text $cancel
18542 
18543     dialog_wait $top prInfo($top-status)
18544 
18545     if {$prInfo($top-status)} {
18546         switch $prInfo($top-where) {
18547             printer {
18548                 set info [dialog_info $top]
18549 
18550                 set print [format {
18551                     exec %s << [%s]
18552                 } [$info.printerCmd get] $cmd]
18553 
18554                 if {[catch $print result] != 0} {
18555                     notice_show $result error
18556                 } elseif {[string trim $result] != ""} {
18557                     notice_show $result info
18558                 } else {
18559                     notice_show "Document printed" info
18560                 }
18561             }
18562             file {
18563                 set info [dialog_info $top]
18564                 set fname [$info.fileName get]
18565         if {[file pathtype $fname] == "relative"} {
18566             set fname [file dirname $file_lue]/$fname
18567         }
18568                 set print [format {
18569                     set fid [open_file %s w]
18570                     puts -nonewline $fid [%s]
18571                     close $fid
18572                 } $fname $cmd]
18573 
18574                 if {[catch $print result] != 0} {
18575                     notice_show $result error
18576                 } else {
18577                     notice_show "Output saved in file \"$fname\"" info
18578                 }
18579             }
18580         }
18581     }
18582 }
18583 
18584 ret  {quit_gui_oasis} () {
18585 #
18586     if {![file isdirectory  ~/.OASIS]} {catch {file mkdir ~/.OASIS}}
18587     catch {set f [open_file ~/.OASIS/gui_oasis_window w]
18588         puts $f "[winfo geometry .gui]"
18589         close $f
18590     }
18591 
18592     global dico_applis
18593     set quitter 1
18594 
18595     # S'il y a un couplage a l'ecran (au moins une appli chargee)    
18596     set liste_applis {}
18597     catch { set liste_applis $dico_applis(lapplis) }
18598     if {[llength $liste_applis]} {
18599         set rep [tk_messageBox -message "Save current coupling configuration ?" -type yesno -icon question]
18600         if {$rep == "yes"} {
18601             set quitter [sauve_tout]
18602         }
18603     }
18604     if {$quitter == 1} {exit}
18605 }
18606 
18607 ret  {read_AD_files} (type file_, type list) {
18608 #
18609     global entityselected
18610     # Liste des composants ajoutés
18611     set liste_comp_ajoutes {}
18612     array set dico_ajout {}
18613     
18614     # Lit les fichiers XML : AD et les PMIOD relatifs
18615     foreach file $file_list {
18616         if {$file != ""} {
18617             lit_fichier_AD $file dico_ajout liste_comp_ajoutes
18618         }
18619     }
18620     # Met à jour les données et le graphe avec les composants ajoutes
18621     maj_composants_ajoutes dico_ajout $liste_comp_ajoutes
18622     
18623     if { $entityselected == "COMPONENT" } {
18624         control_entity $entityselected
18625     }
18626 }
18627 
18628 ret  {scrollform_create} (type win) {
18629 # ----------------------------------------------------------------------
18630 #  USAGE:  scrollform_resize <win>
18631 #
18632 #  Used internally to handle size changes in the form area within
18633 #  a scrollform assembly.  Updates the canvas to recognize the new
18634 #  scrolling area.
18635 # ----------------------------------------------------------------------
18636 proc scrollform_resize {win} {
18637     set bbox [$win.vport bbox all]
18638     set wid [winfo width $win.vport.form]
18639     $win.vport configure -width $wid  -scrollregion $bbox -yscrollincrement 0.1i
18640 }
18641 
18642 # ----------------------------------------------------------------------
18643 #  USAGE:  scrollform_interior <win>
18644 #
18645 #  Returns the name of the interior frame that represents the
18646 #  body of the scrollform.  Widgets should be packed in this
18647 #  frame to build the form.
18648 # ----------------------------------------------------------------------
18649 proc scrollform_interior {win} {
18650     return "$win.vport.form"
18651 }
18652 
18653 # ----------------------------------------------------------------------
18654 #  EXAMPLE: use the canvas to build a scrollable form
18655 # ----------------------------------------------------------------------
18656 #  Effective Tcl/Tk Programming
18657 #    Mark Harrison, DSC Communications Corp.
18658 #    Michael McLennan, Bell Labs Innovations for Lucent Technologies
18659 #    Addison-Wesley Professional Computing Series
18660 # ======================================================================
18661 #  Copyright (c) 1996-1997  Lucent Technologies Inc. and Mark Harrison
18662 # ======================================================================
18663 
18664 # ----------------------------------------------------------------------
18665 #  USAGE:  scrollform_create <win>
18666 #
18667 #  Creates an empty scrollform assembly.  The interior frame for this
18668 #  form can be found by calling "scrollform_interior".  Widgets packed
18669 #  into the interior can be scrolled in the vertical direction.
18670 # ----------------------------------------------------------------------
18671 frame $win -class Scrollform
18672 
18673 scrollbar $win.sbar -command "$win.vport yview"
18674 pack $win.sbar -side right -fill y
18675 
18676 canvas $win.vport -yscrollcommand "$win.sbar set"
18677 pack $win.vport -side left -fill both -expand true
18678 
18679 frame $win.vport.form
18680 $win.vport create window 0 0 -anchor nw -window $win.vport.form
18681 
18682 bind $win.vport.form <Configure> "scrollform_resize $win"
18683 
18684 return $win
18685 }
18686 
18687 ret  {unit_delete} (type clef_, type unit) {
18688 # Quand l'utilisateur appuie sur la touche <delete> sur une unité (composant ou fichier) dans le graphe
18689 #   ceci provoque la suppression de cette unité (pour les fichiers seulement, pour l'instant)
18690 #
18691     global dico_applis dico_units dico_caract_fichiers
18692     global oldcolor tagselected
18693 
18694     set nom_unit $dico_units($clef_unit)
18695     # Extrait de la clef le nom de l'appli
18696     set len_unit [string length $nom_unit]
18697     incr len_unit
18698     set nom_appli [string range $clef_unit 0 end-$len_unit]
18699     
18700     # Si l'unité est un fichier 
18701     if { $nom_appli == "///file///" } {
18702         # Supprime le fichier
18703         NetCDF_file_delete $clef_unit
18704     }
18705 }
18706 
18707 ret  {unit_edit} (type clef_, type unit) {
18708 # Quand l'utilisateur double-clique sur une unité (composant ou fichier) dans le graphe
18709 #   ceci provoque la saisie des paramètres de cette unité
18710 #
18711 # Cette fonction appelle la procédure qu'il faut selon que l'unité en question 
18712 # est un fichier ou un composant d'application
18713 #
18714     global dico_applis dico_units dico_caract_fichiers
18715     global oldcolor tagselected
18716 
18717     set nom_unit $dico_units($clef_unit)
18718     # Extrait de la clef le nom de l'appli
18719     set len_unit [string length $nom_unit]
18720     incr len_unit
18721     set nom_appli [string range $clef_unit 0 end-$len_unit]
18722     
18723     # Si l'unité est un fichier 
18724     if { $nom_appli == "///file///" } {
18725         NetCDF_file_edit $nom_unit
18726         tkwait window .mod_file
18727         
18728         # L'utilisateur a pu changer le nom du fichier
18729         # ==> raffiche le fichier sur le graphe
18730         Draw_one_unit $clef_unit
18731         Draw_comm $clef_unit
18732 
18733     } else {
18734         component_edit $clef_unit
18735         tkwait window .mod_component
18736         # La couleur a pu changer : raffiche le composant sur le graphe
18737         Draw_one_unit $clef_unit
18738     }
18739 
18740     # Si cette unité était sélectionnée
18741     if {$tagselected == "unitpoint_color($clef_unit)"} {
18742         # Change la couleur de l'objet sélectionné
18743         set w .gui.pr.cpd22.03
18744         set oldcolor [$w itemcget $tagselected  -fill ]
18745         $w itemconfigure $tagselected -fill red
18746     }
18747 }
18748 
18749 ret  {unit_init} () {
18750 # Initialise arbitrairement les données graphiques des unités sur le graphe de couplage
18751     global dico_applis liste_fichiers dico_units 
18752 
18753     # Position de la 1ère unité sur le graphe : en haut à gauche
18754     # Les autres seront alignés sur une diagonale a travers l'écran
18755     set position_x 80
18756     set position_y 25
18757             
18758     # Pour toutes les applications
18759     foreach nom_appli $dico_applis(lapplis) {
18760         # Pour tous les composants de cette application
18761         foreach nom_composant $dico_applis(app.$nom_appli.lcomp) {
18762             set clef_comp $nom_appli.$nom_composant
18763             
18764             # Si l'élément apparait sur le graphe
18765             if { [info exists dico_units($clef_comp)] } {
18766                 # Position en x et y, couleur et mode d'affichage
18767                 set dico_units($clef_comp.coor_x) $position_x
18768                 set dico_units($clef_comp.coor_y) $position_y
18769                 set dico_units($clef_comp.colour) [color_rgb [expr int(rand()*254)] [expr int(rand()*254)] [expr int(rand()*254)]]
18770                 set dico_units($clef_comp.expand) 1
18771                 incr position_x 30
18772                 incr position_y 80
18773             }
18774         }
18775     }
18776     
18777     # Pour tous les fichiers NetCDF
18778     foreach id_fichier $liste_fichiers {
18779         set clef_fichier ///file///.$id_fichier
18780         
18781         # Si l'élément apparait sur le graphe
18782         if { [info exists dico_units($clef_fichier)] } {
18783             # Position en x et y, couleur et mode d'affichage
18784             set dico_units($clef_fichier.coor_x) $position_x
18785             set dico_units($clef_fichier.coor_y) $position_y
18786             set dico_units($clef_fichier.colour) [color_rgb [expr int(rand()*254)] [expr int(rand()*254)] [expr int(rand()*254)]]
18787             set dico_units($clef_fichier.expand) 1
18788             incr position_x 30
18789             incr position_y 80
18790         }
18791     }
18792 }
18793 
18794 ret  {unitplotdown} (type w , type x , type y , type i) {
18795 # plotDown --
18796 # This procedure is invoked when the mouse is pressed over one of the
18797 # data points.  It sets up state to allow the point to be dragged.
18798 #
18799 # Arguments:
18800 # w -       The canvas window.
18801 # x, y -    The coordinates of the mouse press.
18802     global plot
18803     global unitindex
18804     global plotdown
18805 
18806     set plotdown "active"
18807     $w dtag selected
18808     $w addtag selected withtag UNIT_tag($i)
18809     set plot(lastX) $x
18810     set plot(lastY) $y
18811     set plot(firstX) $x
18812     set plot(firstY) $y
18813 }
18814 
18815 ret  {unitplotmove} (type w , type x , type y) {
18816 # plotMove --
18817 # This procedure is invoked during mouse motion events.  It drags the
18818 # current item.
18819 #
18820 # Arguments:
18821 # w -       The canvas window.
18822 # x, y -    The coordinates of the mouse.
18823     global plot
18824     $w move selected [expr $x-$plot(lastX)] [expr $y-$plot(lastY)]
18825     set plot(lastX) $x
18826     set plot(lastY) $y
18827 }
18828 
18829 ret  {unitplotrelease} (type w , type x , type y , type i) {
18830 #
18831     global zoomfactor
18832     global dico_units
18833     global plot
18834     global plotdown
18835     global tagselected
18836 
18837     if {$plotdown == "active"} {
18838         $w dtag selected
18839         set plot(lastX) $x
18840         set plot(lastY) $y
18841         set dico_units($i.coor_x) [expr $dico_units($i.coor_x) + ($plot(lastX) - $plot(firstX))/$zoomfactor]
18842         set dico_units($i.coor_y) [expr $dico_units($i.coor_y) + ($plot(lastY) - $plot(firstY))/$zoomfactor]
18843         
18844         Draw_one_unit $i
18845         # Si le composant est sélectionné
18846         if {$tagselected == "unitpoint_color($i)"} {
18847             # remet la couleur rouge de l'objet sélectionné 
18848             $w itemconfigure $tagselected -fill red
18849         }
18850         update
18851         Draw_comm $i
18852         set plotdown "inactive"
18853     }
18854 }
18855 
18856 ret  {user_name} () {
18857 global env
18858    return $env(USER)
18859 }
18860 
18861 ret  {window.font} (optional init_font ={helvetica 12) texttoedit} {
18862 global dxf
18863     .gui configure -cursor watch
18864     update
18865     set dxf(tmp) ""
18866     catch {destroy .choose_font}
18867      w =  .choose_font
18868     toplevel $w
18869     wm withdraw $w
18870     wm protocol $w WM_DELETE_WINDOW " dxf = (tmp) \"\"; destroy $w"
18871     catch {wm transient $w .appearance}
18872     wm title $w "Text edit"
18873 
18874 
18875     label $w.l11 -text "Font:"
18876     label $w.l12 -text "Size:"
18877     label $w.l13 -text "Color:"
18878     label $w.l14 -text "Justification:"
18879 
18880 
18881     combobox_lim $w.cb_name [lsort [font families]] font_view_update dxf(choose_font_cb_name) 30 $dxf(choose_font_cb_name)
18882 /*     bind $w.cb_name.e <Return> "font_view_update"*/
18883 
18884     combobox_lim $w.cb_size [list 8 10 12 14 16 18 20 24 36] font_view_update dxf(choose_font_cb_size) 4 $dxf(choose_font_cb_size)
18885 /*     bind $w.cb_size.e <Return> "font_view_update"*/
18886 
18887     combobox_lim $w.cb_color [list black red blue green cyan magenta yellow pink grey20 grey50 grey80] font_view_update dxf(choose_font_cb_color) 10 $dxf(choose_font_cb_color)
18888 /*     bind $w.cb_color.e <Return> "font_view_update"*/
18889 
18890     combobox_lim $w.cb_justify [list left right center] font_view_update dxf(choose_font_cb_justify) 10 $dxf(choose_font_cb_justify)
18891 /*     bind $w.cb_justify.e <Return> "font_view_update"*/
18892 
18893     checkbutton $w.c_bold -highlightthickness 0 -indicatoron 0 -text "B" -font {Times 12 bold} -onvalue bold -offvalue ""  -command "font_view_update" -variable dxf(choose_font_c_bold)
18894     checkbutton $w.c_italic -highlightthickness 0 -indicatoron 0 -text "I" -font {Times 12 italic} -onvalue italic -offvalue ""  -command "font_view_update" -variable dxf(choose_font_c_italic)
18895     checkbutton $w.c_underline -highlightthickness 0 -indicatoron 0 -text "U" -font {Times 12 underline} -onvalue underline -offvalue ""  -command "font_view_update" -variable dxf(choose_font_c_underline)
18896 
18897     text $w.view  -font "[get_cur_font]" -width 20 -height 5 -background white
18898 
18899     frame $w.f
18900     button $w.f.ok -text "Ok" -command " dxf = (text) \"\[.choose_font.view get 0.0 end\]\" ; dxf = (tmp) \"\[get_cur_font\]\"; destroy $w" 
18901     button $w.f.cancel -text "Close" -command " dxf = (tmp) \"\"; destroy $w"   
18902 
18903 
18904    grid $w.l11 $w.l12 $w.l13 $w.l14  -row 0 -pady 2 -padx 4 -sticky w
18905    grid $w.cb_name $w.cb_size $w.cb_color $w.cb_justify $w.c_bold $w.c_italic $w.c_underline -row 1 -padx 4 -sticky w
18906    grid $w.view -row 2 -columnspan 7 -sticky we -pady 4
18907    grid $w.f -row 3 -columnspan 7 -sticky we -pady 5 -padx 5
18908    grid $w.f.ok $w.f.cancel -row 0 -padx 10
18909 
18910    focus $w.view
18911 
18912    wm geometry $w +150+150
18913     .gui configure -cursor ""
18914 }
18915 
18916 ret  {window_position} (type w , optional x =100 , optional y =100) {
18917 #
18918     set x [expr [winfo rootx .gui]+$x]
18919     set y [expr [winfo rooty .gui]+$y]
18920     wm geometry $w "+$x+$y"
18921 }
18922 
18923 ret  {write_file_as} () {
18924 set types { { { XML Files} {.xml} } }
18925     set file [tk_getSaveFile -initialfile "scc.xml" -title "Save configuration file" -filetypes $types -defaultextension .xml ]
18926 
18927     if {$file != ""} {
18928         if {[file extension $file] != ".xml"} {
18929             set file  $file.xml
18930         }
18931     }
18932 
18933     return $file
18934 }
18935 
18936 ret  {main} (type argc , type argv) {
18937 #
18938     global largfen
18939     global entity_id
18940     global OASIS_GUI_DIR
18941     global entityselected
18942 
18943     # Load "XML support" packages : they are in subdirectory 'packages'
18944     package require xml
18945     package require xml::libxml2
18946     package require dom
18947     
18948     global env
18949     global file_lue
18950     set file_lue ""
18951     global DRAW
18952     set DRAW(unitXcur) 20
18953     set DRAW(unitYcur) 50
18954     
18955     wm title .gui "OASIS"
18956     if {$argc > 0} {
18957         set file [lindex $argv 0]
18958 
18959         #
18960         #** Lecture du fichier .XML
18961         #
18962         if {[file exists $file]} {
18963             .gui configure -cursor watch
18964             update
18965             set erreur [lit_fichier_SCC $file]
18966             .gui configure -cursor ""
18967             
18968             if {! $erreur } {
18969                 set file_lue $file
18970                 wm title .gui "OASIS - $file"
18971             
18972                 global entityselected
18973                 control_entity $entityselected
18974                 drawall
18975             } else {
18976                 # Efface les traces d'une config qui serait lue de façon incomplète
18977                 config_RAZ
18978             }
18979         }
18980     }
18981 
18982     #==recupere la derniere taille de la fenetre
18983     catch {
18984         set f [open_file ~/.OASIS/gui_oasis_window r]
18985         gets $f geom
18986         wm geometry .gui $geom
18987         close $f
18988     }
18989 
18990     wm protocol .gui WM_DELETE_WINDOW "quit_gui_oasis"
18991     drawall
18992     catch {destroy .start_banner}
18993 
18994     set largfen 37
18995     control_entity $entityselected
18996 
18997     #===creation des images
18998     
18999     # Symboles des tranformations
19000     foreach transform {add multiply remaillage accumulate average interpol} {
19001         image create photo img_$transform -file [file join $OASIS_GUI_DIR IMAGES $transform.gif]
19002     }
19003 
19004     # Boutons
19005     set imag [image create photo  -file [file join $OASIS_GUI_DIR IMAGES open.gif]]
19006     .gui.pr.toolframe.toolsbox.fobt configure -image $imag
19007     bind .gui.pr.toolframe.toolsbox.fobt <Any-Enter> ".gui.pr.tools.infolabel configure -text {Open SCC file}"
19008     bind .gui.pr.toolframe.toolsbox.fobt <Any-Leave> ".gui.pr.tools.infolabel configure -text {}"
19009 
19010     set imag [image create photo  -file [file join $OASIS_GUI_DIR IMAGES save.gif]]
19011     .gui.pr.toolframe.toolsbox.but32 configure -image $imag
19012     bind .gui.pr.toolframe.toolsbox.but32 <Any-Enter> ".gui.pr.tools.infolabel configure -text {Save all (SCC and SMIOC)}"
19013     bind .gui.pr.toolframe.toolsbox.but32 <Any-Leave> ".gui.pr.tools.infolabel configure -text {}"
19014 
19015     set imag [image create photo  -file [file join $OASIS_GUI_DIR IMAGES zoommoins.gif]]
19016     .gui.pr.zoomframe.zm configure -image $imag
19017     bind .gui.pr.zoomframe.zm <Any-Enter> ".gui.pr.tools.infolabel configure -text {Zoom -}"
19018     bind .gui.pr.zoomframe.zm <Any-Leave> ".gui.pr.tools.infolabel configure -text {}"
19019 
19020     set imag [image create photo  -file [file join $OASIS_GUI_DIR IMAGES zoomplus.gif]]
19021     .gui.pr.zoomframe.zp configure -image $imag
19022     bind .gui.pr.zoomframe.zp <Any-Enter> ".gui.pr.tools.infolabel configure -text {Zoom +}"
19023     bind .gui.pr.zoomframe.zp <Any-Leave> ".gui.pr.tools.infolabel configure -text {}"
19024 
19025     set imag [image create photo  -file [file join $OASIS_GUI_DIR IMAGES lunit.gif]]
19026     .gui.pr.toolframe.toolsbox.but34 configure -image $imag
19027     bind .gui.pr.toolframe.toolsbox.but34 <Any-Enter> ".gui.pr.tools.infolabel configure -text {Load XML application definition}"
19028     bind .gui.pr.toolframe.toolsbox.but34 <Any-Leave> ".gui.pr.tools.infolabel configure -text {}"
19029 
19030     set imag [image create photo  -file [file join $OASIS_GUI_DIR IMAGES binary.gif]]
19031     .gui.pr.toolframe.toolsbox.but35 configure -image $imag
19032     bind .gui.pr.toolframe.toolsbox.but35 <Any-Enter> ".gui.pr.tools.infolabel configure -text {Load NetCDF file}"
19033     bind .gui.pr.toolframe.toolsbox.but35 <Any-Leave> ".gui.pr.tools.infolabel configure -text {}"
19034 }
19035 
19036 ret  init (type argc , type argv) {
19037 #initialisations
19038     combobox_init
19039     #** chemins
19040     global OASIS_GUI_DIR
19041     global env
19042     global zoomfactor
19043     set zoomfactor 1
19044 
19045     global plotdown
19046     set plotdown inactive
19047     
19048     # Determine le chemin du script en cours d'execution
19049     global argv0
19050     set script_file [file normalize [file join [pwd]  $argv0]]
19051     # Prend le repertoire
19052     set OASIS_GUI_DIR [file dirname $script_file]
19053 
19054     #LAURE
19055     # packages : they are in subdirectory 'packages/lib'
19056     global auto_path
19057     lappend auto_path [file join $OASIS_GUI_DIR packages/lib]
19058     # Lit les paramètres de la ligne de commande
19059     package require cmdline
19060     set options {
19061         {silent          "silent mode : no help balloon"}
19062     }
19063     set usage ": oasis-gui.tcl \[options] \noptions:"
19064     array set params [::cmdline::getoptions argv $options $usage]
19065     # Vérifie si l'option -silent a été mise sur la ligne de commande
19066     global no_help_balloon
19067     set no_help_balloon $params(silent)
19068     
19069     global plot
19070     set plot(lastX) 0
19071     set plot(lastY) 0
19072     
19073     global oasis_GUI_param
19074     
19075     global global_font
19076     set global_font "-adobe-helvetica-bold-r-normal--12-180-90-90-m-90-iso8859-1"
19077     
19078     global viewcommcheck
19079     set viewcommcheck 1
19080     global oasis4_version; set oasis4_version OASIS4_0_2
19081     
19082     global dico_applis
19083     array set dico_applis {}
19084     global liste_points_i liste_points_o
19085     set liste_points_i {}
19086     set liste_points_o {}
19087     global dico_units
19088     array set dico_units {}
19089     global liste_fichiers dico_caract_fichiers
19090     set liste_fichiers {}
19091     array set dico_caract_fichiers {}
19092     
19093     global liste_cnx_in
19094     set liste_cnx_in {}
19095     global dico_cnx_out
19096     array set dico_cnx_out {}
19097 
19098     #** default order in selection list
19099     global orderentity
19100     set orderentity(CONNECTION)    1
19101     set orderentity(COUPLING_FIELD)        3
19102     set orderentity(COMPONENT)    0
19103     set orderentity(FILE)    0
19104     
19105     global objectselected tagselected;    set objectselected ""; set tagselected ""
19106     
19107     global entityselected
19108     set entityselected COMPONENT
19109 }
19110 
19111 init $argc $argv
19112 
19113 /* */
19114 /*  VTCL GENERATED GUI PROCEDURES*/
19115 /* */
19116 
19117 ret  vTclWindow. (type base , optional container =0) {
19118     if {$base == ""} {
19119         set base .
19120     }
19121     ###################
19122     # CREATING WIDGETS
19123     ###################
19124     if {!$container} {
19125     wm focusmodel $base passive
19126     wm geometry $base 1x1+0+0; update
19127     wm maxsize $base 1009 738
19128     wm minsize $base 1 1
19129     wm overrideredirect $base 0
19130     wm resizable $base 1 1
19131     wm withdraw $base
19132     wm title $base "vtcl.tcl"
19133     bindtags $base "$base Vtcl.tcl all"
19134     }
19135     ###################
19136     # SETTING GEOMETRY
19137     ###################
19138 }
19139 
19140 ret  vTclWindow.ballon_object (type base , optional container =0) {
19141     if {$base == ""} {
19142         set base .ballon_object
19143     }
19144     if {[winfo exists $base] && (!$container)} {
19145         wm deiconify $base; return
19146     }
19147 
19148     global widget
19149 
19150     ###################
19151     # CREATING WIDGETS
19152     ###################
19153     if {!$container} {
19154     toplevel $base -class Toplevel \
19155         -borderwidth 1 
19156     wm withdraw .ballon_object
19157     wm focusmodel $base passive
19158     wm geometry $base 1x1+0+0; update
19159     wm maxsize $base 1009 738
19160     wm minsize $base 1 1
19161     wm overrideredirect $base 1
19162     wm resizable $base 1 1
19163     wm title $base "ballon_object"
19164     }
19165     label $base.text \
19166         -background #fefed4 -font {helvetica 10} -foreground black 
19167     ###################
19168     # SETTING GEOMETRY
19169     ###################
19170     pack $base.text \
19171         -in $base -anchor w -expand 0 -fill none -side left 
19172 }
19173 
19174 ret  vTclWindow.gui (type base , optional container =0) {
19175     if {$base == ""} {
19176         set base .gui
19177     }
19178     if {[winfo exists $base] && (!$container)} {
19179         wm deiconify $base; return
19180     }
19181 
19182     global widget
19183     vTcl:DefineAlias "$base.cpd17.settingbutton" "Menubutton1" vTcl:WidgetProc "$base" 1
19184     vTcl:DefineAlias "$base.pr.toolframe.cpd18.01" "Mclistbox1" vTcl:WidgetProc "$base" 1
19185     vTcl:DefineAlias "$base.pr.toolframe.toolsbox" "Frame1" vTcl:WidgetProc "$base" 1
19186     vTcl:DefineAlias "$base.pr.toolframe.toolsbox.but32" "Button2" vTcl:WidgetProc "$base" 1
19187     vTcl:DefineAlias "$base.pr.toolframe.toolsbox.but34" "Button16" vTcl:WidgetProc "$base" 1
19188     vTcl:DefineAlias "$base.pr.toolframe.toolsbox.but35" "Button17" vTcl:WidgetProc "$base" 1
19189     vTcl:DefineAlias "$base.pr.toolframe.toolsbox.fobt" "Button1" vTcl:WidgetProc "$base" 1
19190 
19191     ###################
19192     # CREATING WIDGETS
19193     ###################
19194     if {!$container} {
19195     toplevel $base -class Toplevel
19196     wm focusmodel $base passive
19197     wm geometry $base 1024x695+0+48; update
19198     wm maxsize $base 1600 1200
19199     wm minsize $base 400 300
19200     wm overrideredirect $base 0
19201     wm resizable $base 1 1
19202     wm deiconify $base
19203     wm title $base "OASIS"
19204     bind $base <Button-4> {
19205         catch {%W yview scroll -1 units}
19206     }
19207     bind $base <Button-5> {
19208         catch {%W yview scroll 1 units}
19209     }
19210     bind $base <Control-Button-4> {
19211         catch {%W xview scroll -1 units}
19212     }
19213     bind $base <Control-Button-5> {
19214         catch {%W xview scroll 1 units}
19215     }
19216     bind $base <Shift-Button-4> {
19217         catch {%W yview scroll -5 units}
19218     }
19219     bind $base <Shift-Button-5> {
19220         catch {%W yview scroll 5 units}
19221     }
19222     }
19223     frame $base.cpd17 \
19224         -borderwidth 1 -relief sunken -background #e6e6e6 -height 30 \
19225         -width 30 
19226     menubutton $base.cpd17.01 \
19227         -anchor w -borderwidth 2 -font {helvetica -12 bold} \
19228         -menu "$base.cpd17.01.02" -padx 4 -pady 3 -text File -underline 0 \
19229         -width 4 
19230     bind $base.cpd17.01 <Enter> {
19231         .gui.pr.tools.infolabel configure -text "Open, save and print"
19232     }
19233     bind $base.cpd17.01 <Leave> {
19234         .gui.pr.tools.infolabel configure -text " "
19235     }
19236     menu $base.cpd17.01.02 \
19237         -cursor {} -tearoff 0 
19238     $base.cpd17.01.02 add command \
19239         -accelerator {} -command config_new -image {} -label {New file} 
19240     $base.cpd17.01.02 add command \
19241         -accelerator {} -command config_read -image {} -label {Open SCC file} 
19242     $base.cpd17.01.02 add command \
19243         -accelerator {} -command sauve_tout -image {} -label {Save all (SCC and SMIOC)} 
19244     $base.cpd17.01.02 add command \
19245         -accelerator {} -command sauve_scc -image {} -label {Save SCC file} 
19246     $base.cpd17.01.02 add command \
19247         -accelerator {} -command sauve_smioc -image {} -label {Save SMIOC files}
19248     $base.cpd17.01.02 add separator
19249     $base.cpd17.01.02 add command \
19250         -accelerator {} -command load_XML_ad_files -image {} \
19251         -label {Load Application Descriptions} 
19252     $base.cpd17.01.02 add command \
19253         -accelerator {} -command load_NetCDF_files -image {} \
19254         -label {Load NetCDF files} 
19255     $base.cpd17.01.02 add separator
19256     $base.cpd17.01.02 add command \
19257         -accelerator {} \
19258         -command {printer_create .print "Oasis"
19259 printer_print .print draw_print} \
19260         -image {} -label Print 
19261     $base.cpd17.01.02 add separator
19262     $base.cpd17.01.02 add command \
19263         -accelerator {} -command quit_gui_oasis -image {} -label {Quit Oasis} 
19264     menubutton $base.cpd17.settingbutton \
19265         -font {helvetica -12 bold} -menu "$base.cpd17.settingbutton.m" \
19266         -padx 6 -pady 4 -text Settings -underline 0 
19267     bind $base.cpd17.settingbutton <Enter> {
19268         .gui.pr.tools.infolabel configure -text "Set display options, colors"
19269     }
19270     bind $base.cpd17.settingbutton <Leave> {
19271         .gui.pr.tools.infolabel configure -text ""
19272     }
19273     menu $base.cpd17.settingbutton.m
19274     $base.cpd17.settingbutton.m add command \
19275         -accelerator {} -command experiment_settings -image {} \
19276         -label {Experiment settings ...} 
19277     $base.cpd17.settingbutton.m add command \
19278         -accelerator {} -command Draw_settings -image {} \
19279         -label {Canvas settings ...} 
19280     frame $base.pr \
19281         -borderwidth 2 -relief groove -height 75 -width 200 
19282     frame $base.pr.tools \
19283         -height 4 -width 300 
19284     bind $base.pr.tools <Configure> {
19285         set l [winfo width .gui]
19286 set l [expr ($l-10) / 7.17]
19287 .gui.pr.tools.resizescrl configure -to $l
19288     }
19289     label $base.pr.tools.infolabel \
19290         -anchor w -background grey95 -borderwidth 1 \
19291         -font {helvetica -12 bold} -relief sunken -text {} 
19292     scale $base.pr.tools.resizescrl \
19293         -bigincrement 0.0 -borderwidth 0 \
19294         -command {.gui.pr.toolframe.cpd18.01 configure -width} -from 1.0 \
19295         -highlightthickness 0 -orient horizontal -relief ridge \
19296         -resolution 1.0 -showvalue 0 -sliderlength 10 -tickinterval 0.0 \
19297         -to 141.0 -troughcolor #d9d9d9 -variable largfen -width 9 
19298     frame $base.pr.toolframe \
19299         -borderwidth 0 -relief sunken -height 1063 -width 125 
19300     frame $base.pr.toolframe.toolsbox \
19301         -relief groove -background #e6e6e6 -height 75 \
19302         -highlightbackground #e6e6e6 -highlightcolor #000000 -width 125 
19303     button $base.pr.toolframe.toolsbox.fobt \
19304         -borderwidth 2 -command config_read -text l 
19305     bind $base.pr.toolframe.toolsbox.fobt <Enter> {
19306         .gui.pr.tools.infolabel configure -text {Read PrePalm file}
19307     }
19308     bind $base.pr.toolframe.toolsbox.fobt <Leave> {
19309         .gui.pr.tools.infolabel configure -text {}
19310     }
19311     button $base.pr.toolframe.toolsbox.but34 \
19312         -command load_XML_ad_files -padx 3m -text u 
19313     bind $base.pr.toolframe.toolsbox.but34 <Enter> {
19314         .gui.pr.tools.infolabel configure -text {Load XML application definition}
19315     }
19316     bind $base.pr.toolframe.toolsbox.but34 <Leave> {
19317         .gui.pr.tools.infolabel configure -text {}
19318     }
19319     button $base.pr.toolframe.toolsbox.but35 \
19320         -command load_NetCDF_files -padx 3m -text u 
19321     bind $base.pr.toolframe.toolsbox.but35 <Enter> {
19322         .gui.pr.tools.infolabel configure -text {Load NetCDF file}
19323     }
19324     bind $base.pr.toolframe.toolsbox.but35 <Leave> {
19325         .gui.pr.tools.infolabel configure -text {}
19326     }
19327     button $base.pr.toolframe.toolsbox.but32 \
19328         -anchor center -borderwidth 2 -command sauve_tout
19329     bind $base.pr.toolframe.toolsbox.but32 <Enter> {
19330         .gui.pr.tools.infolabel configure -text {Save all (SCC and SMIOC)}
19331     }
19332     bind $base.pr.toolframe.toolsbox.but32 <Leave> {
19333         .gui.pr.tools.infolabel configure -text {}
19334     }
19335     frame $base.pr.toolframe.entityframe \
19336         -borderwidth 2 -height 75 -width 177 
19337     radiobutton $base.pr.toolframe.entityframe.comprb \
19338         -anchor w -borderwidth 2 -command {control_entity COMPONENT} \
19339         -font {helvetica -12 bold} -highlightthickness 0 -padx 1 -pady 3 \
19340         -selectcolor #b03060 -text {Applications and components } -value 0 \
19341         -variable entity_id -width 25 
19342     radiobutton $base.pr.toolframe.entityframe.ficrb \
19343         -anchor w -command {control_entity FILE} -font {helvetica -12 bold} \
19344         -highlightthickness 0 -padx 1 -pady 3 -text {NetCDF files } -value 3 \
19345         -variable entity_id 
19346     radiobutton $base.pr.toolframe.entityframe.cnxrb \
19347         -anchor w -command {control_entity CONNECTION} \
19348         -font {helvetica -12 bold} -highlightthickness 0 -padx 1 -pady 3 \
19349         -text Connections -value 2 -variable entity_id 
19350     radiobutton $base.pr.toolframe.entityframe.fieldsrb \
19351         -anchor w -command {control_entity COUPLING_FIELD} \
19352         -font {helvetica -12 bold} -highlightthickness 0 -padx 1 -pady 3 \
19353         -text {Coupling fields} -value 1 -variable entity_id -width 27 
19354     frame $base.pr.toolframe.buttonfrc \
19355         -height 75 -width 4 
19356     button $base.pr.toolframe.buttonfrc.editb \
19357         -command control_edit_entity -font {helvetica -12 bold} -padx 5 \
19358         -pady 4 -text Edit 
19359     button $base.pr.toolframe.buttonfrc.deleteb \
19360         -command control_delete_entity -font {helvetica -12 bold} -padx 5 \
19361         -pady 4 -state disabled -text Delete 
19362     frame $base.pr.toolframe.cpd18 \
19363         -borderwidth 0 -relief raised -height 2 -width 31 
19364     scrollbar $base.pr.toolframe.cpd18.02 \
19365         -borderwidth 1 -command "$base.pr.toolframe.cpd18.01 xview" \
19366         -orient horizontal -width 10 
19367     scrollbar $base.pr.toolframe.cpd18.03 \
19368         -borderwidth 1 -command "$base.pr.toolframe.cpd18.01 yview" -width 11 
19369     ::mclistbox::mclistbox $base.pr.toolframe.cpd18.01 \
19370         -background #d9d9d9 -borderwidth 2 -font {helvetica -12 bold} \
19371         -height 278 -labelanchor center -labelborderwidth 1 \
19372         -labelrelief raised -relief sunken -selectborderwidth 1 \
19373         -selectmode extended -width 37 \
19374         -xscrollcommand "$base.pr.toolframe.cpd18.02 set" \
19375         -yscrollcommand "$base.pr.toolframe.cpd18.03 set" 
19376     bind $base.pr.toolframe.cpd18.01 <<ListboxSelect>> {
19377         control_entity_selectionChanged %W
19378     }
19379     bind $base.pr.toolframe.cpd18.01 <Key-Delete> {control_delete_entity}
19380     bind $base.pr.toolframe.cpd18.01 <Double-Button-1> {
19381         control_edit_entity
19382     }
19383     frame $base.pr.zoomframe \
19384         -relief groove -height 75 -width 125 
19385     frame $base.pr.zoomframe.viewframe \
19386         -borderwidth 0 -relief groove -height 75 -width 208 
19387     checkbutton $base.pr.zoomframe.viewframe.viewcommcheck \
19388         -command {update
19389 Draw_comm} -font {helvetica -12 bold} -pady 0 \
19390         -text {View connections} -variable viewcommcheck 
19391     button $base.pr.zoomframe.zm \
19392         -borderwidth 0 \
19393         -command {global zoomfactor
19394 if {$zoomfactor > .4} {set zoomfactor [expr $zoomfactor-0.1]}
19395 drawall} \
19396         -padx 0 -pady 0 -relief flat -text button 
19397     bind $base.pr.zoomframe.zm <Enter> {
19398         .gui.pr.tools.infolabel configure -text {Zoom -}
19399     }
19400     bind $base.pr.zoomframe.zm <Leave> {
19401         .gui.pr.tools.infolabel configure -text {}
19402     }
19403     button $base.pr.zoomframe.zp \
19404         -borderwidth 0 \
19405         -command {global zoomfactor
19406 if {$zoomfactor < 1.5} {set zoomfactor [expr $zoomfactor +0.1]}
19407 drawall} \
19408         -padx 0 -pady 0 -relief flat -text button 
19409     bind $base.pr.zoomframe.zp <Enter> {
19410         .gui.pr.tools.infolabel configure -text {Zoom +}
19411     }
19412     bind $base.pr.zoomframe.zp <Leave> {
19413         .gui.pr.tools.infolabel configure -text {}
19414     }
19415     frame $base.pr.cpd22 \
19416         -borderwidth 0 -relief raised -height 30 -width 30 
19417     scrollbar $base.pr.cpd22.01 \
19418         -borderwidth 1 -command "$base.pr.cpd22.03 xview" -jump 1 \
19419         -orient horizontal -width 10 
19420     scrollbar $base.pr.cpd22.02 \
19421         -borderwidth 1 -command "$base.pr.cpd22.03 yview" -jump 1 -width 10 
19422     canvas $base.pr.cpd22.03 \
19423         -background #fefefe -borderwidth 1 -closeenough 1.0 -height 999 \
19424         -relief ridge -scrollregion {0 0 2000 3000} -width 2000 \
19425         -xscrollcommand "$base.pr.cpd22.01 set" \
19426         -yscrollcommand "$base.pr.cpd22.02 set" 
19427     bind $base.pr.cpd22.03 <B1-Motion> {
19428         unitplotmove .gui.pr.cpd22.03 %x %y
19429     }
19430     bind $base.pr.cpd22.03 <B3-Motion> {
19431         unitplotmove .gui.pr.cpd22.03 %x %y
19432     }
19433     ###################
19434     # SETTING GEOMETRY
19435     ###################
19436     pack $base.cpd17 \
19437         -in $base -anchor center -expand 0 -fill x -side top 
19438     pack $base.cpd17.01 \
19439         -in $base.cpd17 -anchor center -expand 0 -fill none -side left 
19440     pack $base.cpd17.settingbutton \
19441         -in $base.cpd17 -anchor center -expand 0 -fill none -side left 
19442     pack $base.pr \
19443         -in $base -anchor center -expand 1 -fill x -side right 
19444     pack $base.pr.tools \
19445         -in $base.pr -anchor n -expand 0 -fill x -side bottom 
19446     pack $base.pr.tools.infolabel \
19447         -in $base.pr.tools -anchor w -expand 0 -fill x -ipadx 15 -side bottom 
19448     pack $base.pr.tools.resizescrl \
19449         -in $base.pr.tools -anchor w -expand 1 -fill x -side left 
19450     pack $base.pr.toolframe \
19451         -in $base.pr -anchor n -expand 0 -fill both -side left 
19452     pack $base.pr.toolframe.toolsbox \
19453         -in $base.pr.toolframe -anchor center -expand 0 -fill x -side top 
19454     pack $base.pr.toolframe.toolsbox.fobt \
19455         -in $base.pr.toolframe.toolsbox -anchor center -expand 0 -fill none \
19456         -ipadx 2 -ipady 2 -side left 
19457     pack $base.pr.toolframe.toolsbox.but34 \
19458         -in $base.pr.toolframe.toolsbox -anchor center -expand 0 -fill none \
19459         -side left 
19460     pack $base.pr.toolframe.toolsbox.but35 \
19461         -in $base.pr.toolframe.toolsbox -anchor center -expand 0 -fill none \
19462         -side left 
19463     pack $base.pr.toolframe.toolsbox.but32 \
19464         -in $base.pr.toolframe.toolsbox -anchor s -expand 0 -fill none \
19465         -ipadx 2 -ipady 2 -side left 
19466     pack $base.pr.toolframe.entityframe \
19467         -in $base.pr.toolframe -anchor nw -expand 1 -fill none -padx 5 \
19468         -pady 5 -side top 
19469     grid $base.pr.toolframe.entityframe.comprb \
19470         -in $base.pr.toolframe.entityframe -column 0 -row 0 -columnspan 1 \
19471         -rowspan 1 -sticky nesw 
19472     grid $base.pr.toolframe.entityframe.ficrb \
19473         -in $base.pr.toolframe.entityframe -column 0 -row 1 -columnspan 1 \
19474         -rowspan 1 -sticky nesw 
19475     grid $base.pr.toolframe.entityframe.cnxrb \
19476         -in $base.pr.toolframe.entityframe -column 0 -row 3 -columnspan 1 \
19477         -rowspan 1 -sticky nesw 
19478     grid $base.pr.toolframe.entityframe.fieldsrb \
19479         -in $base.pr.toolframe.entityframe -column 0 -row 2 -columnspan 1 \
19480         -rowspan 1 -sticky nesw 
19481     pack $base.pr.toolframe.buttonfrc \
19482         -in $base.pr.toolframe -anchor n -expand 1 -fill x -side top 
19483     pack $base.pr.toolframe.buttonfrc.editb \
19484         -in $base.pr.toolframe.buttonfrc -anchor center -expand 1 -fill x \
19485         -side left 
19486     pack $base.pr.toolframe.buttonfrc.deleteb \
19487         -in $base.pr.toolframe.buttonfrc -anchor center -expand 1 -fill x \
19488         -side left 
19489     pack $base.pr.toolframe.cpd18 \
19490         -in $base.pr.toolframe -anchor w -expand 0 -fill none -side top 
19491     grid columnconf $base.pr.toolframe.cpd18 0 -weight 1
19492     grid rowconf $base.pr.toolframe.cpd18 0 -weight 1
19493     grid $base.pr.toolframe.cpd18.02 \
19494         -in $base.pr.toolframe.cpd18 -column 0 -row 1 -columnspan 1 \
19495         -rowspan 1 -sticky ew 
19496     grid $base.pr.toolframe.cpd18.03 \
19497         -in $base.pr.toolframe.cpd18 -column 1 -row 0 -columnspan 1 \
19498         -rowspan 1 -sticky ns 
19499     grid $base.pr.toolframe.cpd18.01 \
19500         -in $base.pr.toolframe.cpd18 -column 0 -row 0 -columnspan 1 \
19501         -rowspan 1 
19502     pack $base.pr.zoomframe \
19503         -in $base.pr -anchor center -expand 0 -fill x -side top 
19504     pack $base.pr.zoomframe.viewframe \
19505         -in $base.pr.zoomframe -anchor center -expand 0 -fill none \
19506         -side right 
19507     pack $base.pr.zoomframe.viewframe.viewcommcheck \
19508         -in $base.pr.zoomframe.viewframe -anchor center -expand 0 -fill none \
19509         -ipadx 6 -side left 
19510     pack $base.pr.zoomframe.zm \
19511         -in $base.pr.zoomframe -anchor center -expand 0 -fill none -side left 
19512     pack $base.pr.zoomframe.zp \
19513         -in $base.pr.zoomframe -anchor center -expand 0 -fill none -side left 
19514     pack $base.pr.cpd22 \
19515         -in $base.pr -anchor center -expand 0 -fill none -side left 
19516     grid columnconf $base.pr.cpd22 0 -weight 1
19517     grid rowconf $base.pr.cpd22 0 -weight 1
19518     grid $base.pr.cpd22.01 \
19519         -in $base.pr.cpd22 -column 0 -row 1 -columnspan 1 -rowspan 1 \
19520         -sticky ew 
19521     grid $base.pr.cpd22.02 \
19522         -in $base.pr.cpd22 -column 1 -row 0 -columnspan 1 -rowspan 1 \
19523         -sticky ns 
19524     grid $base.pr.cpd22.03 \
19525         -in $base.pr.cpd22 -column 0 -row 0 -columnspan 1 -rowspan 1 \
19526         -sticky nesw 
19527 }
19528 
19529 Window show .
19530 Window show .ballon_object
19531 Window show .gui
19532 
19533 main $argc $argv
19534 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1