wizard.tcl

Go to the documentation of this file.
00001 /* !/bin/sh*/
00002 /*  the next line restarts using wish\*/
00003 exec wish "$0" "$@" 
00004 
00005 /* */
00006 /* BEGIN combobox*/
00007 
00008 ret  {combobox_init} () {
00009 # Copyright (c) 1998-2002, Bryan Oakley
00010 # All Rights Reservered
00011 #
00012 # Bryan Oakley
00013 # oakley@bardo.clearlight.com
00014 #
00015 # combobox v2.2.1 September 22, 2002
00016 #
00017 # a combobox / dropdown listbox (pick your favorite name) widget
00018 # written in pure tcl
00019 #
00020 # this code is freely distributable without restriction, but is
00021 # provided as-is with no warranty expressed or implied.
00022 #
00023 # thanks to the following people who provided beta test support or
00024 # patches to the code (in no particular order):
00025 #
00026 # Scott Beasley     Alexandre Ferrieux      Todd Helfter
00027 # Matt Gushee       Laurent Duperval        John Jackson
00028 # Fred Rapp         Christopher Nelson
00029 # Eric Galluzzo     Jean-Francois Moine
00030 #
00031 # A special thanks to Martin M. Hunt who provided several good ideas,
00032 # and always with a patch to implement them. Jean-Francois Moine,
00033 # Todd Helfter and John Jackson were also kind enough to send in some
00034 # code patches.
00035 #
00036 # ... and many others over the years.
00037 
00038 package require Tk 8.0
00039 package provide combobox 2.2.1
00040 
00041 namespace eval ::combobox {
00042 
00043     # this is the public interface
00044     namespace export combobox
00045 
00046     # these contain references to available options
00047     variable widgetOptions
00048 
00049     # these contain references to available commands and subcommands
00050     variable widgetCommands
00051     variable scanCommands
00052     variable listCommands
00053 }
00054 
00055 # ::combobox::combobox --
00056 #
00057 #     This is the command that gets exported. It creates a new
00058 #     combobox widget.
00059 #
00060 # Arguments:
00061 #
00062 #     w        path of new widget to create
00063 #     args     additional option/value pairs (eg: -background white, etc.)
00064 #
00065 # Results:
00066 #
00067 #     It creates the widget and sets up all of the default bindings
00068 #
00069 # Returns:
00070 #
00071 #     The name of the newly create widget
00072 
00073 proc ::combobox::combobox {w args} {
00074     variable widgetOptions
00075     variable widgetCommands
00076     variable scanCommands
00077     variable listCommands
00078 
00079     # perform a one time initialization
00080     if {![info exists widgetOptions]} {
00081     Init
00082     }
00083 
00084     # build it...
00085     eval Build $w $args
00086 
00087     # set some bindings...
00088     SetBindings $w
00089 
00090     # and we are done!
00091     return $w
00092 }
00093 
00094 
00095 # ::combobox::Init --
00096 #
00097 #     Initialize the namespace variables. This should only be called
00098 #     once, immediately prior to creating the first instance of the
00099 #     widget
00100 #
00101 # Arguments:
00102 #
00103 #    none
00104 #
00105 # Results:
00106 #
00107 #     All state variables are set to their default values; all of
00108 #     the option database entries will exist.
00109 #
00110 # Returns:
00111 #
00112 #     empty string
00113 
00114 proc ::combobox::Init {} {
00115     variable widgetOptions
00116     variable widgetCommands
00117     variable scanCommands
00118     variable listCommands
00119     variable defaultEntryCursor
00120 
00121     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}  ]
00122 
00123 
00124     set widgetCommands [list  bbox      cget     configure    curselection  delete    get      icursor      index         insert    list     scan         selection     xview     select   toggle       open          close      ]
00125 
00126     set listCommands [list  delete       get       index        insert       size  ]
00127 
00128     set scanCommands [list mark dragto]
00129 
00130     # why check for the Tk package? This lets us be sourced into
00131     # an interpreter that doesn't have Tk loaded, such as the slave
00132     # interpreter used by pkg_mkIndex. In theory it should have no
00133     # side effects when run
00134     if {[lsearch -exact [package names] "Tk"] != -1} {
00135 
00136     ##################################################################
00137     #- this initializes the option database. Kinda gross, but it works
00138     #- (I think).
00139     ##################################################################
00140 
00141     # the image used for the button...
00142     if {$::tcl_platform(platform) == "windows"} {
00143         image create bitmap ::combobox::bimage -data {
00144         #define down_arrow_width 12
00145         #define down_arrow_height 12
00146         static char down_arrow_bits[] = {
00147             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
00148             0xfc,0xf1,0xf8,0xf0,0x70,0xf0,0x20,0xf0,
00149             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00;
00150         }
00151         }
00152     } else {
00153         image create bitmap ::combobox::bimage -data  {
00154         #define down_arrow_width 15
00155         #define down_arrow_height 15
00156         static char down_arrow_bits[] = {
00157             0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,
00158             0x00,0x80,0xf8,0x8f,0xf0,0x87,0xe0,0x83,
00159             0xc0,0x81,0x80,0x80,0x00,0x80,0x00,0x80,
00160             0x00,0x80,0x00,0x80,0x00,0x80
00161         }
00162         }
00163     }
00164 
00165     # compute a widget name we can use to create a temporary widget
00166     set tmpWidget ".__tmp__"
00167     set count 0
00168     while {[winfo exists $tmpWidget] == 1} {
00169         set tmpWidget ".__tmp__$count"
00170         incr count
00171     }
00172 
00173     # get the scrollbar width. Because we try to be clever and draw our
00174     # own button instead of using a tk widget, we need to know what size
00175     # button to create. This little hack tells us the width of a scroll
00176     # bar.
00177     #
00178     # NB: we need to be sure and pick a window  that doesn't already
00179     # exist...
00180     scrollbar $tmpWidget
00181     set sb_width [winfo reqwidth $tmpWidget]
00182     destroy $tmpWidget
00183 
00184     # steal options from the entry widget
00185     # we want darn near all options, so we'll go ahead and do
00186     # them all. No harm done in adding the one or two that we
00187     # don't use.
00188     entry $tmpWidget
00189     foreach foo [$tmpWidget configure] {
00190         # the cursor option is special, so we'll save it in
00191         # a special way
00192         if {[lindex $foo 0] == "-cursor"} {
00193         set defaultEntryCursor [lindex $foo 4]
00194         }
00195         if {[llength $foo] == 5} {
00196         set option [lindex $foo 1]
00197         set value [lindex $foo 4]
00198         option add *Combobox.$option $value widgetDefault
00199 
00200         # these options also apply to the dropdown listbox
00201         if {[string compare $option "foreground"] == 0  || [string compare $option "background"] == 0  || [string compare $option "font"] == 0} {
00202             option add *Combobox*ComboboxListbox.$option $value  widgetDefault
00203         }
00204         }
00205     }
00206     destroy $tmpWidget
00207 
00208     # these are unique to us...
00209     option add *Combobox.dropdownWidth       {}     widgetDefault
00210     option add *Combobox.openCommand         {}     widgetDefault
00211     option add *Combobox.cursor              {}     widgetDefault
00212     option add *Combobox.commandState        normal widgetDefault
00213     option add *Combobox.editable            1      widgetDefault
00214     option add *Combobox.maxHeight           10     widgetDefault
00215     option add *Combobox.height              0
00216     }
00217 
00218     # set class bindings
00219     SetClassBindings
00220 }
00221 
00222 # ::combobox::SetClassBindings --
00223 #
00224 #    Sets up the default bindings for the widget class
00225 #
00226 #    this proc exists since it's The Right Thing To Do, but
00227 #    I haven't had the time to figure out how to do all the
00228 #    binding stuff on a class level. The main problem is that
00229 #    the entry widget must have focus for the insertion cursor
00230 #    to be visible. So, I either have to have the entry widget
00231 #    have the Combobox bindtag, or do some fancy juggling of
00232 #    events or some such. What a pain.
00233 #
00234 # Arguments:
00235 #
00236 #    none
00237 #
00238 # Returns:
00239 #
00240 #    empty string
00241 
00242 proc ::combobox::SetClassBindings {} {
00243 
00244     # make sure we clean up after ourselves...
00245     bind Combobox <Destroy> [list ::combobox::DestroyHandler %W]
00246 
00247     # this will (hopefully) close (and lose the grab on) the
00248     # listbox if the user clicks anywhere outside of it. Note
00249     # that on Windows, you can click on some other app and
00250     # the listbox will still be there, because tcl won't see
00251     # that button click
00252     set this {[::combobox::convert %W -W]}
00253     bind Combobox <Any-ButtonPress>   "$this close"
00254     bind Combobox <Any-ButtonRelease> "$this close"
00255 
00256     # this helps (but doesn't fully solve) focus issues. The general
00257     # idea is, whenever the frame gets focus it gets passed on to
00258     # the entry widget
00259     bind Combobox <FocusIn> {::combobox::tkTabToWindow [::combobox::convert %W -W].entry}
00260 
00261     # this closes the listbox if we get hidden
00262     bind Combobox <Unmap> {[::combobox::convert %W -W] close}
00263 
00264     return ""
00265 }
00266 
00267 # ::combobox::SetBindings --
00268 #
00269 #    here's where we do most of the binding foo. I think there's probably
00270 #    a few bindings I ought to add that I just haven't thought
00271 #    about...
00272 #
00273 #    I'm not convinced these are the proper bindings. Ideally all
00274 #    bindings should be on "Combobox", but because of my juggling of
00275 #    bindtags I'm not convinced thats what I want to do. But, it all
00276 #    seems to work, its just not as robust as it could be.
00277 #
00278 # Arguments:
00279 #
00280 #    w    widget pathname
00281 #
00282 # Returns:
00283 #
00284 #    empty string
00285 
00286 proc ::combobox::SetBindings {w} {
00287     upvar ::combobox::${w}::widgets  widgets
00288     upvar ::combobox::${w}::options  options
00289 
00290     # juggle the bindtags. The basic idea here is to associate the
00291     # widget name with the entry widget, so if a user does a bind
00292     # on the combobox it will get handled properly since it is
00293     # the entry widget that has keyboard focus.
00294     bindtags $widgets(entry)  [concat $widgets(this) [bindtags $widgets(entry)]]
00295 
00296     bindtags $widgets(button)  [concat $widgets(this) [bindtags $widgets(button)]]
00297 
00298     # override the default bindings for tab and shift-tab. The
00299     # focus procs take a widget as their only parameter and we
00300     # want to make sure the right window gets used (for shift-
00301     # tab we want it to appear as if the event was generated
00302     # on the frame rather than the entry.
00303     bind $widgets(entry) <Tab>  "::combobox::tkTabToWindow \[tk_focusNext $widgets(entry)\]; break"
00304     bind $widgets(entry) <Shift-Tab>  "::combobox::tkTabToWindow \[tk_focusPrev $widgets(this)\]; break"
00305 
00306     # this makes our "button" (which is actually a label)
00307     # do the right thing
00308     bind $widgets(button) <ButtonPress-1> [list $widgets(this) toggle]
00309 
00310     # this lets the autoscan of the listbox work, even if they
00311     # move the cursor over the entry widget.
00312     bind $widgets(entry) <B1-Enter> "break"
00313 
00314     bind $widgets(listbox) <ButtonRelease-1>  "::combobox::Select [list $widgets(this)]  \[$widgets(listbox) nearest %y\]; break"
00315 
00316     bind $widgets(vsb) <ButtonPress-1>   {continue}
00317     bind $widgets(vsb) <ButtonRelease-1> {continue}
00318 
00319     bind $widgets(listbox) <Any-Motion> {
00320     %W selection clear 0 end
00321     %W activate @%x,%y
00322     %W selection anchor @%x,%y
00323     %W selection set @%x,%y @%x,%y
00324     # need to do a yview if the cursor goes off the top
00325     # or bottom of the window... (or do we?)
00326     }
00327 
00328     # these events need to be passed from the entry widget
00329     # to the listbox, or otherwise need some sort of special
00330     # handling.
00331     foreach event [list <Up> <Down> <Tab> <Return> <Escape>  <Next> <Prior> <Double-1> <1> <Any-KeyPress>  <FocusIn> <FocusOut>] {
00332     bind $widgets(entry) $event  [list ::combobox::HandleEvent $widgets(this) $event]
00333     }
00334 
00335     # like the other events, <MouseWheel> needs to be passed from
00336     # the entry widget to the listbox. However, in this case we
00337     # need to add an additional parameter
00338     catch {
00339     bind $widgets(entry) <MouseWheel>  [list ::combobox::HandleEvent $widgets(this) <MouseWheel> %D]
00340     }
00341 }
00342 
00343 # ::combobox::Build --
00344 #
00345 #    This does all of the work necessary to create the basic
00346 #    combobox.
00347 #
00348 # Arguments:
00349 #
00350 #    w        widget name
00351 #    args     additional option/value pairs
00352 #
00353 # Results:
00354 #
00355 #    Creates a new widget with the given name. Also creates a new
00356 #    namespace patterened after the widget name, as a child namespace
00357 #    to ::combobox
00358 #
00359 # Returns:
00360 #
00361 #    the name of the widget
00362 
00363 proc ::combobox::Build {w args } {
00364     variable widgetOptions
00365 
00366     if {[winfo exists $w]} {
00367     error "window name \"$w\" already exists"
00368     }
00369 
00370     # create the namespace for this instance, and define a few
00371     # variables
00372     namespace eval ::combobox::$w {
00373 
00374     variable ignoreTrace 0
00375     variable oldFocus    {}
00376     variable oldGrab     {}
00377     variable oldValue    {}
00378     variable options
00379     variable this
00380     variable widgets
00381 
00382     set widgets(foo) foo  ;# coerce into an array
00383     set options(foo) foo  ;# coerce into an array
00384 
00385     unset widgets(foo)
00386     unset options(foo)
00387     }
00388 
00389     # import the widgets and options arrays into this proc so
00390     # we don't have to use fully qualified names, which is a
00391     # pain.
00392     upvar ::combobox::${w}::widgets widgets
00393     upvar ::combobox::${w}::options options
00394 
00395     # this is our widget -- a frame of class Combobox. Naturally,
00396     # it will contain other widgets. We create it here because
00397     # we need it in order to set some default options.
00398     set widgets(this)   [frame  $w -class Combobox -takefocus 0]
00399     set widgets(entry)  [entry  $w.entry -takefocus 1]
00400     set widgets(button) [label  $w.button -takefocus 0]
00401 
00402     # this defines all of the default options. We get the
00403     # values from the option database. Note that if an array
00404     # value is a list of length one it is an alias to another
00405     # option, so we just ignore it
00406     foreach name [array names widgetOptions] {
00407     if {[llength $widgetOptions($name)] == 1} continue
00408 
00409     set optName  [lindex $widgetOptions($name) 0]
00410     set optClass [lindex $widgetOptions($name) 1]
00411 
00412     set value [option get $w $optName $optClass]
00413     set options($name) $value
00414     }
00415 
00416     # a couple options aren't available in earlier versions of
00417     # tcl, so we'll set them to sane values. For that matter, if
00418     # they exist but are empty, set them to sane values.
00419     if {[string length $options(-disabledforeground)] == 0} {
00420         set options(-disabledforeground) $options(-foreground)
00421     }
00422     if {[string length $options(-disabledbackground)] == 0} {
00423         set options(-disabledbackground) $options(-background)
00424     }
00425 
00426     # if -value is set to null, we'll remove it from our
00427     # local array. The assumption is, if the user sets it from
00428     # the option database, they will set it to something other
00429     # than null (since it's impossible to determine the difference
00430     # between a null value and no value at all).
00431     if {[info exists options(-value)]  && [string length $options(-value)] == 0} {
00432     unset options(-value)
00433     }
00434 
00435     # we will later rename the frame's widget proc to be our
00436     # own custom widget proc. We need to keep track of this
00437     # new name, so we'll define and store it here...
00438     set widgets(frame) ::combobox::${w}::$w
00439 
00440     # gotta do this sooner or later. Might as well do it now
00441     pack $widgets(entry)  -side left  -fill both -expand yes
00442     pack $widgets(button) -side right -fill y    -expand no
00443 
00444     # I should probably do this in a catch, but for now it's
00445     # good enough... What it does, obviously, is put all of
00446     # the option/values pairs into an array. Make them easier
00447     # to handle later on...
00448     array set options $args
00449 
00450     # now, the dropdown list... the same renaming nonsense
00451     # must go on here as well...
00452     set widgets(dropdown)   [toplevel  $w.top]
00453     set widgets(listbox) [listbox   $w.top.list]
00454     set widgets(vsb)     [scrollbar $w.top.vsb]
00455 
00456     pack $widgets(listbox) -side left -fill both -expand y
00457 
00458     # fine tune the widgets based on the options (and a few
00459     # arbitrary values...)
00460 
00461     # NB: we are going to use the frame to handle the relief
00462     # of the widget as a whole, so the entry widget will be
00463     # flat. This makes the button which drops down the list
00464     # to appear "inside" the entry widget.
00465 
00466     $widgets(vsb) configure  -command "$widgets(listbox) yview"  -highlightthickness 0
00467 
00468     $widgets(button) configure  -highlightthickness 0  -borderwidth 1  -relief raised  -width [expr {[winfo reqwidth $widgets(vsb)] - 2}]
00469 
00470     $widgets(entry) configure  -borderwidth 0  -relief flat  -highlightthickness 0
00471 
00472     $widgets(dropdown) configure  -borderwidth 1  -relief sunken
00473 
00474     $widgets(listbox) configure  -selectmode browse  -background [$widgets(entry) cget -bg]  -yscrollcommand "$widgets(vsb) set"  -exportselection false  -borderwidth 0
00475 
00476 
00477 #    trace variable ::combobox::${w}::entryTextVariable w  #        [list ::combobox::EntryTrace $w]
00478     
00479     # do some window management foo on the dropdown window
00480     wm overrideredirect $widgets(dropdown) 1
00481     wm transient        $widgets(dropdown) [winfo toplevel $w]
00482     wm group            $widgets(dropdown) [winfo parent $w]
00483     wm resizable        $widgets(dropdown) 0 0
00484     wm withdraw         $widgets(dropdown)
00485 
00486     # this moves the original frame widget proc into our
00487     # namespace and gives it a handy name
00488     rename ::$w $widgets(frame)
00489 
00490     # now, create our widget proc. Obviously (?) it goes in
00491     # the global namespace. All combobox widgets will actually
00492     # share the same widget proc to cut down on the amount of
00493     # bloat.
00494     proc ::$w {command args}  "eval ::combobox::WidgetProc $w \$command \$args"
00495 
00496 
00497     # ok, the thing exists... let's do a bit more configuration.
00498     if {[catch "::combobox::Configure [list $widgets(this)] [array get options]" error]} {
00499     catch {destroy $w}
00500     error "internal error: $error"
00501     }
00502 
00503     return ""
00504 
00505 }
00506 
00507 # ::combobox::HandleEvent --
00508 #
00509 #    this proc handles events from the entry widget that we want
00510 #    handled specially (typically, to allow navigation of the list
00511 #    even though the focus is in the entry widget)
00512 #
00513 # Arguments:
00514 #
00515 #    w       widget pathname
00516 #    event   a string representing the event (not necessarily an
00517 #            actual event)
00518 #    args    additional arguments required by particular events
00519 
00520 proc ::combobox::HandleEvent {w event args} {
00521     upvar ::combobox::${w}::widgets  widgets
00522     upvar ::combobox::${w}::options  options
00523     upvar ::combobox::${w}::oldValue oldValue
00524 
00525     # for all of these events, if we have a special action we'll
00526     # do that and do a "return -code break" to keep additional
00527     # bindings from firing. Otherwise we'll let the event fall
00528     # on through.
00529     switch $event {
00530 
00531         "<MouseWheel>" {
00532         if {[winfo ismapped $widgets(dropdown)]} {
00533                 set D [lindex $args 0]
00534                 # the '120' number in the following expression has
00535                 # it's genesis in the tk bind manpage, which suggests
00536                 # that the smallest value of %D for mousewheel events
00537                 # will be 120. The intent is to scroll one line at a time.
00538                 $widgets(listbox) yview scroll [expr {-($D/120)}] units
00539             }
00540         }
00541 
00542     "<Any-KeyPress>" {
00543         # if the widget is editable, clear the selection.
00544         # this makes it more obvious what will happen if the
00545         # user presses <Return> (and helps our code know what
00546         # to do if the user presses return)
00547         if {$options(-editable)} {
00548         $widgets(listbox) see 0
00549         $widgets(listbox) selection clear 0 end
00550         $widgets(listbox) selection anchor 0
00551         $widgets(listbox) activate 0
00552         }
00553     }
00554 
00555     "<FocusIn>" {
00556         set oldValue [$widgets(entry) get]
00557     }
00558 
00559     "<FocusOut>" {
00560         if {![winfo ismapped $widgets(dropdown)]} {
00561         # did the value change?
00562         set newValue [$widgets(entry) get]
00563         if {$oldValue != $newValue} {
00564             CallCommand $widgets(this) $newValue
00565         }
00566         }
00567     }
00568 
00569     "<1>" {
00570         set editable [::combobox::GetBoolean $options(-editable)]
00571         if {!$editable} {
00572         if {[winfo ismapped $widgets(dropdown)]} {
00573             $widgets(this) close
00574             return -code break;
00575 
00576         } else {
00577             if {$options(-state) != "disabled"} {
00578             $widgets(this) open
00579             return -code break;
00580             }
00581         }
00582         }
00583     }
00584 
00585     "<Double-1>" {
00586         if {$options(-state) != "disabled"} {
00587         $widgets(this) toggle
00588         return -code break;
00589         }
00590     }
00591 
00592     "<Tab>" {
00593         if {[winfo ismapped $widgets(dropdown)]} {
00594         ::combobox::Find $widgets(this) 0
00595         return -code break;
00596         } else {
00597         ::combobox::SetValue $widgets(this) [$widgets(this) get]
00598         }
00599     }
00600 
00601     "<Escape>" {
00602 #       $widgets(entry) delete 0 end
00603 #       $widgets(entry) insert 0 $oldValue
00604         if {[winfo ismapped $widgets(dropdown)]} {
00605         $widgets(this) close
00606         return -code break;
00607         }
00608     }
00609 
00610     "<Return>" {
00611         # did the value change?
00612         set newValue [$widgets(entry) get]
00613         if {$oldValue != $newValue} {
00614         CallCommand $widgets(this) $newValue
00615         }
00616 
00617         if {[winfo ismapped $widgets(dropdown)]} {
00618         ::combobox::Select $widgets(this)  [$widgets(listbox) curselection]
00619         return -code break;
00620         }
00621 
00622     }
00623 
00624     "<Next>" {
00625         $widgets(listbox) yview scroll 1 pages
00626         set index [$widgets(listbox) index @0,0]
00627         $widgets(listbox) see $index
00628         $widgets(listbox) activate $index
00629         $widgets(listbox) selection clear 0 end
00630         $widgets(listbox) selection anchor $index
00631         $widgets(listbox) selection set $index
00632 
00633     }
00634 
00635     "<Prior>" {
00636         $widgets(listbox) yview scroll -1 pages
00637         set index [$widgets(listbox) index @0,0]
00638         $widgets(listbox) activate $index
00639         $widgets(listbox) see $index
00640         $widgets(listbox) selection clear 0 end
00641         $widgets(listbox) selection anchor $index
00642         $widgets(listbox) selection set $index
00643     }
00644 
00645     "<Down>" {
00646         if {[winfo ismapped $widgets(dropdown)]} {
00647         ::combobox::tkListboxUpDown $widgets(listbox) 1
00648         return -code break;
00649 
00650         } else {
00651         if {$options(-state) != "disabled"} {
00652             $widgets(this) open
00653             return -code break;
00654         }
00655         }
00656     }
00657     "<Up>" {
00658         if {[winfo ismapped $widgets(dropdown)]} {
00659         ::combobox::tkListboxUpDown $widgets(listbox) -1
00660         return -code break;
00661 
00662         } else {
00663         if {$options(-state) != "disabled"} {
00664             $widgets(this) open
00665             return -code break;
00666         }
00667         }
00668     }
00669     }
00670 
00671     return ""
00672 }
00673 
00674 # ::combobox::DestroyHandler {w} --
00675 #
00676 #    Cleans up after a combobox widget is destroyed
00677 #
00678 # Arguments:
00679 #
00680 #    w    widget pathname
00681 #
00682 # Results:
00683 #
00684 #    The namespace that was created for the widget is deleted,
00685 #    and the widget proc is removed.
00686 
00687 proc ::combobox::DestroyHandler {w} {
00688 
00689     # if the widget actually being destroyed is of class Combobox,
00690     # crush the namespace and kill the proc. Get it? Crush. Kill.
00691     # Destroy. Heh. Danger Will Robinson! Oh, man! I'm so funny it
00692     # brings tears to my eyes.
00693     if {[string compare [winfo class $w] "Combobox"] == 0} {
00694     upvar ::combobox::${w}::widgets  widgets
00695     upvar ::combobox::${w}::options  options
00696 
00697     # delete the namespace and the proc which represents
00698     # our widget
00699     namespace delete ::combobox::$w
00700     rename $w {}
00701     }
00702 
00703     return ""
00704 }
00705 
00706 # ::combobox::Find
00707 #
00708 #    finds something in the listbox that matches the pattern in the
00709 #    entry widget and selects it
00710 #
00711 #    N.B. I'm not convinced this is working the way it ought to. It
00712 #    works, but is the behavior what is expected? I've also got a gut
00713 #    feeling that there's a better way to do this, but I'm too lazy to
00714 #    figure it out...
00715 #
00716 # Arguments:
00717 #
00718 #    w      widget pathname
00719 #    exact  boolean; if true an exact match is desired
00720 #
00721 # Returns:
00722 #
00723 #    Empty string
00724 
00725 proc ::combobox::Find {w {exact 0}} {
00726     upvar ::combobox::${w}::widgets widgets
00727     upvar ::combobox::${w}::options options
00728 
00729     ## *sigh* this logic is rather gross and convoluted. Surely
00730     ## there is a more simple, straight-forward way to implement
00731     ## all this. As the saying goes, I lack the time to make it
00732     ## shorter...
00733 
00734     # use what is already in the entry widget as a pattern
00735     set pattern [$widgets(entry) get]
00736 
00737     if {[string length $pattern] == 0} {
00738     # clear the current selection
00739     $widgets(listbox) see 0
00740     $widgets(listbox) selection clear 0 end
00741     $widgets(listbox) selection anchor 0
00742     $widgets(listbox) activate 0
00743     return
00744     }
00745 
00746     # we're going to be searching this list...
00747     set list [$widgets(listbox) get 0 end]
00748 
00749     # if we are doing an exact match, try to find,
00750     # well, an exact match
00751     set exactMatch -1
00752     if {$exact} {
00753     set exactMatch [lsearch -exact $list $pattern]
00754     }
00755 
00756     # search for it. We'll try to be clever and not only
00757     # search for a match for what they typed, but a match for
00758     # something close to what they typed. We'll keep removing one
00759     # character at a time from the pattern until we find a match
00760     # of some sort.
00761     set index -1
00762     while {$index == -1 && [string length $pattern]} {
00763     set index [lsearch -glob $list "$pattern*"]
00764     if {$index == -1} {
00765         regsub {.$} $pattern {} pattern
00766     }
00767     }
00768 
00769     # this is the item that most closely matches...
00770     set thisItem [lindex $list $index]
00771 
00772     # did we find a match? If so, do some additional munging...
00773     if {$index != -1} {
00774 
00775     # we need to find the part of the first item that is
00776     # unique WRT the second... I know there's probably a
00777     # simpler way to do this...
00778 
00779     set nextIndex [expr {$index + 1}]
00780     set nextItem [lindex $list $nextIndex]
00781 
00782     # we don't really need to do much if the next
00783     # item doesn't match our pattern...
00784     if {[string match $pattern* $nextItem]} {
00785         # ok, the next item matches our pattern, too
00786         # now the trick is to find the first character
00787         # where they *don't* match...
00788         set marker [string length $pattern]
00789         while {$marker <= [string length $pattern]} {
00790         set a [string index $thisItem $marker]
00791         set b [string index $nextItem $marker]
00792         if {[string compare $a $b] == 0} {
00793             append pattern $a
00794             incr marker
00795         } else {
00796             break
00797         }
00798         }
00799     } else {
00800         set marker [string length $pattern]
00801     }
00802     
00803     } else {
00804     set marker end
00805     set index 0
00806     }
00807 
00808     # ok, we know the pattern and what part is unique;
00809     # update the entry widget and listbox appropriately
00810     if {$exact && $exactMatch == -1} {
00811     # this means we didn't find an exact match
00812     $widgets(listbox) selection clear 0 end
00813     $widgets(listbox) see $index
00814 
00815     } elseif {!$exact}  {
00816     # this means we found something, but it isn't an exact
00817     # match. If we find something that *is* an exact match we
00818     # don't need to do the following, since it would merely
00819     # be replacing the data in the entry widget with itself
00820     set oldstate [$widgets(entry) cget -state]
00821     $widgets(entry) configure -state normal
00822     $widgets(entry) delete 0 end
00823     $widgets(entry) insert end $thisItem
00824     $widgets(entry) selection clear
00825     $widgets(entry) selection range $marker end
00826     $widgets(listbox) activate $index
00827     $widgets(listbox) selection clear 0 end
00828     $widgets(listbox) selection anchor $index
00829     $widgets(listbox) selection set $index
00830     $widgets(listbox) see $index
00831     $widgets(entry) configure -state $oldstate
00832     }
00833 }
00834 
00835 # ::combobox::Select --
00836 #
00837 #    selects an item from the list and sets the value of the combobox
00838 #    to that value
00839 #
00840 # Arguments:
00841 #
00842 #    w      widget pathname
00843 #    index  listbox index of item to be selected
00844 #
00845 # Returns:
00846 #
00847 #    empty string
00848 
00849 proc ::combobox::Select {w index} {
00850     upvar ::combobox::${w}::widgets widgets
00851     upvar ::combobox::${w}::options options
00852 
00853     # the catch is because I'm sloppy -- presumably, the only time
00854     # an error will be caught is if there is no selection.
00855     if {![catch {set data [$widgets(listbox) get [lindex $index 0]]}]} {
00856     ::combobox::SetValue $widgets(this) $data
00857 
00858     $widgets(listbox) selection clear 0 end
00859     $widgets(listbox) selection anchor $index
00860     $widgets(listbox) selection set $index
00861 
00862     }
00863     $widgets(entry) selection range 0 end
00864 
00865     $widgets(this) close
00866 
00867     return ""
00868 }
00869 
00870 # ::combobox::HandleScrollbar --
00871 #
00872 #    causes the scrollbar of the dropdown list to appear or disappear
00873 #    based on the contents of the dropdown listbox
00874 #
00875 # Arguments:
00876 #
00877 #    w       widget pathname
00878 #    action  the action to perform on the scrollbar
00879 #
00880 # Returns:
00881 #
00882 #    an empty string
00883 
00884 proc ::combobox::HandleScrollbar {w {action "unknown"}} {
00885     upvar ::combobox::${w}::widgets widgets
00886     upvar ::combobox::${w}::options options
00887 
00888     if {$options(-height) == 0} {
00889     set hlimit $options(-maxheight)
00890     } else {
00891     set hlimit $options(-height)
00892     }       
00893 
00894     switch $action {
00895     "grow" {
00896         if {$hlimit > 0 && [$widgets(listbox) size] > $hlimit} {
00897         pack $widgets(vsb) -side right -fill y -expand n
00898         }
00899     }
00900 
00901     "shrink" {
00902         if {$hlimit > 0 && [$widgets(listbox) size] <= $hlimit} {
00903         pack forget $widgets(vsb)
00904         }
00905     }
00906 
00907     "crop" {
00908         # this means the window was cropped and we definitely
00909         # need a scrollbar no matter what the user wants
00910         pack $widgets(vsb) -side right -fill y -expand n
00911     }
00912 
00913     default {
00914         if {$hlimit > 0 && [$widgets(listbox) size] > $hlimit} {
00915         pack $widgets(vsb) -side right -fill y -expand n
00916         } else {
00917         pack forget $widgets(vsb)
00918         }
00919     }
00920     }
00921 
00922     return ""
00923 }
00924 
00925 # ::combobox::ComputeGeometry --
00926 #
00927 #    computes the geometry of the dropdown list based on the size of the
00928 #    combobox...
00929 #
00930 # Arguments:
00931 #
00932 #    w     widget pathname
00933 #
00934 # Returns:
00935 #
00936 #    the desired geometry of the listbox
00937 
00938 proc ::combobox::ComputeGeometry {w} {
00939     upvar ::combobox::${w}::widgets widgets
00940     upvar ::combobox::${w}::options options
00941 
00942     if {$options(-height) == 0 && $options(-maxheight) != "0"} {
00943     # if this is the case, count the items and see if
00944     # it exceeds our maxheight. If so, set the listbox
00945     # size to maxheight...
00946     set nitems [$widgets(listbox) size]
00947     if {$nitems > $options(-maxheight)} {
00948         # tweak the height of the listbox
00949         $widgets(listbox) configure -height $options(-maxheight)
00950     } else {
00951         # un-tweak the height of the listbox
00952         $widgets(listbox) configure -height 0
00953     }
00954     update idletasks
00955     }
00956 
00957     # compute height and width of the dropdown list
00958     set bd [$widgets(dropdown) cget -borderwidth]
00959     set height [expr {[winfo reqheight $widgets(dropdown)] + $bd + $bd}]
00960     if {[string length $options(-dropdownwidth)] == 0 ||
00961         $options(-dropdownwidth) == 0} {
00962         set width [winfo width $widgets(this)]
00963     } else {
00964         set m [font measure [$widgets(listbox) cget -font] "m"]
00965         set width [expr {$options(-dropdownwidth) * $m}]
00966     }
00967 
00968     # figure out where to place it on the screen, trying to take into
00969     # account we may be running under some virtual window manager
00970     set screenWidth  [winfo screenwidth $widgets(this)]
00971     set screenHeight [winfo screenheight $widgets(this)]
00972     set rootx        [winfo rootx $widgets(this)]
00973     set rooty        [winfo rooty $widgets(this)]
00974     set vrootx       [winfo vrootx $widgets(this)]
00975     set vrooty       [winfo vrooty $widgets(this)]
00976 
00977     # the x coordinate is simply the rootx of our widget, adjusted for
00978     # the virtual window. We won't worry about whether the window will
00979     # be offscreen to the left or right -- we want the illusion that it
00980     # is part of the entry widget, so if part of the entry widget is off-
00981     # screen, so will the list. If you want to change the behavior,
00982     # simply change the if statement... (and be sure to update this
00983     # comment!)
00984     set x  [expr {$rootx + $vrootx}]
00985     if {0} {
00986     set rightEdge [expr {$x + $width}]
00987     if {$rightEdge > $screenWidth} {
00988         set x [expr {$screenWidth - $width}]
00989     }
00990     if {$x < 0} {set x 0}
00991     }
00992 
00993     # the y coordinate is the rooty plus vrooty offset plus
00994     # the height of the static part of the widget plus 1 for a
00995     # tiny bit of visual separation...
00996     set y [expr {$rooty + $vrooty + [winfo reqheight $widgets(this)] + 1}]
00997     set bottomEdge [expr {$y + $height}]
00998 
00999     if {$bottomEdge >= $screenHeight} {
01000     # ok. Fine. Pop it up above the entry widget isntead of
01001     # below.
01002     set y [expr {($rooty - $height - 1) + $vrooty}]
01003 
01004     if {$y < 0} {
01005         # this means it extends beyond our screen. How annoying.
01006         # Now we'll try to be real clever and either pop it up or
01007         # down, depending on which way gives us the biggest list.
01008         # then, we'll trim the list to fit and force the use of
01009         # a scrollbar
01010 
01011         # (sadly, for windows users this measurement doesn't
01012         # take into consideration the height of the taskbar,
01013         # but don't blame me -- there isn't any way to detect
01014         # it or figure out its dimensions. The same probably
01015         # applies to any window manager with some magic windows
01016         # glued to the top or bottom of the screen)
01017 
01018         if {$rooty > [expr {$screenHeight / 2}]} {
01019         # we are in the lower half of the screen --
01020         # pop it up. Y is zero; that parts easy. The height
01021         # is simply the y coordinate of our widget, minus
01022         # a pixel for some visual separation. The y coordinate
01023         # will be the topof the screen.
01024         set y 1
01025         set height [expr {$rooty - 1 - $y}]
01026 
01027         } else {
01028         # we are in the upper half of the screen --
01029         # pop it down
01030         set y [expr {$rooty + $vrooty +  [winfo reqheight $widgets(this)] + 1}]
01031         set height [expr {$screenHeight - $y}]
01032 
01033         }
01034 
01035         # force a scrollbar
01036         HandleScrollbar $widgets(this) crop
01037     }   
01038     }
01039 
01040     if {$y < 0} {
01041     # hmmm. Bummer.
01042     set y 0
01043     set height $screenheight
01044     }
01045 
01046     set geometry [format "=%dx%d+%d+%d" $width $height $x $y]
01047 
01048     return $geometry
01049 }
01050 
01051 # ::combobox::DoInternalWidgetCommand --
01052 #
01053 #    perform an internal widget command, then mung any error results
01054 #    to look like it came from our megawidget. A lot of work just to
01055 #    give the illusion that our megawidget is an atomic widget
01056 #
01057 # Arguments:
01058 #
01059 #    w           widget pathname
01060 #    subwidget   pathname of the subwidget
01061 #    command     subwidget command to be executed
01062 #    args        arguments to the command
01063 #
01064 # Returns:
01065 #
01066 #    The result of the subwidget command, or an error
01067 
01068 proc ::combobox::DoInternalWidgetCommand {w subwidget command args} {
01069     upvar ::combobox::${w}::widgets widgets
01070     upvar ::combobox::${w}::options options
01071 
01072     set subcommand $command
01073     set command [concat $widgets($subwidget) $command $args]
01074     if {[catch $command result]} {
01075     # replace the subwidget name with the megawidget name
01076     regsub $widgets($subwidget) $result $widgets(this) result
01077 
01078     # replace specific instances of the subwidget command
01079     # with out megawidget command
01080     switch $subwidget,$subcommand {
01081         listbox,index  {regsub "index"  $result "list index"  result}
01082         listbox,insert {regsub "insert" $result "list insert" result}
01083         listbox,delete {regsub "delete" $result "list delete" result}
01084         listbox,get    {regsub "get"    $result "list get"    result}
01085         listbox,size   {regsub "size"   $result "list size"   result}
01086     }
01087     error $result
01088 
01089     } else {
01090     return $result
01091     }
01092 }
01093 
01094 
01095 # ::combobox::WidgetProc --
01096 #
01097 #    This gets uses as the widgetproc for an combobox widget.
01098 #    Notice where the widget is created and you'll see that the
01099 #    actual widget proc merely evals this proc with all of the
01100 #    arguments intact.
01101 #
01102 #    Note that some widget commands are defined "inline" (ie:
01103 #    within this proc), and some do most of their work in
01104 #    separate procs. This is merely because sometimes it was
01105 #    easier to do it one way or the other.
01106 #
01107 # Arguments:
01108 #
01109 #    w         widget pathname
01110 #    command   widget subcommand
01111 #    args      additional arguments; varies with the subcommand
01112 #
01113 # Results:
01114 #
01115 #    Performs the requested widget command
01116 
01117 proc ::combobox::WidgetProc {w command args} {
01118     upvar ::combobox::${w}::widgets widgets
01119     upvar ::combobox::${w}::options options
01120     upvar ::combobox::${w}::oldFocus oldFocus
01121     upvar ::combobox::${w}::oldFocus oldGrab
01122 
01123     set command [::combobox::Canonize $w command $command]
01124 
01125     # this is just shorthand notation...
01126     set doWidgetCommand  [list ::combobox::DoInternalWidgetCommand $widgets(this)]
01127 
01128     if {$command == "list"} {
01129     # ok, the next argument is a list command; we'll
01130     # rip it from args and append it to command to
01131     # create a unique internal command
01132     #
01133     # NB: because of the sloppy way we are doing this,
01134     # we'll also let the user enter our secret command
01135     # directly (eg: listinsert, listdelete), but we
01136     # won't document that fact
01137     set command "list-[lindex $args 0]"
01138     set args [lrange $args 1 end]
01139     }
01140 
01141     set result ""
01142 
01143     # many of these commands are just synonyms for specific
01144     # commands in one of the subwidgets. We'll get them out
01145     # of the way first, then do the custom commands.
01146     switch $command {
01147     bbox -
01148     delete -
01149     get -
01150     icursor -
01151     index -
01152     insert -
01153     scan -
01154     selection -
01155     xview {
01156         set result [eval $doWidgetCommand entry $command $args]
01157     }
01158     list-get    {set result [eval $doWidgetCommand listbox get $args]}
01159     list-index  {set result [eval $doWidgetCommand listbox index $args]}
01160     list-size   {set result [eval $doWidgetCommand listbox size $args]}
01161 
01162     select {
01163         if {[llength $args] == 1} {
01164         set index [lindex $args 0]
01165         set result [Select $widgets(this) $index]
01166         } else {
01167         error "usage: $w select index"
01168         }
01169     }
01170 
01171     subwidget {
01172         set knownWidgets [list button entry listbox dropdown vsb]
01173         if {[llength $args] == 0} {
01174         return $knownWidgets
01175         }
01176 
01177         set name [lindex $args 0]
01178         if {[lsearch $knownWidgets $name] != -1} {
01179         set result $widgets($name)
01180         } else {
01181         error "unknown subwidget $name"
01182         }
01183     }
01184 
01185     curselection {
01186         set result [eval $doWidgetCommand listbox curselection]
01187     }
01188 
01189     list-insert {
01190         eval $doWidgetCommand listbox insert $args
01191         set result [HandleScrollbar $w "grow"]
01192     }
01193 
01194     list-delete {
01195         eval $doWidgetCommand listbox delete $args
01196         set result [HandleScrollbar $w "shrink"]
01197     }
01198 
01199     toggle {
01200         # ignore this command if the widget is disabled...
01201         if {$options(-state) == "disabled"} return
01202 
01203         # pops down the list if it is not, hides it
01204         # if it is...
01205         if {[winfo ismapped $widgets(dropdown)]} {
01206         set result [$widgets(this) close]
01207         } else {
01208         set result [$widgets(this) open]
01209         }
01210     }
01211 
01212     open {
01213 
01214         # if this is an editable combobox, the focus should
01215         # be set to the entry widget
01216         if {$options(-editable)} {
01217         focus $widgets(entry)
01218         $widgets(entry) select range 0 end
01219         $widgets(entry) icur end
01220         }
01221 
01222         # if we are disabled, we won't allow this to happen
01223         if {$options(-state) == "disabled"} {
01224         return 0
01225         }
01226 
01227         # if there is a -opencommand, execute it now
01228         if {[string length $options(-opencommand)] > 0} {
01229         # hmmm... should I do a catch, or just let the normal
01230         # error handling handle any errors? For now, the latter...
01231         uplevel \#0 $options(-opencommand)
01232         }
01233 
01234         # compute the geometry of the window to pop up, and set
01235         # it, and force the window manager to take notice
01236         # (even if it is not presently visible).
01237         #
01238         # this isn't strictly necessary if the window is already
01239         # mapped, but we'll go ahead and set the geometry here
01240         # since its harmless and *may* actually reset the geometry
01241         # to something better in some weird case.
01242         set geometry [::combobox::ComputeGeometry $widgets(this)]
01243         wm geometry $widgets(dropdown) $geometry
01244         update idletasks
01245 
01246         # if we are already open, there's nothing else to do
01247         if {[winfo ismapped $widgets(dropdown)]} {
01248         return 0
01249         }
01250 
01251         # save the widget that currently has the focus; we'll restore
01252         # the focus there when we're done
01253         set oldFocus [focus]
01254 
01255         # ok, tweak the visual appearance of things and
01256         # make the list pop up
01257         $widgets(button) configure -relief sunken
01258         raise $widgets(dropdown) [winfo parent $widgets(this)]
01259         wm deiconify $widgets(dropdown)
01260         raise $widgets(dropdown)
01261 
01262         # force focus to the entry widget so we can handle keypress
01263         # events for traversal
01264         focus -force $widgets(entry)
01265 
01266         # select something by default, but only if its an
01267         # exact match...
01268         ::combobox::Find $widgets(this) 1
01269 
01270         # save the current grab state for the display containing
01271         # this widget. We'll restore it when we close the dropdown
01272         # list
01273         set status "none"
01274         set grab [grab current $widgets(this)]
01275         if {$grab != ""} {set status [grab status $grab]}
01276         set oldGrab [list $grab $status]
01277         unset grab status
01278 
01279         # *gasp* do a global grab!!! Mom always told me not to
01280         # do things like this, but sometimes a man's gotta do
01281         # what a man's gotta do.
01282         grab -global $widgets(this)
01283 
01284         # fake the listbox into thinking it has focus. This is
01285         # necessary to get scanning initialized properly in the
01286         # listbox.
01287         event generate $widgets(listbox) <B1-Enter>
01288 
01289         return 1
01290     }
01291 
01292     close {
01293         # if we are already closed, don't do anything...
01294         if {![winfo ismapped $widgets(dropdown)]} {
01295         return 0
01296         }
01297 
01298         # restore the focus and grab, but ignore any errors...
01299         # we're going to be paranoid and release the grab before
01300         # trying to set any other grab because we really really
01301         # really want to make sure the grab is released.
01302         catch {focus $oldFocus} result
01303         catch {grab release $widgets(this)}
01304         catch {
01305         set status [lindex $oldGrab 1]
01306         if {$status == "global"} {
01307             grab -global [lindex $oldGrab 0]
01308         } elseif {$status == "local"} {
01309             grab [lindex $oldGrab 0]
01310         }
01311         unset status
01312         }
01313 
01314         # hides the listbox
01315         $widgets(button) configure -relief raised
01316         wm withdraw $widgets(dropdown)
01317 
01318         # select the data in the entry widget. Not sure
01319         # why, other than observation seems to suggest that's
01320         # what windows widgets do.
01321         set editable [::combobox::GetBoolean $options(-editable)]
01322         if {$editable} {
01323         $widgets(entry) selection range 0 end
01324         $widgets(button) configure -relief raised
01325         }
01326 
01327 
01328         # magic tcl stuff (see tk.tcl in the distribution
01329         # lib directory)
01330         ::combobox::tkCancelRepeat
01331 
01332         return 1
01333     }
01334 
01335     cget {
01336         if {[llength $args] != 1} {
01337         error "wrong # args: should be $w cget option"
01338         }
01339         set opt [::combobox::Canonize $w option [lindex $args 0]]
01340 
01341         if {$opt == "-value"} {
01342         set result [$widgets(entry) get]
01343         } else {
01344         set result $options($opt)
01345         }
01346     }
01347 
01348     configure {
01349         set result [eval ::combobox::Configure {$w} $args]
01350     }
01351 
01352     default {
01353         error "bad option \"$command\""
01354     }
01355     }
01356 
01357     return $result
01358 }
01359 
01360 # ::combobox::Configure --
01361 #
01362 #    Implements the "configure" widget subcommand
01363 #
01364 # Arguments:
01365 #
01366 #    w      widget pathname
01367 #    args   zero or more option/value pairs (or a single option)
01368 #
01369 # Results:
01370 #
01371 #    Performs typcial "configure" type requests on the widget
01372 
01373 proc ::combobox::Configure {w args} {
01374     variable widgetOptions
01375     variable defaultEntryCursor
01376 
01377     upvar ::combobox::${w}::widgets widgets
01378     upvar ::combobox::${w}::options options
01379 
01380     if {[llength $args] == 0} {
01381     # hmmm. User must be wanting all configuration information
01382     # note that if the value of an array element is of length
01383     # one it is an alias, which needs to be handled slightly
01384     # differently
01385     set results {}
01386     foreach opt [lsort [array names widgetOptions]] {
01387         if {[llength $widgetOptions($opt)] == 1} {
01388         set alias $widgetOptions($opt)
01389         set optName $widgetOptions($alias)
01390         lappend results [list $opt $optName]
01391         } else {
01392         set optName  [lindex $widgetOptions($opt) 0]
01393         set optClass [lindex $widgetOptions($opt) 1]
01394         set default [option get $w $optName $optClass]
01395         if {[info exists options($opt)]} {
01396             lappend results [list $opt $optName $optClass  $default $options($opt)]
01397         } else {
01398             lappend results [list $opt $optName $optClass  $default ""]
01399         }
01400         }
01401     }
01402 
01403     return $results
01404     }
01405 
01406     # one argument means we are looking for configuration
01407     # information on a single option
01408     if {[llength $args] == 1} {
01409     set opt [::combobox::Canonize $w option [lindex $args 0]]
01410 
01411     set optName  [lindex $widgetOptions($opt) 0]
01412     set optClass [lindex $widgetOptions($opt) 1]
01413     set default [option get $w $optName $optClass]
01414     set results [list $opt $optName $optClass  $default $options($opt)]
01415     return $results
01416     }
01417 
01418     # if we have an odd number of values, bail.
01419     if {[expr {[llength $args]%2}] == 1} {
01420     # hmmm. An odd number of elements in args
01421     error "value for \"[lindex $args end]\" missing"
01422     }
01423 
01424     # Great. An even number of options. Let's make sure they
01425     # are all valid before we do anything. Note that Canonize
01426     # will generate an error if it finds a bogus option; otherwise
01427     # it returns the canonical option name
01428     foreach {name value} $args {
01429     set name [::combobox::Canonize $w option $name]
01430     set opts($name) $value
01431     }
01432 
01433     # process all of the configuration options
01434     # some (actually, most) options require us to
01435     # do something, like change the attributes of
01436     # a widget or two. Here's where we do that...
01437     #
01438     # note that the handling of disabledforeground and
01439     # disabledbackground is a little wonky. First, we have
01440     # to deal with backwards compatibility (ie: tk 8.3 and below
01441     # didn't have such options for the entry widget), and
01442     # we have to deal with the fact we might want to disable
01443     # the entry widget but use the normal foreground/background
01444     # for when the combobox is not disabled, but not editable either.
01445 
01446     set updateVisual 0
01447     foreach option [array names opts] {
01448     set newValue $opts($option)
01449     if {[info exists options($option)]} {
01450         set oldValue $options($option)
01451     }
01452 
01453     switch -- $option {
01454         -background {
01455         set updateVisual 1
01456         set options($option) $newValue
01457         }
01458 
01459         -borderwidth {
01460         $widgets(frame) configure -borderwidth $newValue
01461         set options($option) $newValue
01462         }
01463 
01464         -command {
01465         # nothing else to do...
01466         set options($option) $newValue
01467         }
01468 
01469         -commandstate {
01470         # do some value checking...
01471         if {$newValue != "normal" && $newValue != "disabled"} {
01472             set options($option) $oldValue
01473             set message "bad state value \"$newValue\";"
01474             append message " must be normal or disabled"
01475             error $message
01476         }
01477         set options($option) $newValue
01478         }
01479 
01480         -cursor {
01481         $widgets(frame) configure -cursor $newValue
01482         $widgets(entry) configure -cursor $newValue
01483         $widgets(listbox) configure -cursor $newValue
01484         set options($option) $newValue
01485         }
01486 
01487         -disabledforeground {
01488         set updateVisual 1
01489         set options($option) $newValue
01490         }
01491 
01492         -disabledbackground {
01493         set updateVisual 1
01494         set options($option) $newValue
01495         }
01496 
01497             -dropdownwidth {
01498                 set options($option) $newValue
01499             }
01500 
01501         -editable {
01502         set updateVisual 1
01503         if {$newValue} {
01504             # it's editable...
01505             $widgets(entry) configure  -state normal  -cursor $defaultEntryCursor
01506         } else {
01507             $widgets(entry) configure  -state disabled  -cursor $options(-cursor)
01508         }
01509         set options($option) $newValue
01510         }
01511 
01512         -font {
01513         $widgets(entry) configure -font $newValue
01514         $widgets(listbox) configure -font $newValue
01515         set options($option) $newValue
01516         }
01517 
01518         -foreground {
01519         set updateVisual 1
01520         set options($option) $newValue
01521         }
01522 
01523         -height {
01524         $widgets(listbox) configure -height $newValue
01525         HandleScrollbar $w
01526         set options($option) $newValue
01527         }
01528 
01529         -highlightbackground {
01530         $widgets(frame) configure -highlightbackground $newValue
01531         set options($option) $newValue
01532         }
01533 
01534         -highlightcolor {
01535         $widgets(frame) configure -highlightcolor $newValue
01536         set options($option) $newValue
01537         }
01538 
01539         -highlightthickness {
01540         $widgets(frame) configure -highlightthickness $newValue
01541         set options($option) $newValue
01542         }
01543     
01544         -image {
01545         if {[string length $newValue] > 0} {
01546             $widgets(button) configure -image $newValue
01547         } else {
01548             $widgets(button) configure -image ::combobox::bimage
01549         }
01550         set options($option) $newValue
01551         }
01552 
01553         -maxheight {
01554         # ComputeGeometry may dork with the actual height
01555         # of the listbox, so let's undork it
01556         $widgets(listbox) configure -height $options(-height)
01557         HandleScrollbar $w
01558         set options($option) $newValue
01559         }
01560 
01561         -opencommand {
01562         # nothing else to do...
01563         set options($option) $newValue
01564         }
01565 
01566         -relief {
01567         $widgets(frame) configure -relief $newValue
01568         set options($option) $newValue
01569         }
01570 
01571         -selectbackground {
01572         $widgets(entry) configure -selectbackground $newValue
01573         $widgets(listbox) configure -selectbackground $newValue
01574         set options($option) $newValue
01575         }
01576 
01577         -selectborderwidth {
01578         $widgets(entry) configure -selectborderwidth $newValue
01579         $widgets(listbox) configure -selectborderwidth $newValue
01580         set options($option) $newValue
01581         }
01582 
01583         -selectforeground {
01584         $widgets(entry) configure -selectforeground $newValue
01585         $widgets(listbox) configure -selectforeground $newValue
01586         set options($option) $newValue
01587         }
01588 
01589         -state {
01590         if {$newValue == "normal"} {
01591             set updateVisual 1
01592             # it's enabled
01593 
01594             set editable [::combobox::GetBoolean  $options(-editable)]
01595             if {$editable} {
01596             $widgets(entry) configure -state normal
01597             $widgets(entry) configure -takefocus 1
01598             }
01599 
01600                     # note that $widgets(button) is actually a label,
01601                     # not a button. And being able to disable labels
01602                     # wasn't possible until tk 8.3. (makes me wonder
01603             # why I chose to use a label, but that answer is
01604             # lost to antiquity)
01605                     if {[info patchlevel] >= 8.3} {
01606                         $widgets(button) configure -state normal
01607                     }
01608 
01609         } elseif {$newValue == "disabled"}  {
01610             set updateVisual 1
01611             # it's disabled
01612             $widgets(entry) configure -state disabled
01613             $widgets(entry) configure -takefocus 0
01614                     # note that $widgets(button) is actually a label,
01615                     # not a button. And being able to disable labels
01616                     # wasn't possible until tk 8.3. (makes me wonder
01617             # why I chose to use a label, but that answer is
01618             # lost to antiquity)
01619                     if {$::tcl_version >= 8.3} {
01620                         $widgets(button) configure -state disabled
01621                     }
01622 
01623         } else {
01624             set options($option) $oldValue
01625             set message "bad state value \"$newValue\";"
01626             append message " must be normal or disabled"
01627             error $message
01628         }
01629 
01630         set options($option) $newValue
01631         }
01632 
01633         -takefocus {
01634         $widgets(entry) configure -takefocus $newValue
01635         set options($option) $newValue
01636         }
01637 
01638         -textvariable {
01639         $widgets(entry) configure -textvariable $newValue
01640         set options($option) $newValue
01641         }
01642 
01643         -value {
01644         ::combobox::SetValue $widgets(this) $newValue
01645         set options($option) $newValue
01646         }
01647 
01648         -width {
01649         $widgets(entry) configure -width $newValue
01650         $widgets(listbox) configure -width $newValue
01651         set options($option) $newValue
01652         }
01653 
01654         -xscrollcommand {
01655         $widgets(entry) configure -xscrollcommand $newValue
01656         set options($option) $newValue
01657         }
01658     }   
01659 
01660     if {$updateVisual} {UpdateVisualAttributes $w}
01661     }
01662 }
01663 
01664 # ::combobox::UpdateVisualAttributes --
01665 #
01666 # sets the visual attributes (foreground, background mostly)
01667 # based on the current state of the widget (normal/disabled,
01668 # editable/non-editable)
01669 #
01670 # why a proc for such a simple thing? Well, in addition to the
01671 # various states of the widget, we also have to consider the
01672 # version of tk being used -- versions from 8.4 and beyond have
01673 # the notion of disabled foreground/background options for various
01674 # widgets. All of the permutations can get nasty, so we encapsulate
01675 # it all in one spot.
01676 #
01677 # note also that we don't handle all visual attributes here; just
01678 # the ones that depend on the state of the widget. The rest are
01679 # handled on a case by case basis
01680 #
01681 # Arguments:
01682 #    w      widget pathname
01683 #
01684 # Returns:
01685 #    empty string
01686 
01687 proc ::combobox::UpdateVisualAttributes {w} {
01688 
01689     upvar ::combobox::${w}::widgets     widgets
01690     upvar ::combobox::${w}::options     options
01691 
01692     if {$options(-state) == "normal"} {
01693 
01694     set foreground $options(-foreground)
01695     set background $options(-background)
01696     
01697     } elseif {$options(-state) == "disabled"} {
01698 
01699     set foreground $options(-disabledforeground)
01700     set background $options(-disabledbackground)
01701     }
01702 
01703     $widgets(entry)   configure -foreground $foreground -background $background
01704     $widgets(listbox) configure -foreground $foreground -background $background
01705     $widgets(button)  configure -foreground $foreground
01706     $widgets(vsb)     configure -background $background -troughcolor $background
01707     $widgets(frame)   configure -background $background
01708 
01709     # we need to set the disabled colors in case our widget is disabled.
01710     # We could actually check for disabled-ness, but we also need to
01711     # check whether we're enabled but not editable, in which case the
01712     # entry widget is disabled but we still want the enabled colors. It's
01713     # easier just to set everything and be done with it.
01714 
01715     if {$::tcl_version >= 8.4} {
01716     $widgets(entry) configure  -disabledforeground $foreground  -disabledbackground $background
01717     $widgets(button)  configure -disabledforeground $foreground
01718     $widgets(listbox) configure -disabledforeground $foreground
01719     }
01720 }
01721 
01722 # ::combobox::SetValue --
01723 #
01724 #    sets the value of the combobox and calls the -command,
01725 #    if defined
01726 #
01727 # Arguments:
01728 #
01729 #    w          widget pathname
01730 #    newValue   the new value of the combobox
01731 #
01732 # Returns
01733 #
01734 #    Empty string
01735 
01736 proc ::combobox::SetValue {w newValue} {
01737 
01738     upvar ::combobox::${w}::widgets     widgets
01739     upvar ::combobox::${w}::options     options
01740     upvar ::combobox::${w}::ignoreTrace ignoreTrace
01741     upvar ::combobox::${w}::oldValue    oldValue
01742 
01743     if {[info exists options(-textvariable)]  && [string length $options(-textvariable)] > 0} {
01744     set variable ::$options(-textvariable)
01745     set $variable $newValue
01746     } else {
01747     set oldstate [$widgets(entry) cget -state]
01748     $widgets(entry) configure -state normal
01749     $widgets(entry) delete 0 end
01750     $widgets(entry) insert 0 $newValue
01751     $widgets(entry) configure -state $oldstate
01752     }
01753 
01754     # set our internal textvariable; this will cause any public
01755     # textvariable (ie: defined by the user) to be updated as
01756     # well
01757 #    set ::combobox::${w}::entryTextVariable $newValue
01758 
01759     # redefine our concept of the "old value". Do it before running
01760     # any associated command so we can be sure it happens even
01761     # if the command somehow fails.
01762     set oldValue $newValue
01763 
01764 
01765     # call the associated command. The proc will handle whether or
01766     # not to actually call it, and with what args
01767     CallCommand $w $newValue
01768 
01769     return ""
01770 }
01771 
01772 # ::combobox::CallCommand --
01773 #
01774 #   calls the associated command, if any, appending the new
01775 #   value to the command to be called.
01776 #
01777 # Arguments:
01778 #
01779 #    w         widget pathname
01780 #    newValue  the new value of the combobox
01781 #
01782 # Returns
01783 #
01784 #    empty string
01785 
01786 proc ::combobox::CallCommand {w newValue} {
01787     upvar ::combobox::${w}::widgets widgets
01788     upvar ::combobox::${w}::options options
01789 
01790     # call the associated command, if defined and -commandstate is
01791     # set to "normal"
01792     if {$options(-commandstate) == "normal" &&  [string length $options(-command)] > 0} {
01793     set args [list $widgets(this) $newValue]
01794     uplevel \#0 $options(-command) $args
01795     }
01796 }
01797 
01798 
01799 # ::combobox::GetBoolean --
01800 #
01801 #     returns the value of a (presumably) boolean string (ie: it should
01802 #     do the right thing if the string is "yes", "no", "true", 1, etc
01803 #
01804 # Arguments:
01805 #
01806 #     value       value to be converted
01807 #     errorValue  a default value to be returned in case of an error
01808 #
01809 # Returns:
01810 #
01811 #     a 1 or zero, or the value of errorValue if the string isn't
01812 #     a proper boolean value
01813 
01814 proc ::combobox::GetBoolean {value {errorValue 1}} {
01815     if {[catch {expr {([string trim $value])?1:0}} res]} {
01816     return $errorValue
01817     } else {
01818     return $res
01819     }
01820 }
01821 
01822 # ::combobox::convert --
01823 #
01824 #     public routine to convert %x, %y and %W binding substitutions.
01825 #     Given an x, y and or %W value relative to a given widget, this
01826 #     routine will convert the values to be relative to the combobox
01827 #     widget. For example, it could be used in a binding like this:
01828 #
01829 #     bind .combobox <blah> {doSomething [::combobox::convert %W -x %x]}
01830 #
01831 #     Note that this procedure is *not* exported, but is intended for
01832 #     public use. It is not exported because the name could easily
01833 #     clash with existing commands.
01834 #
01835 # Arguments:
01836 #
01837 #     w     a widget path; typically the actual result of a %W
01838 #           substitution in a binding. It should be either a
01839 #           combobox widget or one of its subwidgets
01840 #
01841 #     args  should one or more of the following arguments or
01842 #           pairs of arguments:
01843 #
01844 #           -x <x>      will convert the value <x>; typically <x> will
01845 #                       be the result of a %x substitution
01846 #           -y <y>      will convert the value <y>; typically <y> will
01847 #                       be the result of a %y substitution
01848 #           -W (or -w)  will return the name of the combobox widget
01849 #                       which is the parent of $w
01850 #
01851 # Returns:
01852 #
01853 #     a list of the requested values. For example, a single -w will
01854 #     result in a list of one items, the name of the combobox widget.
01855 #     Supplying "-x 10 -y 20 -W" (in any order) will return a list of
01856 #     three values: the converted x and y values, and the name of
01857 #     the combobox widget.
01858 
01859 proc ::combobox::convert {w args} {
01860     set result {}
01861     if {![winfo exists $w]} {
01862     error "window \"$w\" doesn't exist"
01863     }
01864 
01865     while {[llength $args] > 0} {
01866     set option [lindex $args 0]
01867     set args [lrange $args 1 end]
01868 
01869     switch -exact -- $option {
01870         -x {
01871         set value [lindex $args 0]
01872         set args [lrange $args 1 end]
01873         set win $w
01874         while {[winfo class $win] != "Combobox"} {
01875             incr value [winfo x $win]
01876             set win [winfo parent $win]
01877             if {$win == "."} break
01878         }
01879         lappend result $value
01880         }
01881 
01882         -y {
01883         set value [lindex $args 0]
01884         set args [lrange $args 1 end]
01885         set win $w
01886         while {[winfo class $win] != "Combobox"} {
01887             incr value [winfo y $win]
01888             set win [winfo parent $win]
01889             if {$win == "."} break
01890         }
01891         lappend result $value
01892         }
01893 
01894         -w -
01895         -W {
01896         set win $w
01897         while {[winfo class $win] != "Combobox"} {
01898             set win [winfo parent $win]
01899             if {$win == "."} break;
01900         }
01901         lappend result $win
01902         }
01903     }
01904     }
01905     return $result
01906 }
01907 
01908 # ::combobox::Canonize --
01909 #
01910 #    takes a (possibly abbreviated) option or command name and either
01911 #    returns the canonical name or an error
01912 #
01913 # Arguments:
01914 #
01915 #    w        widget pathname
01916 #    object   type of object to canonize; must be one of "command",
01917 #             "option", "scan command" or "list command"
01918 #    opt      the option (or command) to be canonized
01919 #
01920 # Returns:
01921 #
01922 #    Returns either the canonical form of an option or command,
01923 #    or raises an error if the option or command is unknown or
01924 #    ambiguous.
01925 
01926 proc ::combobox::Canonize {w object opt} {
01927     variable widgetOptions
01928     variable columnOptions
01929     variable widgetCommands
01930     variable listCommands
01931     variable scanCommands
01932 
01933     switch $object {
01934     command {
01935         if {[lsearch -exact $widgetCommands $opt] >= 0} {
01936         return $opt
01937         }
01938 
01939         # command names aren't stored in an array, and there
01940         # isn't a way to get all the matches in a list, so
01941         # we'll stuff the commands in a temporary array so
01942         # we can use [array names]
01943         set list $widgetCommands
01944         foreach element $list {
01945         set tmp($element) ""
01946         }
01947         set matches [array names tmp ${opt}*]
01948     }
01949 
01950     {list command} {
01951         if {[lsearch -exact $listCommands $opt] >= 0} {
01952         return $opt
01953         }
01954 
01955         # command names aren't stored in an array, and there
01956         # isn't a way to get all the matches in a list, so
01957         # we'll stuff the commands in a temporary array so
01958         # we can use [array names]
01959         set list $listCommands
01960         foreach element $list {
01961         set tmp($element) ""
01962         }
01963         set matches [array names tmp ${opt}*]
01964     }
01965 
01966     {scan command} {
01967         if {[lsearch -exact $scanCommands $opt] >= 0} {
01968         return $opt
01969         }
01970 
01971         # command names aren't stored in an array, and there
01972         # isn't a way to get all the matches in a list, so
01973         # we'll stuff the commands in a temporary array so
01974         # we can use [array names]
01975         set list $scanCommands
01976         foreach element $list {
01977         set tmp($element) ""
01978         }
01979         set matches [array names tmp ${opt}*]
01980     }
01981 
01982     option {
01983         if {[info exists widgetOptions($opt)]  && [llength $widgetOptions($opt)] == 2} {
01984         return $opt
01985         }
01986         set list [array names widgetOptions]
01987         set matches [array names widgetOptions ${opt}*]
01988     }
01989 
01990     }
01991 
01992     if {[llength $matches] == 0} {
01993     set choices [HumanizeList $list]
01994     error "unknown $object \"$opt\"; must be one of $choices"
01995 
01996     } elseif {[llength $matches] == 1} {
01997     set opt [lindex $matches 0]
01998 
01999     # deal with option aliases
02000     switch $object {
02001         option {
02002         set opt [lindex $matches 0]
02003         if {[llength $widgetOptions($opt)] == 1} {
02004             set opt $widgetOptions($opt)
02005         }
02006         }
02007     }
02008 
02009     return $opt
02010 
02011     } else {
02012     set choices [HumanizeList $list]
02013     error "ambiguous $object \"$opt\"; must be one of $choices"
02014     }
02015 }
02016 
02017 # ::combobox::HumanizeList --
02018 #
02019 #    Returns a human-readable form of a list by separating items
02020 #    by columns, but separating the last two elements with "or"
02021 #    (eg: foo, bar or baz)
02022 #
02023 # Arguments:
02024 #
02025 #    list    a valid tcl list
02026 #
02027 # Results:
02028 #
02029 #    A string which as all of the elements joined with ", " or
02030 #    the word " or "
02031 
02032 proc ::combobox::HumanizeList {list} {
02033 
02034     if {[llength $list] == 1} {
02035     return [lindex $list 0]
02036     } else {
02037     set list [lsort $list]
02038     set secondToLast [expr {[llength $list] -2}]
02039     set most [lrange $list 0 $secondToLast]
02040     set last [lindex $list end]
02041 
02042     return "[join $most {, }] or $last"
02043     }
02044 }
02045 
02046 # This is some backwards-compatibility code to handle TIP 44
02047 # (http://purl.org/tcl/tip/44.html). For all private tk commands
02048 # used by this widget, we'll make duplicates of the procs in the
02049 # combobox namespace.
02050 #
02051 # I'm not entirely convinced this is the right thing to do. I probably
02052 # shouldn't even be using the private commands. Then again, maybe the
02053 # private commands really should be public. Oh well; it works so it
02054 # must be OK...
02055 foreach command {TabToWindow CancelRepeat ListboxUpDown} {
02056     if {[llength [info commands ::combobox::tk$command]] == 1} break;
02057 
02058     set tmp [info commands tk$command]
02059     set proc ::combobox::tk$command
02060     if {[llength [info commands tk$command]] == 1} {
02061         set command [namespace which [lindex $tmp 0]]
02062         proc $proc {args} "uplevel $command \$args"
02063     } else {
02064         if {[llength [info commands ::tk::$command]] == 1} {
02065             proc $proc {args} "uplevel ::tk::$command \$args"
02066         }
02067     }
02068 }
02069 
02070 # end of combobox.tcl
02071 }
02072 
02073 /* END combobox*/
02074 /* */
02075 /* BEGIN procedures*/
02076 
02077 /*  Procedure appelee sur click sur bouton <NEXT>*/
02078 ret  {on_next_page} () {
02079 
02080     # Valide la page en cours
02081     if {[validate_page true]} {
02082         # Choisit la page suivante
02083         choose_next_page
02084         # Affiche la page
02085         display_page
02086     }
02087 }
02088 
02089 
02090 /*  Procedure appelee sur click sur bouton <PREVIOUS>*/
02091 ret  {on_previous_page} () {
02092 
02093     # Valide la page en cours
02094     validate_page false
02095     # Choisit la page precedente
02096     choose_previous_page
02097     # Affiche la page
02098     display_page
02099 }
02100 
02101 ret  {validate_page} (type if_, type valid_, type required) {
02102     global Type_page
02103     # Lit les donnees saisies dans la page
02104     switch $Type_page {
02105         0 { set valid [validate_application_page $if_valid_required] }
02106         1 { set valid [validate_component_page $if_valid_required] }
02107         2 { set valid [validate_field_page $if_valid_required] }
02108         3 { set valid 1 }
02109     }
02110     return $valid
02111 }
02112 
02113 /*  Affiche et initialise une page de saisie*/
02114 ret  {display_page} () {
02115     # Donnees de l'application
02116     global Data
02117     # Type de page d'ecran en cours :
02118     global Type_page
02119     # Numero de composant en cours de saisie
02120     global Num_composant
02121     # Numero de champ de couplage en cours de saisie
02122     global Num_champ
02123 
02124     # Des-affiche la page en cours
02125     pack forget .initial .comp_name .component .field .terminal
02126 
02127     # Affiche la page interessante
02128     array set page_names {0 .initial 1 .component 2 .field 3 .terminal}
02129     set page_name $page_names($Type_page)
02130     pack $page_name -after .intro -side top -expand 1
02131     
02132     # Si page de composant ou de champ
02133     if {$Type_page == 1 || $Type_page == 2} {
02134         # Affiche le titre du composant
02135         pack .comp_name -after .intro -side top -pady 5
02136     }
02137     
02138     # Initialise les donnees de la page
02139     switch $Type_page {
02140         0 { init_application_page }
02141         1 { init_component_page }
02142         2 { init_field_page }
02143         3 { init_terminal_page }
02144     }
02145     # Affiche les boutons de validation adequats
02146     switch $Type_page {
02147         0 {
02148             # C'est la premiere page
02149             pack forget .buttons.previous .buttons.ok
02150             pack .buttons.next -side right -expand 1 -padx 10
02151         }
02152         1 -
02153         2 {
02154             # Page intermediaire
02155             pack forget .buttons.ok
02156             pack .buttons.previous -side left -expand 1 -padx 10
02157             pack .buttons.next -side right -expand 1 -padx 10
02158         }
02159         3 {
02160             # Page terminale
02161             pack forget .buttons.next
02162             pack .buttons.previous -side left -expand 1 -padx 10
02163             pack .buttons.ok -side right -expand 1 -padx 10
02164         }
02165     }
02166 }
02167 
02168 
02169 /*  Determine le type de page a afficher */
02170 /*  apres appui sur le bouton <Next>*/
02171 ret  {choose_next_page} () {
02172     global Data Type_page Num_composant Num_champ
02173     switch $Type_page {
02174         0 { 
02175             # Page initiale
02176             # S'il y a au moins un composant dans l'appli
02177             if {[llength $Data(l_composants)] > 0} {
02178                 set Type_page 1
02179                 set Num_composant 0
02180             } else {
02181                 set Type_page 3
02182             }
02183         }
02184          1 {
02185             # Page initiale d'un composant
02186             # Nom du composant
02187             set nom_comp [lindex $Data(l_composants) $Num_composant]
02188             # S'il y a au moins un champ dans le composant
02189             if {[llength $Data(comp.$nom_comp.lchamps)]} {
02190                 set Type_page 2
02191                 set Num_champ 0
02192             } else {
02193                 # Passe au composant suivant ou a la page terminale
02194                 incr Num_composant
02195                 if { $Num_composant < [llength $Data(l_composants)]} {
02196                     set Type_page 1
02197                 } else {
02198                     set Type_page 3
02199                 }
02200             }
02201         }
02202         2 {
02203             # Page d'un champ d'un composant
02204             # Nom du composant
02205             set nom_comp [lindex $Data(l_composants) $Num_composant]
02206             # Si ce n'est pas le dernier champ dans le composant
02207             incr Num_champ
02208             if {$Num_champ < [llength $Data(comp.$nom_comp.lchamps)]} {
02209                 set Type_page 2
02210             } else {
02211                 # Passe au composant suivant ou a la page terminale
02212                 incr Num_composant
02213                 if { $Num_composant < [llength $Data(l_composants)]} {
02214                     set Type_page 1
02215                 } else {
02216                     set Type_page 3
02217                 }
02218             }
02219         }
02220 
02221     }
02222 }
02223 
02224 
02225 /*  Determine le type de page a afficher */
02226 /*  apres appui sur le bouton <Previous>*/
02227 ret  {choose_previous_page} () {
02228     global Data Type_page Num_composant Num_champ
02229 
02230     switch $Type_page {
02231         1 {
02232             # Page initiale d'un composant
02233             # Si c'est le premier composant
02234             if {$Num_composant == 0} {
02235                 set Type_page 0
02236             } else {
02237                 # Passe au composant precedent
02238                 incr Num_composant -1
02239                 # Nom du composant
02240                 set nom_comp [lindex $Data(l_composants) $Num_composant]
02241                 # Nombre de champs
02242                 set nb_champs [llength $Data(comp.$nom_comp.lchamps)]
02243                 if {$nb_champs > 0} {
02244                     # Passe au dernier champ du composant precedent
02245                     set Num_champ [expr $nb_champs - 1]
02246                     set Type_page 2
02247                 } else {
02248                     # Passe a la page initiale du composant precedent
02249                     set Type_page 1
02250                 }
02251             }
02252         }
02253         2 {
02254             # Page d'un champ d'un composant
02255             # Nom du composant
02256             set nom_comp [lindex $Data(l_composants) $Num_composant]
02257             # Si ce n'est pas le premier champ dans le composant
02258             incr Num_champ -1
02259             if {$Num_champ >= 0} {
02260                 set Type_page 2
02261             } else {
02262                 # Passe a la page initiale du composant
02263                 set Type_page 1
02264             }
02265         }
02266         3 { 
02267             # Page terminale
02268             # S'il y a au moins un composant dans l'appli
02269             set nb_composants [llength $Data(l_composants)]
02270             if {$nb_composants} {
02271                 set Num_composant [expr $nb_composants - 1]
02272                 set nom_comp [lindex $Data(l_composants) $Num_composant]
02273                 # S'il y a au moins un champs dans le dernier composant
02274                 set nb_champs [llength $Data(comp.$nom_comp.lchamps)]
02275                 if {$nb_champs > 0} {
02276                     # Passe au dernier champ du composant
02277                     set Num_champ [expr $nb_champs - 1]
02278                     set Type_page 2
02279                 } else {
02280                     # Passe a la page initiale du composant
02281                     set Type_page 1
02282                 }
02283             } else {
02284                 set Type_page 0
02285             }
02286         }
02287 
02288     }
02289 }
02290 
02291 /*  Cree les champs de saisie de la page initiale*/
02292 /*  Cette page s'appelle .initial*/
02293 ret  {create_application_page} () {
02294     global Data
02295     # Variables de saisie
02296     global start_mode coupling_mode
02297         
02298     labelframe .initial -text "Application main attributes"
02299     set w .initial
02300     set r 0
02301     
02302     #== Name
02303     label $w.label1 -text "Symbolic name : " -fg #ee3333
02304     entry $w.entry1 -textvariable Data(name) -width 20 -background white
02305     grid $w.label1 $w.entry1 -row $r -sticky w
02306     # Ajoute une bulle d'aide pour ce champ
02307     set help_text {Enter here the application name, which should match argument appl_name of PSMILe call prism_init}
02308     help_attach_balloon $w.entry1 $help_text
02309     incr r
02310     
02311     #== Long name
02312     label $w.label2 -text "Description : "
02313     entry $w.entry2 -textvariable Data(long_name) -width 35 -background white
02314     grid $w.label2 $w.entry2 -row $r -sticky w
02315     # Ajoute une bulle d'aide pour ce champ
02316     set help_text {a short description of the application}
02317     help_attach_balloon $w.entry2 $help_text
02318     incr r
02319     
02320     #== number of processes
02321     label $w.label3 -text "Number of processes : " -fg #ee3333
02322     frame $w.frrank
02323     trace variable Data(nb_procs_min) w {entry_forceRegexp {^[0-9]*$} }
02324     trace variable Data(nb_procs_min) w {entry_forceRegexp {^[0-9]*$} }
02325     trace variable Data(nb_procs_incr) w {entry_forceRegexp {^[0-9]*$} }
02326     pack [entry $w.frrank.e1 -textvariable Data(nb_procs_min) -width 10 -background white] -side left
02327     pack [label $w.frrank.l1 -text " to "] -side left -padx 2
02328     pack [entry $w.frrank.e2 -textvariable Data(nb_procs_max) -width 10 -background white] -side left -padx 2
02329     pack [label $w.frrank.l2 -text " increment "] -side left -padx 2
02330     pack [entry $w.frrank.e3 -textvariable Data(nb_procs_incr) -width 10 -background white] -side left -padx 2
02331     grid $w.label3 $w.frrank -row $r -sticky w
02332     # Ajoute une bulle d'aide pour ce groupe de champs
02333     set help_text {the total number range of processes the application can run on}
02334     help_attach_balloon $w.frrank $help_text
02335     incr r
02336     
02337     #== Start mode
02338     label $w.label4 -text "Start mode : " -fg #ee3333
02339     combobox_lim $w.cmb4 {spawn notspawn notspawn_or_spawn} {} start_mode 20 "" 1
02340     grid  $w.label4 $w.cmb4 -row $r -sticky w
02341     # Ajoute une bulle d'aide pour ce champ
02342     set help_text {the mode into which the application may be started.
02343 For the spawn approach, only the OASIS Driver should be started and a full MPI2 implementation is required as the Driver uses the MPI2 MPI_Comm_Spawn_Multiple functionality.
02344 If only MPI1 implementation is available, the Driver and the applications must be all started at once in the run script; this is the so-called not spawn approach (see Oasis User Guide, section: the driver part).}
02345     help_attach_balloon $w.cmb4 $help_text
02346     incr r
02347 
02348     #== Coupling mode
02349     label $w.label5 -text "Coupling mode : " -fg #ee3333
02350     combobox_lim $w.cmb5 {coupled standalone coupled_or_standalone} {} coupling_mode 20 "" 1
02351     grid  $w.label5 $w.cmb5 -row $r -sticky w
02352     # Ajoute une bulle d'aide pour ce champ
02353     set help_text {the mode into which the application may run}
02354     help_attach_balloon $w.cmb5 $help_text
02355     incr r
02356 
02357     #== Components
02358     label $w.label7 -text "Components : " -fg #ee3333
02359     grid  $w.label7 -row $r -column 0 -sticky nw -pady 10
02360     
02361     # une "scrollable frame" pour les "components"
02362     scrollform_create $w.components
02363     set form [scrollform_interior $w.components]
02364     $w.components.vport configure -height 140
02365     $form configure -borderwidth 1 -relief sunken -height 20
02366     grid [button $form.add -text "Add" -command "edited_list_add $form component"] -sticky w
02367     grid $w.components -row $r -column 1 -sticky w -pady 10
02368     # Ajoute une bulle d'aide pour ce champ
02369     set help_text {the list of components included in the application : each component name that you enter should match the argument comp_name of each PSMILe call prism_init_comp.
02370 In order to remove one item from the list, click on the right mouse button.}
02371     help_attach_balloon $w.components $help_text
02372     incr r
02373 }
02374 
02375 /*  Cree les champs de saisie de la page initiale d'un composant        */
02376 /*  Cette page s'appelle .component*/
02377 ret  {create_component_page} () {
02378     
02379     # Donnees associees aux champs de saisie
02380     global Comp_long_name
02381     global Comp_procs_min Comp_procs_max Comp_procs_incr
02382     global Comp_simul Comp_default
02383     
02384     labelframe .component -text "Component main attributes"
02385     set w .component
02386     set r 0
02387     
02388     #== Long name
02389     label $w.label2 -text "Description : "
02390     entry $w.entry2 -textvariable Comp_long_name -width 35 -background white
02391     grid $w.label2 $w.entry2 -row $r -sticky w
02392     # Ajoute une bulle d'aide pour ce champ
02393     set help_text {a short general description of the component model}
02394     help_attach_balloon $w.entry2 $help_text
02395     incr r
02396     
02397     #== Simulated
02398     label $w.label3 -text "Simulated : " -fg #ee3333
02399     set Comp_simul ""
02400     combobox_lim $w.cmb3 {ocean sea_ice ocean_biogeochemistry atmosphere atmospheric_chemistry land} {} Comp_simul 25 $Comp_simul -1
02401     grid  $w.label3 $w.cmb3 -row $r -sticky w
02402     # Ajoute une bulle d'aide pour ce champ
02403     set help_text {the simulated part of the climate system.
02404 if an application contains more than one component simulating the same part of the climate system, the user will have to choose among these components at coupling configuration time.}
02405     help_attach_balloon $w.cmb3 $help_text
02406     incr r
02407     
02408     #== Default
02409     label $w.label4 -text "Default component : " -fg #ee3333
02410     set Comp_default yes
02411     combobox_lim $w.cmb4 {yes no} {} Comp_default 10 $Comp_default 1
02412     grid  $w.label4 $w.cmb4 -row $r -sticky w
02413     # Ajoute une bulle d'aide pour ce champ
02414     set help_text {whether or not this component is always active in the application}
02415     help_attach_balloon $w.cmb4 $help_text
02416     incr r
02417     
02418     #== number of processes
02419     label $w.label5 -text "Number of processes : " -fg #ee3333
02420     frame $w.frrank
02421     trace variable Comp_procs_min  w {entry_forceRegexp {^[0-9]*$} }
02422     trace variable Comp_procs_max  w {entry_forceRegexp {^[0-9]*$} }
02423     trace variable Comp_procs_incr w {entry_forceRegexp {^[0-9]*$} }
02424     pack [entry $w.frrank.e1 -textvariable Comp_procs_min -width 10 -background white] -side left
02425     pack [label $w.frrank.l1 -text " to "] -side left -padx 2
02426     pack [entry $w.frrank.e2 -textvariable Comp_procs_max -width 10 -background white] -side left -padx 2
02427     pack [label $w.frrank.l2 -text " increment "] -side left -padx 2
02428     pack [entry $w.frrank.e3 -textvariable Comp_procs_incr -width 10 -background white] -side left -padx 2
02429     grid $w.label5 $w.frrank -row $r -sticky w
02430     # Ajoute une bulle d'aide pour ce groupe de champs
02431     set help_text {the total number range of processes on which the component can run}
02432     help_attach_balloon $w.frrank $help_text
02433     incr r
02434 
02435     # S�arateur
02436     frame $w.sep2 -width 200 -height 2 -borderwidth 1 -relief sunken
02437     grid  $w.sep2 -row $r -columnspan 2 -pady 15
02438     incr r
02439 
02440     #== Grids and periodicity
02441 
02442     label $w.label6 -text "Grids : " -fg #ee3333
02443     scrollform_create $w.grids
02444     set form [scrollform_interior $w.grids]
02445     button $w.insert_grid -text "Add" -command "grid_insert $form"
02446     grid  $w.label6 $w.insert_grid -row $r -sticky w
02447     incr r
02448 
02449     $w.grids.vport configure -height 80
02450     grid $w.grids -row $r -sticky w 
02451     # Ajoute une bulle d'aide pour ce groupe de champs
02452     set help_text {Enter all the grids possibles for a component and the direction of periodicity if there is ; the grid name must fit the argument of prism_def_grid}
02453     help_attach_balloon $w.grids $help_text
02454     incr r
02455     # une frame et un label pour le titre
02456     frame $w.fr2 
02457     grid $w.fr2 -row $r -sticky w -columnspan 4
02458     label $w.fr2.title1 -text "                                     Name               I periodic       J periodic       K periodic" -borderwidth 1 -justify left
02459     pack $w.fr2.title1 -anchor w
02460     incr r
02461     # une frame scrollable pour les "grilles"
02462     grid $w.grids -row $r -column 1 -sticky ew 
02463     grid $w.grids -row $r -sticky ew 
02464     $form configure -borderwidth 1 -relief sunken
02465     # insertion des boutons pour les grilles existantes
02466     incr r
02467 
02468     #== Fields
02469     label $w.label7 -text "Coupling fields : " -fg #ee3333
02470     grid  $w.label7 -row $r -column 0 -sticky nw -pady 10
02471     
02472     # une "scrollable frame" pour les "fields"
02473     scrollform_create $w.fields
02474     set form [scrollform_interior $w.fields]
02475     $w.fields.vport configure -height 140
02476     $form configure -borderwidth 1 -relief sunken -height 20
02477     grid [button $form.add -text "Add" -command "edited_list_add $form field"] -sticky w
02478     grid $w.fields -row $r -column 1 -sticky w -pady 10
02479     # Ajoute une bulle d'aide pour cette liste
02480     set help_text {Enter here all coupling/IO fields possibly received or provided by the component model from/to its external environment (another model or a disk file) through prism_get or prism_put calls.
02481 Each field name must match 2nd argument in the corresponding PSMILe call prism_def_var.
02482 
02483 In order to remove one item from the list, click on the right mouse button.}
02484     help_attach_balloon $w.fields $help_text
02485     incr r
02486 }
02487 
02488 
02489 ret  {grid_boutons_insert} (type form , type grid , type nb_, type grids) {
02490 # Ajoute une ligne au formulaire de saisie des grilles
02491     global Grid_names
02492     global Data Num_composant
02493     global OASIS_GUI_DIR
02494     
02495     # Ajoute une ligne
02496     set no_ligne $nb_grids
02497 
02498     # Nom du composant
02499     set nom_comp [lindex $Data(l_composants) $Num_composant]
02500     grid [entry $form.n$grid -textvariable Grid_names($nom_comp.grid.$grid.name) -width 10 -relief groove -background white -borderwidth 1] -row $no_ligne -column 0
02501     combobox_lim $form.pi$grid {true false} {} Grid_names($nom_comp.grid.$grid.periodi) 10 $Grid_names($nom_comp.grid.$grid.periodi) 1
02502     grid $form.pi$grid -row $no_ligne -column 1
02503     combobox_lim $form.pj$grid {true false} {} Grid_names($nom_comp.grid.$grid.periodj) 10 $Grid_names($nom_comp.grid.$grid.periodj) 1
02504     grid $form.pj$grid -row $no_ligne -column 2
02505     combobox_lim $form.pk$grid {true false} {} Grid_names($nom_comp.grid.$grid.periodk) 10 $Grid_names($nom_comp.grid.$grid.periodk) 1
02506     grid $form.pk$grid -row $no_ligne -column 3
02507     
02508     set file delete1.xbm
02509     grid [button  $form.delete$grid -bitmap  @[file join $OASIS_GUI_DIR IMAGES $file] -command "grid_boutons_delete $form $grid; grid_delete $grid" -background white -foreground red -relief groove  -borderwidth 1] -row $no_ligne -column 4
02510     focus $form.n$grid 
02511     bind $form.pk$grid <KeyPress-Return> "grid_boutons_next $form $grid"
02512     bind $form.pk$grid <KeyPress-Tab> "grid_boutons_next $form $grid"
02513 }
02514 
02515 ret  {grid_boutons_delete} (type form , type grid) {
02516 # Supprime une ligne du tableau des grilles pour l'appli
02517 # avant que la ligne ait ��supprim� en m�oire de la variable Grid_names
02518 #
02519     global Grid_names
02520     global Data Num_composant
02521 
02522     # Nom du composant
02523     set nom_comp [lindex $Data(l_composants) $Num_composant]
02524 
02525     array set info_grid [grid info $form.n$grid]
02526     set num_ligne $info_grid(-row)
02527     
02528     destroy $form.n$grid; destroy $form.pi$grid; destroy $form.pj$grid; destroy $form.pk$grid; destroy $form.delete$grid
02529 
02530     set grid_suivants [lrange $Grid_names($nom_comp.lgrid) [expr $num_ligne + 1] end]
02531     # Pour toutes les lignes suivantes
02532     set i $num_ligne
02533     foreach grid_suivant $grid_suivants {
02534         # Change le no de lignes des trois widgets
02535         grid configure $form.n$grid_suivant $form.pi$grid_suivant $form.pj$grid_suivant $form.pk$grid_suivant $form.delete$grid_suivant -row $i
02536         incr i
02537     }
02538 }
02539 
02540 ret  {grid_delete} (type grid) {
02541 # Supprime une grille de la liste pour une composante
02542     global Grid_names
02543     global Data Num_composant
02544 
02545     # Nom du composant
02546     set nom_comp [lindex $Data(l_composants) $Num_composant]
02547     
02548     # Supprime de la liste
02549     set Grid_names($nom_comp.lgrid) [lsearch -not -all -inline $Grid_names($nom_comp.lgrid) $grid]
02550     # Oublie ses parametres
02551     array unset Grid_names grid.$grid.*
02552 }
02553 
02554 ret  {grid_boutons_next} (type form , type grid) {
02555     global Grid_names
02556     global Data Num_composant
02557 
02558     # Nom du composant
02559     set nom_comp [lindex $Data(l_composants) $Num_composant]
02560     
02561     # Determine le no de ligne du bouton
02562     array set info_grid [grid info $form.n$grid]
02563     set num_ligne $info_grid(-row)
02564     
02565     # Si le bouton est en derniere ligne de la table
02566     if { [lindex [grid size $form] 1] == [expr $num_ligne + 1] } {
02567         grid_insert $form
02568     } else {
02569         set grid_suivant [lindex $Grid_names($nom_comp.lgrid) [expr $num_ligne + 1]]
02570         focus $form.n$grid_suivant
02571     }
02572 }
02573 
02574 ret  {grid_insert} (type form) {
02575 # Ajoute une nouvelle grille
02576     global Grid_names
02577     global Data Num_composant
02578 
02579     # Nom du composant
02580     set nom_comp [lindex $Data(l_composants) $Num_composant]
02581     
02582     set nb_grids [llength $Grid_names($nom_comp.lgrid)]
02583     # Si d�a des grilles sont listees
02584     if { $nb_grids != 0 } {
02585         # D�ermine le no du dernier identifiant de grille (id = num�o)
02586         set derniere_grid [lindex $Grid_names($nom_comp.lgrid) end]
02587         # Cr� un nouvel identifiant de grille
02588         set nouvelle_grid [expr $derniere_grid + 1]
02589     } else {
02590         set nouvelle_grid 1
02591     }
02592     # Ajoute a la liste
02593     lappend Grid_names($nom_comp.lgrid) $nouvelle_grid
02594     set Grid_names($nom_comp.grid.$nouvelle_grid.name) ""
02595     set Grid_names($nom_comp.grid.$nouvelle_grid.periodi) ""
02596     set Grid_names($nom_comp.grid.$nouvelle_grid.periodj) ""
02597     set Grid_names($nom_comp.grid.$nouvelle_grid.periodk) ""
02598 
02599     # Ajoute une ligne au formulaire
02600     grid_boutons_insert $form $nouvelle_grid $nb_grids
02601 }
02602 
02603 /*  Cree les champs de saisie de la page d'un champ de couplage*/
02604 /*  Cette page s'appelle .field*/
02605 ret  {create_field_page} () {
02606     # Donnees globales
02607     global CF_standard_name_list
02608     global Row_in_field_page
02609     # Donnees associees aux champs de saisie
02610     global Field_long_name Field_std_name Field_intent 
02611     global Field_req_but_change Field_gather_scatter 
02612     global Field_type Field_bundle_size Field_units Field_datatype
02613     global Field_period_value_1 Field_period_unit_1
02614     global Field_period_value_2 Field_period_unit_2
02615 
02616     # Initiqlise les champs de saisie de cette page    
02617     set Field_long_name ""
02618     set Field_type single
02619     set Field_bundle_size 1
02620     set Field_std_name ""
02621     #== bundle : component standard names
02622     for {set i 1} {$i < 11} {incr i} {
02623         global Field_comp_std_name_$i
02624         set Field_comp_std_name_$i ""
02625     }    
02626     set Field_intent ""
02627     set Field_req_but_change yes
02628     set Field_gather_scatter no
02629     set Field_units ""
02630     set Field_datatype ""
02631     set Field_period_value_1 1
02632     set Field_period_unit_1 ""
02633     set Field_period_value_2 1
02634     set Field_period_unit_2 ""
02635 
02636     labelframe .field -text "Coupling field attributes"
02637     set w .field
02638     set r 0
02639     
02640     #== Name
02641     label $w.label1 -text "Symbolic name : "
02642     label $w.label11 -relief sunken
02643     grid $w.label1 $w.label11 -row $r -sticky w
02644     incr r
02645     
02646     # Long name
02647     label $w.label2a -text "Description : "
02648     entry $w.entry2a -textvariable Field_long_name -width 35 -background white
02649     grid $w.label2a $w.entry2a -row $r -sticky w
02650     # Ajoute une bulle d'aide pour ce champ
02651     set help_text {a general description of the field}
02652     help_attach_balloon $w.entry2a $help_text
02653     incr r
02654     
02655     # Type : Single or bundle
02656     label $w.label3 -text "Type : " 
02657     frame $w.fr_type
02658     set Field_type single
02659     combobox_lim $w.fr_type.cmb {single bundle} CF_field_type_change Field_type 10 $Field_type 1
02660     # Ajoute une bulle d'aide pour ce champ
02661     set help_text {the coupling/IO field physical type}
02662     help_attach_balloon $w.fr_type.cmb $help_text
02663     pack $w.fr_type.cmb -side left 
02664     
02665     #== Bundle : number of components
02666     label $w.fr_type.label -text "size : " -fg #ee3333
02667     entry $w.fr_type.entry -textvariable Field_bundle_size -width 10 -background white    
02668     # Ajoute une bulle d'aide pour ce champ
02669     set help_text {the number of bundles}
02670     help_attach_balloon $w.fr_type.entry $help_text
02671     # pack $w.fr_type.label $w.fr_type.entry -side left -padx 5
02672     
02673     # Type et "Bundle size" sur la meme ligne
02674     grid  $w.label3 $w.fr_type -row $r -sticky w
02675     incr r
02676     
02677     #== Standard name
02678     label $w.label2 -text "CF Standard name : " -fg #ee3333
02679     set Field_std_name ""
02680     frame $w.stdname
02681     combobox_lim $w.stdname.cmb $CF_standard_name_list {} Field_std_name 35 $Field_std_name -1
02682     button $w.stdname.bt -text "Search list" -command "CF_search_list"
02683     pack $w.stdname.cmb -side left
02684     pack $w.stdname.bt -side left -padx 5
02685     grid $w.label2 $w.stdname -row $r -sticky w
02686     # Ajoute une bulle d'aide pour ce champ
02687     set help_text {the standard variable name following the CF convention (if it exists). This uniquely identifies the nature of the coupling/IO field. In case of bundle, more elements need to be specified (one for each bundle component)}
02688     help_attach_balloon $w.stdname.cmb $help_text
02689     incr r
02690 
02691     #== Bundle : other standard names
02692     set Row_in_field_page(other_std_names) $r
02693     for {set i 1} {$i < 11} {incr i} {
02694         label "$w.label_std$i" -text "  - name of bundle $i : " -fg #ee3333
02695         global Field_comp_std_name_$i
02696         set Field_comp_std_name_$i ""
02697         combobox_lim $w.cmb_std$i $CF_standard_name_list {} Field_comp_std_name_$i 35 "" -1
02698         # Ajoute une bulle d'aide pour ce champ
02699         set help_text {bundle name following the CF convention (if it exists)}
02700         help_attach_balloon $w.cmb_std$i $help_text
02701         incr r
02702     }
02703     # Execute une procedure a chaque changement de valeur
02704     trace variable Field_bundle_size w {entry_forceRegexp {^[0-9]*$} }
02705     trace variable Field_bundle_size w {CF_bundle_size_change}
02706 
02707     # intent : Input/Output
02708     label $w.label2c -text "Intent : " -fg #ee3333
02709     set Field_intent ""
02710     combobox_lim $w.cmb2c {input output {input/output}} {Field_intent_change} Field_intent 10 "" 1
02711     grid  $w.label2c $w.cmb2c -row $r -sticky w
02712     # Ajoute une bulle d'aide pour ce champ
02713     set help_text {Describes if the coupling/IO field may be exported, through PSMILe prism_put call, or imported, through a prism get_call, or both.}
02714     help_attach_balloon $w.cmb2c $help_text
02715     incr r
02716     
02717     # required
02718     set Row_in_field_page(required_but_changeable) $r
02719     label $w.label2d -text "Required : " 
02720     combobox_lim $w.cmb2d {yes no} {} Field_req_but_change 10 yes 1
02721     # on ne l'affiche pas systematiquement
02722     # grid  $w.label2d $w.cmb2d -row $r -sticky w
02723     # Ajoute une bulle d'aide pour ce champ
02724     set help_text {If input of the coupling/IO field (imported through a prism_get) is required in the component code, set this attriute to "yes".}
02725     help_attach_balloon $w.cmb2d $help_text
02726     incr r
02727 
02728     # Non masked value gathering/scattering
02729     label $w.label2e -text "Gathering/Scattering : "
02730     combobox_lim $w.cmb2e {yes no} {} Field_gather_scatter 10 no 1
02731     grid $w.label2e $w.cmb2e -row $r -sticky w
02732     # Ajoute une bulle d'aide pour ce champ
02733     set help_text {Non masked value gathering (input field) or scattering (output field).
02734 This attribute should be specified in the following conditions :
02735 
02736 - Input field : when the grid information transfered to the PSMILe covers the whole grid (masked points included), and when the array expected through prism_get API is a vector gathering only the non-masked points.
02737 If this attribute is set to "yes", gathering is performed automatically on an input field below the prism_get by the target PSMILe.
02738 
02739 - Output field : when grid information transfered to the PSMILe includes the masked points and when the array transfered to the prism_put API is a vector gathering only the non-masked points.
02740 If this attribute is set to "yes", scattering is performed automatically on an output field below the prism_put by the source PSMILe.}
02741     help_attach_balloon $w.cmb2e $help_text
02742     incr r
02743     
02744     # Units
02745     label $w.label4 -text "Units : " -fg #ee3333
02746     entry $w.entry4 -textvariable Field_units -width 20 -background white
02747     grid $w.label4 $w.entry4 -row $r -sticky w
02748     set help_text {the coupling/IO field physical units}
02749     help_attach_balloon $w.entry4 $help_text
02750     incr r
02751 
02752     # Datatype
02753     label $w.label6 -text "Numeric datatype : " -fg #ee3333
02754     set Field_datatype ""
02755     combobox_lim $w.cmb6 {real double integer} {} Field_datatype 10 $Field_datatype 1
02756     grid  $w.label6 $w.cmb6 -row $r -sticky w
02757     set help_text {the coupling/IO field numeric type}
02758     help_attach_balloon $w.cmb6 $help_text
02759     incr r
02760     
02761     #=== Minimal exchange period
02762     grid [label $w.label8 -text "Minimal exchange period : " -fg #ee3333] -row $r -column 0 -sticky w
02763 
02764     grid [frame $w.frexch1] -row $r -column 1 -sticky w
02765     trace variable Field_period_value_1 w {entry_forceRegexp {^[0-9]*$} }
02766     pack [entry $w.frexch1.en -takefocus 1 -textvariable Field_period_value_1 -width 10 -background white] -side left
02767     set liste_unites {seconds minutes hours days months years}
02768     combobox_lim $w.frexch1.cmb $liste_unites {} Field_period_unit_1 28 "" 1
02769     pack $w.frexch1.cmb -side left -padx 2
02770     set help_text {the period at which the prism_put or prism_get is called in the code (to define this period the developer may specify a number of seconds, minutes, hours, days, months, or years)}
02771     help_attach_balloon $w.frexch1 $help_text
02772     incr r
02773 
02774     #=== Minimal exchange period 2 (quand le champ est en input et output)
02775     set Row_in_field_page(min_period) $r
02776     label $w.label9 -text "Minimal period (Out) :    " -fg #ee3333
02777 
02778     frame $w.frexch2
02779     trace variable Field_period_value_2 w {entry_forceRegexp {^[0-9]*$} }
02780     pack [entry $w.frexch2.en -takefocus 1 -textvariable Field_period_value_2 -width 10 -background white] -side left
02781     combobox_lim $w.frexch2.cmb $liste_unites {} Field_period_unit_2 28 "" 1
02782     pack $w.frexch2.cmb -side left -padx 2
02783     set help_text {the period at which the prism_put is called in the code}
02784     help_attach_balloon $w.frexch2 $help_text
02785     # L'affichage de cette ligne est conditionnel    
02786     # grid $w.label9 $w.frexch2 -row $r -sticky w
02787     incr r
02788 
02789 }
02790 
02791 /*  Ecran terminal*/
02792 /* */
02793 /*  Cree les champs d'affichage de la page terminale*/
02794 /*  */
02795 ret  create_terminal_page () {
02796 
02797     labelframe .terminal -text "Validation"
02798     set w .terminal
02799     
02800     frame $w.fr
02801     label $w.fr.titre -justify left -text "Files to be created :" -font {helvetica -12 bold underline} 
02802     label $w.fr.titre2 -justify left -text " (Note: existing ones will be replaced) "
02803     pack $w.fr.titre $w.fr.titre2 -side left
02804     pack $w.fr -side top -anchor w -fill x -padx 10 -pady 5
02805     #== Liste des fichiers générés : text to be determined later
02806     label $w.label1 -anchor w -justify left -text ""  -relief sunken -bg white
02807     pack $w.label1 -side top -fill x -padx 15 -pady 5
02808     
02809     label $w.label2 -justify center -text "Please, click on OK to validate all the data you entered
02810 and generate the above XML files that describe the application and its components"
02811     pack $w.label2 -side top -fill x -padx 10 -pady 20
02812         
02813 }
02814 
02815 ret  {init_application_page} () {
02816     global Data
02817     global List_data
02818     # Variables de saisie
02819     global start_mode coupling_mode
02820 
02821     #== Start mode
02822     set start_mode $Data(start_mode)
02823     set indice [lsearch {spawn notspawn notspawn_or_spawn} $start_mode]
02824     if {$indice != -1} { .initial.cmb4 select $indice }
02825 
02826     #== Coupling mode
02827     set coupling_mode $Data(coupling_mode)
02828     set indice [lsearch {coupled standalone coupled_or_standalone} $coupling_mode]
02829     if {$indice != -1} { .initial.cmb5 select $indice }
02830 
02831     # Insertion des lignes pour les "components" existant
02832     # ---------------------------------------------------
02833 
02834     set form [scrollform_interior .initial.components]
02835     set nb_components 0
02836     set List_data(component.liste) {}
02837     foreach component_name $Data(l_composants) {
02838         set id_component $nb_components
02839         lappend List_data(component.liste) $id_component
02840         set List_data(component.$id_component) $component_name
02841         edited_list_widget_insert $form component $id_component $nb_components
02842         incr nb_components
02843     }
02844     # Ajoute une ligne vierge dans la liste des "components"
02845     edited_list_insert $form component
02846     # Met le focus sur le 1er champ
02847     focus .initial.entry1
02848 }
02849 
02850 /*  Initialise les donnees de la page initiale d'un composant        */
02851 ret  {init_component_page} () {
02852     # Donnees
02853     global Data Num_composant
02854     # Donnees associees aux champs de saisie
02855     global Comp_long_name
02856     global Comp_simul Comp_default
02857     global Comp_procs_min Comp_procs_max Comp_procs_incr
02858     global Grid_names
02859     # Donnees saisies dans une liste 
02860     global List_data
02861 
02862     
02863     # Nom du composant
02864     set nom_comp [lindex $Data(l_composants) $Num_composant]
02865 
02866     #== Affiche le nom du composant
02867     .comp_name.label11 configure -text $nom_comp
02868     #== Long name
02869     set Comp_long_name ""
02870     catch {set Comp_long_name $Data(comp.$nom_comp.long_name)}
02871     
02872     #== Simulated
02873     set Comp_simul ""
02874     catch {set Comp_simul $Data(comp.$nom_comp.simul)}
02875     set indice [lsearch {ocean sea_ice ocean_biogeochemistry atmosphere atmospheric_chemistry land} $Comp_simul]
02876     if {$indice != -1} { .component.cmb3 select $indice }
02877     
02878     #== Default
02879     set Comp_default ""
02880     catch {set Comp_default $Data(comp.$nom_comp.default)}
02881     set indice [lsearch {yes no} $Comp_default]
02882     if {$indice != -1} { .component.cmb4 select $indice }
02883     
02884     #== number of processes
02885     # Test le nombre de composants dans l'application
02886     if { [llength $Data(l_composants)] > 1 } {
02887         set Comp_procs_min  1
02888         set Comp_procs_max  1
02889         set Comp_procs_incr 1
02890     } else {
02891         set Comp_procs_min $Data(nb_procs_min)
02892         set Comp_procs_max $Data(nb_procs_max)
02893         set Comp_procs_incr $Data(nb_procs_incr)
02894     }
02895     
02896     catch {set Comp_procs_min  $Data(comp.$nom_comp.procs_min)}
02897     catch {set Comp_procs_max  $Data(comp.$nom_comp.procs_max)}
02898     catch {set Comp_procs_incr $Data(comp.$nom_comp.procs_incr)}
02899     
02900 
02901     # Insertion des lignes pour les "fields" existant
02902     # -----------------------------------------------
02903 
02904     set form [scrollform_interior .component.fields]
02905     set nb_fields 0
02906     set List_data(field.liste) {}
02907     foreach field_name $Data(comp.$nom_comp.lchamps) {
02908         set id_field $nb_fields
02909         lappend List_data(field.liste) $id_field
02910         set List_data(field.$id_field) $field_name
02911         edited_list_widget_insert $form field $id_field $nb_fields
02912         incr nb_fields
02913     }
02914     # Ajoute une ligne vierge dans la liste des "fields"
02915     edited_list_insert $form field
02916 
02917     # Insertion des lignes pour les "grilles" existantes
02918     # -----------------------------------------------
02919 
02920     set form [scrollform_interior .component.grids]
02921     set nb_grids 0
02922     foreach id_grid $Grid_names($nom_comp.lgrid) {
02923         grid_boutons_insert $form $id_grid $nb_grids
02924         incr nb_grids
02925     }
02926 }
02927 
02928 /*  Initialise les donnees de la page d'un champ de couplage*/
02929 ret  {init_field_page} () {
02930     # Donnees
02931     global Data Num_composant Num_champ
02932     # Donnees associees aux champs de saisie
02933     global Field_long_name Field_std_name Field_intent 
02934     global Field_req_but_change Field_gather_scatter 
02935     global Field_type Field_bundle_size Field_units Field_datatype
02936     global Field_period_value_1 Field_period_unit_1
02937     global Field_period_value_2 Field_period_unit_2
02938     
02939     # Nom du composant
02940     set nom_comp [lindex $Data(l_composants) $Num_composant]
02941     # Nom du champ
02942     set nom_champ [lindex $Data(comp.$nom_comp.lchamps) $Num_champ]
02943     # Clef d'acces aux attributs du champ
02944     set clef_champ comp.$nom_comp.field.$nom_champ
02945 
02946     #== Affiche le nom du composant
02947     .comp_name.label11 configure -text $nom_comp
02948     #== Affiche le nom du champ
02949     .field.label11 configure -text $nom_champ
02950     # Long name
02951     catch {set Field_long_name $Data($clef_champ.long_name)}    
02952     
02953     # Single or bundle
02954     catch {set Field_type $Data($clef_champ.type)}
02955     set indice [lsearch {single bundle} $Field_type]
02956     if {$indice != -1} { .field.fr_type.cmb select $indice }
02957     # Bundle size
02958     catch {set Field_bundle_size $Data($clef_champ.bundle_size)}
02959     
02960     #== Standard name
02961     catch {set Field_std_name $Data($clef_champ.std_name)}
02962     #== bundle : component standard names
02963     for {set i 1} {$i < 11} {incr i} {
02964         global Field_comp_std_name_$i
02965         catch {set Field_comp_std_name_$i $Data($clef_champ.std_name_$i)}
02966     }    
02967     
02968     # intent : Input/Output
02969     catch {set Field_intent $Data($clef_champ.intent)}
02970     set indice [lsearch {input output {input/output}} $Field_intent]
02971     if {$indice != -1} { .field.cmb2c select $indice }
02972     
02973     # required_but_changeable
02974     catch {set Field_req_but_change $Data($clef_champ.req_but_change)}
02975     set indice [lsearch {yes no} $Field_req_but_change]
02976     if {$indice != -1} { .field.cmb2d select $indice }
02977     
02978     # Non masked value gathering/scattering
02979     catch {set Field_gather_scatter $Data($clef_champ.gather_scatter)}
02980     set indice [lsearch {yes no} $Field_gather_scatter]
02981     if {$indice != -1} { .field.cmb2e select $indice }
02982     
02983     # Units
02984     catch {set Field_units $Data($clef_champ.units)}
02985 
02986     # Datatype
02987     catch {set Field_datatype $Data($clef_champ.datatype)}
02988     set indice [lsearch {real double integer} $Field_datatype]
02989     if {$indice != -1} { .field.cmb6 select $indice }
02990 
02991     #=== Minimal exchange period
02992     if {$Field_intent == "output"} {
02993         set direction out
02994     } else {
02995         set direction in
02996     }
02997     catch {set Field_period_value_1 $Data($clef_champ.period_value_$direction)}
02998 
02999     catch {set Field_period_unit_1 $Data($clef_champ.period_unit_$direction)}
03000     set indice [lsearch {seconds minutes hours days months years} $Field_period_unit_1]
03001     if {$indice != -1} { .field.frexch1.cmb select $indice }
03002 
03003     #=== Minimal exchange period OUTPUT (quand le champ est en Input et Output)
03004     if {$Field_intent == "input/output"} {
03005         catch {set Field_period_value_2 $Data($clef_champ.period_value_out)}
03006     
03007         catch {set Field_period_unit_2 $Data($clef_champ.period_unit_out)}
03008         set indice [lsearch {seconds minutes hours days months years} $Field_period_unit_2]
03009         if {$indice != -1} { .field.frexch2.cmb select $indice }
03010     }
03011 
03012     # Met a jour les champs de saisie en fct du type single/bundle
03013     CF_field_type_change .field.fr_type.cmb $Field_type
03014     # Met a jour les champs de saisie en fct du sens Entree/Sortie 
03015     Field_intent_change .field.cmb2c $Field_intent
03016  }
03017 
03018 /*  Valide les champs de saisie de la page initiale*/
03019 ret  {validate_application_page} (type if_, type valid_, type required) {
03020     global Data
03021     global List_data
03022     # Variables de saisie
03023     global start_mode coupling_mode
03024     global Grid_names
03025     
03026     if {$if_valid_required == "true"} {
03027         # Si aucun nom n'a été saisi
03028         if {$Data(name) == ""} {
03029             dialog_notice_show "Please, give a symbolic name to this application."
03030             focus .initial.entry1
03031             # Indique une erreur
03032             return 0
03033         }
03034         # Si le champ "start mode" ou "coupling mode" n'a pas été rempli
03035         if {$start_mode == "" || $coupling_mode == ""} {
03036             dialog_notice_show "Please, fill this field : it is mandatory."
03037             if {$start_mode == ""} {
03038                 focus .initial.cmb4
03039             } else {
03040                 focus .initial.cmb5
03041             }
03042             # Indique une erreur
03043             return 0
03044         }
03045     }   
03046     
03047     # Mémorise les données saisies dans les listes déroulantes
03048     set Data(start_mode) $start_mode
03049     set Data(coupling_mode) $coupling_mode
03050 
03051     # Recopie la liste des composants saisis
03052     set Data(l_composants) {}
03053     foreach component_id $List_data(component.liste) {
03054         # Si ligne pas vide
03055         set component_name [string trim $List_data(component.$component_id)]
03056         if {$component_name != ""} {
03057             lappend Data(l_composants) $component_name
03058         }
03059     }
03060     
03061     # Supprime les champs de saisie de cette liste
03062     set form [scrollform_interior .initial.components]
03063     foreach component_id $List_data(component.liste) {
03064         destroy $form.n$component_id
03065     }
03066 
03067     # Initialise la liste des champs des nouveaux composants
03068     foreach component_name $Data(l_composants) {
03069         if {! [info exists Data(comp.$component_name.lchamps)]} {
03070             set Data(comp.$component_name.lchamps) {}
03071             # definit un seule ligne vierge de grille pour chaque composant
03072         set Grid_names($component_name.lgrid) "1"
03073         set Grid_names($component_name.grid.1.name) ""
03074         set Grid_names($component_name.grid.1.periodi) ""
03075         set Grid_names($component_name.grid.1.periodj) ""
03076         set Grid_names($component_name.grid.1.periodk) ""
03077         }
03078     }
03079     # Pas d'erreur
03080     return 1
03081 }
03082 
03083 /*  Initialise les champs affiches dans la page terminale*/
03084 ret  {init_terminal_page} () {
03085     global Data
03086     
03087     set file_list {}
03088     set AD_file_name $Data(name)_ad.xml
03089     lappend file_list $AD_file_name
03090     
03091     # Pour tous les composants de l'appli
03092     foreach nom_comp $Data(l_composants) {
03093         # Nom du fichier PMIOD : dérivé du nom de l'appli et du composant
03094         set nom_fichier_PMIOD "$Data(name)_${nom_comp}_pmiod.xml"
03095         lappend file_list $nom_fichier_PMIOD
03096     }
03097     
03098     .terminal.label1 configure -text [join $file_list "\n"]
03099 }
03100 
03101 /*  Valide les champs de saisie de la page initiale d'un composant*/
03102 ret  {validate_component_page} (type if_, type valid_, type required) {
03103     # Donnees
03104     global Data Num_composant Num_champ
03105     # Donnees associees aux champs de saisie
03106     global Comp_long_name
03107     global Comp_simul Comp_default
03108     global Comp_procs_min Comp_procs_max Comp_procs_incr
03109     # Donnees saisies dans une liste 
03110     global List_data
03111     global Grid_names
03112     
03113     # Nom du composant
03114     set nom_comp [lindex $Data(l_composants) $Num_composant]
03115     
03116     if {$if_valid_required == "true"} {
03117         # Si le champ "simulated" ou "default" n'a pas été rempli
03118         if {$Comp_simul == "" || $Comp_default == ""} {
03119             dialog_notice_show "Please, fill this field : it is mandatory."
03120             if {$Comp_simul == ""} {
03121                 focus .component.cmb3
03122             } else {
03123                 focus .component.cmb4
03124             }
03125             # Indique une erreur
03126             return 0
03127         }
03128         # Verifie les noms des champs de couplage
03129         foreach field_id $List_data(field.liste) {
03130             set field_name [string trim $List_data(field.$field_id)]
03131             # Si le nom est mal saisi
03132             if {! [string is wordchar $field_name]} {
03133                 dialog_notice_show "Coupling field name must be one word using only alphanumeric or underscore characters."
03134                 # Met le focus sur le champ de saisie en faute
03135                 set form [scrollform_interior .component.fields]
03136                 focus $form.n$field_id
03137                 return 0
03138             }
03139         }
03140         # Verifie les noms des grilles et leurs attributs de periodicite
03141         foreach grid_id $Grid_names($nom_comp.lgrid) {
03142             set grid_name [string trim $Grid_names($nom_comp.grid.$grid_id.name)]
03143             # Si le nom est mal saisi
03144             if {! [string is wordchar $grid_name]} {
03145                 dialog_notice_show "Grid name must be one word using only alphanumeric or underscore characters."
03146                 # Met le focus sur le champ de saisie en faute
03147                 set form [scrollform_interior .component.grids]
03148                 focus $form.n$grid_id
03149                 return 0
03150             }
03151             
03152             # Si il y a un nom de grille saisi
03153             if {$grid_name != ""} {
03154 
03155                 # Verifie si un des champs "periodic" en i,j ou k n'est pas rempli
03156                 set form [scrollform_interior .component.grids]
03157                 if {$Grid_names($nom_comp.grid.$grid_id.periodi) == ""} {
03158                     set champ_fautif $form.pi$grid_id
03159                 } else {
03160                     if {$Grid_names($nom_comp.grid.$grid_id.periodj) == ""} {
03161                         set champ_fautif $form.pj$grid_id
03162                     } else {
03163                         if {$Grid_names($nom_comp.grid.$grid_id.periodk) == ""} {
03164                             set champ_fautif $form.pk$grid_id
03165                         } else {
03166                             set champ_fautif ""
03167                         }
03168                     }
03169                 }
03170                 
03171                 if { $champ_fautif != ""} {
03172                     dialog_notice_show "Please, fill in the grid periodicity."
03173                     # Met le focus sur le champ de saisie en faute
03174                     focus $champ_fautif
03175                     return 0
03176                 }
03177             }
03178         }
03179     }   
03180     
03181     # Recopie en memoire les donnees saisies : les attributs du composant
03182     foreach name {long_name simul default procs_min procs_max procs_incr} {
03183         set Data(comp.$nom_comp.$name) [set Comp_$name]
03184     }
03185 
03186     # Recopie la liste des "fields" saisies
03187     set Data(comp.$nom_comp.lchamps) {}
03188     foreach field_id $List_data(field.liste) {
03189         # Si ligne pas vide
03190         set field_name [string trim $List_data(field.$field_id)]
03191         if {$field_name != ""} {
03192             lappend Data(comp.$nom_comp.lchamps) $field_name
03193         }
03194     }
03195     
03196     # Supprime les champs de saisie de cette liste
03197     set form [scrollform_interior .component.fields]
03198     foreach field_id $List_data(field.liste) {
03199         destroy $form.n$field_id
03200     }
03201 
03202     # Supprime la liste des grilles
03203     set form [scrollform_interior .component.grids]
03204     foreach grid_id $Grid_names($nom_comp.lgrid) {
03205         destroy $form.n$grid_id $form.pi$grid_id $form.pj$grid_id $form.pk$grid_id $form.delete$grid_id
03206     }
03207     
03208     # Initialise la liste des dependences des nouveaux champs
03209     foreach field_name $Data(comp.$nom_comp.lchamps) {
03210         if {! [info exists Data(comp.$nom_comp.field.$field_name.l_dependents)]} {
03211             set Data(comp.$nom_comp.field.$field_name.l_dependents) {}
03212         }
03213     }
03214     # Pas d'erreur
03215     return 1
03216 }
03217 
03218 /*  Valide les champs de saisie de la page d'un champ de couplage*/
03219 ret  {validate_field_page} (type if_, type valid_, type required) {
03220     # Donnees
03221     global Data Num_composant Num_champ
03222     # Donnees associees aux champs de saisie
03223     global Field_long_name Field_std_name Field_intent 
03224     global Field_req_but_change Field_gather_scatter 
03225     global Field_type Field_bundle_size Field_units Field_datatype
03226     global Field_period_value_1 Field_period_unit_1
03227     global Field_period_value_2 Field_period_unit_2
03228 
03229     # Validation
03230     # ----------
03231     
03232     if {$if_valid_required == "true"} {
03233         set valid 1
03234         
03235         # Si le champ "CF standard name" n'a pas été rempli
03236         if {$Field_std_name == ""} {
03237             set field_name .field.stdname.cmb
03238             set valid 0
03239         } else {
03240             # Si le champ "intent" n'a pas été rempli
03241             if {$Field_intent == ""} {
03242                 set field_name .field.cmb2c
03243                 set valid 0
03244             } else {
03245                 # Si le champ "numeric datatype" n'a pas été rempli
03246                 if {$Field_datatype == ""} {
03247                     set field_name .field.cmb6
03248                     set valid 0
03249                 } else {
03250                     # Si le champ "minimal period" n'a pas été rempli
03251                     if {$Field_period_unit_1 == ""} {
03252                         set field_name .field.frexch1.cmb
03253                         set valid 0
03254                     } else {
03255                     }
03256                 }
03257             }
03258         }
03259         
03260         # Si pas d'erreur et le champ de couplage est "bundle"
03261         if {$valid && $Field_type == "bundle"} {
03262             # Pour tous les elements du "bundle"
03263             for {set i 1} {$i <= $Field_bundle_size} {incr i} {
03264                 global Field_comp_std_name_$i
03265                 # Si le champ "CF standard name" n'a pas été rempli
03266                 if {[set Field_comp_std_name_$i] == ""} {
03267                     set field_name ".field.cmb_std$i"
03268                     set valid 0
03269                     break
03270                 }
03271             }
03272         }
03273         
03274         # Si pas d'erreur et le champ de couplage est a la fois "input" et "output"
03275         if {$valid && $Field_intent == "input/output"} {
03276             # Si le deuxieme champ "minimal period" n'a pas été rempli
03277             if {$Field_period_value_2  == "" || $Field_period_unit_2 == ""} {
03278                 if {$Field_period_value_2  == ""} {
03279                     set field_name .field.frexch2.en
03280                 } else {
03281                     set field_name .field.frexch2.cmb
03282                 }
03283                 set valid 0
03284             }
03285         }
03286         
03287         # Si un des champs obligatoires n'a pas été rempli
03288         if {! $valid} {
03289             dialog_notice_show "Please, fill this field : it is mandatory."
03290             focus $field_name
03291             # Indique une erreur
03292             return 0
03293         }
03294     }   
03295     
03296     # Lecture des champs saisis a l'ecran
03297     # -----------------------------------
03298     
03299     # Nom du composant
03300     set nom_comp [lindex $Data(l_composants) $Num_composant]
03301     # Nom du champ
03302     set nom_champ [lindex $Data(comp.$nom_comp.lchamps) $Num_champ]
03303     # Clef d'acces aux attributs du champ
03304     set clef_champ comp.$nom_comp.field.$nom_champ
03305 
03306     # Recopie en memoire les champs saisis
03307     foreach name { long_name std_name intent gather_scatter type units datatype } {
03308         set Data($clef_champ.$name) [set Field_$name]
03309     }
03310     
03311     # required_but_changeable
03312     if {$Field_intent != "output"} {
03313         set Data($clef_champ.req_but_change) $Field_req_but_change
03314     }
03315     
03316     #== bundle : Recopie en memoire les "standard names" des composantes
03317     if {$Field_type == "bundle"} {
03318         # Bundle size
03319         set Data($clef_champ.bundle_size) $Field_bundle_size
03320         
03321         for {set i 1} {$i <= $Field_bundle_size} {incr i} {
03322             global Field_comp_std_name_$i
03323             catch {set Data($clef_champ.std_name_$i) [set Field_comp_std_name_$i]}
03324         }
03325     }
03326 
03327     #=== Minimal exchange period
03328     if {$Field_intent == "output"} {
03329         set direction out
03330     } else {
03331         set direction in
03332     }
03333     set Data($clef_champ.period_value_$direction) $Field_period_value_1
03334     set Data($clef_champ.period_unit_$direction)  $Field_period_unit_1
03335     
03336     #=== Minimal exchange period OUTPUT (quand le champ est en Input et Output)
03337     if {$Field_intent == "input/output"} {
03338         set Data($clef_champ.period_value_out) $Field_period_value_2
03339         set Data($clef_champ.period_unit_out)  $Field_period_unit_2
03340     }
03341     # Pas d'erreur
03342     return 1
03343 }
03344 
03345 /*  Field_intent_change*/
03346 /* */
03347 /*  Procedure appelee quand l'utilisateur change le sens I/O d'un champ de couplage*/
03348 /* */
03349 ret  {Field_intent_change} (type win , type value) {
03350     global Row_in_field_page
03351     global Field_intent
03352 
03353     # Si le champ de couplage est en Entree
03354     if {$Field_intent != "output"} {
03355         # Affiche une ligne supplementaire de saisie pour "required_but_changeable"
03356         grid .field.label2d .field.cmb2d -row $Row_in_field_page(required_but_changeable) -sticky w
03357     } else {
03358         # Cache cette ligne supplementaire
03359         grid forget .field.label2d .field.cmb2d 
03360     }
03361 
03362     # Si le champ de couplage est en Entree et en Sortie
03363     if {$Field_intent == "input/output"} {
03364         # Affiche une ligne supplementaire de saisie pour "Minimal period (Out)"
03365         grid .field.label9 .field.frexch2 -row $Row_in_field_page(min_period) -sticky w
03366         # Renomme la !ere ligne de saisie
03367         .field.label8 configure -text "Minimal period (In) :"
03368     } else {
03369         # Cache une ligne supplementaire de saisie
03370         grid forget .field.label9 .field.frexch2
03371         # Renomme la !ere ligne de saisie
03372         .field.label8 configure -text "Minimal exchange period : "
03373     }
03374 }
03375 
03376 ret  {edited_list_widget_delete} (type form , type tag , type num_, type elem) {
03377 # Supprime une ligne du tableau contenu dans la fenetre $form
03378 # avant que la ligne ait été supprimée en mémoire de la variable $List_data(tag)
03379 #
03380     global List_data
03381     
03382     array set info_liste [grid info $form.n$num_elem]
03383     set num_ligne $info_liste(-row)
03384     
03385     destroy $form.n$num_elem
03386 
03387     set elems_suivants [lrange $List_data($tag.liste) [expr $num_ligne + 1] end]
03388     # Pour toutes les lignes suivantes
03389     set i $num_ligne
03390     foreach elem_suivant $elems_suivants {
03391         # Change le no de lignes des widgets de cette ligne (decale la ligne vers le haut)
03392         grid configure $form.n$elem_suivant -row $i
03393         incr i
03394     }
03395     # Renumerote la ligne contenant le bouton <Add>
03396     grid configure $form.add -row $i
03397 }
03398 
03399 ret  {edited_list_widget_insert} (type form , type tag , type num_, type elem , type nb_, type elems) {
03400     # Ajoute une ligne au formulaire de saisie d'une liste d'elements (indiquee par $tag)
03401     global List_data
03402     global OASIS_GUI_DIR
03403     
03404     # Ajoute une ligne
03405     set no_ligne $nb_elems
03406     
03407     grid forget $form.add
03408     grid [entry $form.n$num_elem -textvariable List_data($tag.$num_elem) -width 20 -relief groove -background white -borderwidth 1] -row $no_ligne -column 0
03409     grid $form.add -sticky w
03410     
03411     # Scroll canvas to the bottom
03412     set canvas_name [string range $form 0 end-5]
03413     $canvas_name yview scroll 1 pages
03414     
03415     bind $form.n$num_elem <KeyPress-Return> "edited_list_widget_next $form $tag $num_elem"
03416     bind $form.n$num_elem <KeyPress-Tab> "edited_list_widget_next $form $tag $num_elem"
03417     
03418     # Crée un menu popup pour supprimer plus tard cette ligne, si besoin
03419     menu $form.n$num_elem.popup
03420     $form.n$num_elem.popup add command -label "delete" -command "edited_list_widget_delete $form $tag $num_elem; edited_list_delete $tag $num_elem"
03421     # Le menu sera appelé sur "clic droit"
03422     bind $form.n$num_elem <ButtonPress-3> "tk_popup $form.n$num_elem.popup %X %Y"
03423 
03424 }
03425 
03426 ret  {edited_list_widget_next} (type form , type tag , type num_, type elem) {
03427     global List_data
03428 
03429     # Determine le no de ligne du bouton
03430     array set info_liste [grid info $form.n$num_elem]
03431     set num_ligne $info_liste(-row)
03432     # Si le bouton est en derniere ligne de la table
03433     if { [lindex [grid size $form] 1] == [expr $num_ligne + 2] } {
03434         # Ajoute une ligne à la table
03435         set nouvel_elem [edited_list_insert $form $tag]
03436         # Met le curseur dans cette ligne ajoutée
03437         focus $form.n$nouvel_elem
03438     } else {
03439         set elem_suivant [lindex $List_data($tag.liste) [expr $num_ligne + 1]]
03440         focus $form.n$elem_suivant
03441     }
03442 }
03443 
03444 ret  {edited_list_add} (type form , type tag) {
03445     # Ajoute une ligne en fin de table
03446     set nouvel_elem [edited_list_insert $form $tag]
03447     # Met le curseur dans cette ligne ajoutée
03448     focus $form.n$nouvel_elem
03449 }
03450 
03451 ret  {edited_list_delete} (type tag , type num_, type elem) {
03452 # Supprime un element d'une liste
03453     global List_data
03454     
03455     # Supprime de la liste
03456     set List_data($tag.liste) [lsearch -not -all -inline $List_data($tag.liste) $num_elem]
03457     # Oublie ses parametres
03458     array unset List_data $tag.$num_elem
03459 }
03460 
03461 ret  {edited_list_insert} (type form , type tag) {
03462 # Ajoute une nouvel element a une liste indiquee par $tag
03463 # Retourne le numéro de l'élément ajouté
03464     global List_data
03465     
03466     set nb_elems [llength $List_data($tag.liste)]
03467     # Si déja des elements sont listés
03468     if { $nb_elems != 0 } {
03469         # Détermine le no du dernier element
03470         set dernier_num_elem [lindex $List_data($tag.liste) end]
03471         # Crée un nouvel identifiant (nouveau numero d'element)
03472         set nouvel_elem [expr $dernier_num_elem + 1]
03473     } else {
03474         set nouvel_elem 1
03475     }
03476     # Ajoute a la liste
03477     lappend List_data($tag.liste) $nouvel_elem
03478     set List_data($tag.$nouvel_elem) ""
03479 
03480     # Ajoute une ligne au formulaire
03481     edited_list_widget_insert $form $tag $nouvel_elem $nb_elems
03482     
03483     return $nouvel_elem
03484 }
03485 
03486 
03487 ret  {entry_forceRegexp} (type regexp , type name , type el , type op) {
03488 global $name ${name}_regexp
03489     if [string comp {} $el] {
03490     set old  ${name}_regexp\($el)
03491     set name $name\($el)
03492     } else { set old ${name}_regexp }
03493     if ![regexp $regexp [set $name]] {
03494     set $name [set $old]
03495     bell; return
03496     }
03497     set $old [set $name]
03498 }
03499 
03500 ret  {combobox_lim} (type window , optional loclist ={) {cmdproc {}} {cb_textvar c_var} {cb_width 15} {cb_opt 0} {lim 1} args} {
03501 /* ****combobox_lim*/
03502 /* */
03503 /* */
03504 /* */
03505 /* ***/
03506 /*    window : nom du combobox*/
03507 /*    procedure : procedure pour creer la liste de choix*/
03508 /*    cmdproc : commande executee a chaque nouveau choix (passe aussi le nom du combo)*/
03509 /*    cb_textvar : nom de la variable*/
03510 /*    cb_width taille en char du combo*/
03511 /*    cb_opt : valeur par defaut*/
03512 /*    lim : 1 saisie limitée (par defaut) -1 sinon*/
03513 /*    args : arguments de la procedure cmdproc*/
03514 
03515     catch {namespace import combobox::*}
03516      edita =  1
03517     if {$lim == 1} { edita =  0}
03518     upvar /* 0 $cb_textvar textvar*/
03519      textvar =  $cb_opt
03520     combobox $window -editable $edita -textvariable  $cb_textvar -width $cb_width -background white
03521     /*  eval $window list insert end $loclist*/
03522     foreach elem $loclist {
03523         $window list insert end $elem
03524     }
03525     /*  Sélectionne la valeur initiale dans la liste*/
03526      indice =  [lsearch $loclist $cb_opt]
03527     if {$indice != -1} { $window select $indice }
03528     /*  Complete la configuratiom*/
03529     $window configure -command $cmdret  
03530 }
03531 
03532 proc (type scrollform_, type create) {win} {
03533 /*  ----------------------------------------------------------------------*/
03534 /*   USAGE:  scrollform_resize <win>*/
03535 /* =[*/
03536 /*   Used internally to handle size changes in the form area within*/
03537 /*   a scrollform assembly.  Updates the canvas to recognize the new*/
03538 /*   scrolling area.*/
03539 /*  ----------------------------------------------------------------------*/
03540 ret  scrollform_resize (type win) {
03541     set bbox [$win.vport bbox all]
03542     
03543     set wid [winfo width $win.vport.form]
03544     $win.vport configure -width $wid -scrollregion $bbox -yscrollincrement 0.1i
03545 }
03546 
03547 /*  ----------------------------------------------------------------------*/
03548 /*   USAGE:  scrollform_interior <win>*/
03549 /* */
03550 /*   Returns the name of the interior frame that represents the*/
03551 /*   body of the scrollform.  Widgets should be packed in this*/
03552 /*   frame to build the form.*/
03553 /*  ----------------------------------------------------------------------*/
03554 ret  scrollform_interior (type win) {
03555     return "$win.vport.form"
03556 }
03557 
03558 /*  ----------------------------------------------------------------------*/
03559 /*   EXAMPLE: use the canvas to build a scrollable form*/
03560 /*  ----------------------------------------------------------------------*/
03561 /*   Effective Tcl/Tk Programming*/
03562 /*     Mark Harrison, DSC Communications Corp.*/
03563 /*     Michael McLennan, Bell Labs Innovations for Lucent Technologies*/
03564 /*     Addison-Wesley Professional Computing Series*/
03565 /*  ======================================================================*/
03566 /*   Copyright (c) 1996-1997  Lucent Technologies Inc. and Mark Harrison*/
03567 /*  ======================================================================*/
03568 
03569 /*  ----------------------------------------------------------------------*/
03570 /*   USAGE:  scrollform_create <win>*/
03571 /* */
03572 /*   Creates an empty scrollform assembly.  The interior frame for this*/
03573 /*   form can be found by calling "scrollform_interior".  Widgets packed*/
03574 /*   into the interior can be scrolled in the vertical direction.*/
03575 /*  ----------------------------------------------------------------------*/
03576 frame $win -class Scrollform
03577 
03578 scrollbar $win.sbar -command "$win.vport yview"
03579 pack $win.sbar -side right -fill y
03580 
03581 canvas $win.vport -yscrollcommand "$win.sbar "
03582 pack =  $win.vport -side left -fill both -expand true
03583 
03584 frame $win.vport.form
03585 $win.vport create window 0 0 -anchor nw -window $win.vport.form
03586 
03587 bind $win.vport.form <Configure> "scrollform_resize $win"
03588 
03589 return $win
03590 }
03591 
03592 /*  CF_search_list*/
03593 /* */
03594 /*  Cherche dans la liste des noms standards CF*/
03595 /*  un nom saisi au clavier dans le champ de saisie "Field_std_name"*/
03596 /* */
03597 ret  {CF_search_list} () {
03598     # Liste des noms std
03599     global CF_standard_name_list
03600     # Donnee associee au champ de saisie
03601     global Field_std_name
03602     
03603     # Sélectionne la valeur initiale dans la liste
03604     set indice [lsearch -glob $CF_standard_name_list "*${Field_std_name}*"]
03605     if {$indice != -1} { .field.stdname.cmb select $indice }
03606 }
03607 
03608 /*  CF_field_type_change*/
03609 /* */
03610 /*  Procedure appelee chaque fois qu'on change de type de champ de couplage :*/
03611 /*    qui est de type Single ou Bundle*/
03612 /* */
03613 /*  Single : il n'y a qu'un seul nom CF*/
03614 /*  Bundle : il y en a un nombre indefini*/
03615 /* */
03616 ret  {CF_field_type_change} (type win , type value) {
03617     global Row_in_field_page
03618     global Field_type Field_bundle_size
03619     
03620     switch $value {
03621         single {
03622             # N'affiche pas le champ de saisie de la taille du "bundle"
03623             pack forget .field.fr_type.label .field.fr_type.entry
03624             # N'affiche pas les champs des composantes de "bundle"
03625             for {set i 1} {$i < 11} {incr i} {
03626                 grid forget .field.label_std$i .field.cmb_std$i
03627             }
03628         }
03629         bundle {
03630             # Affiche le champ de saisie de la taille du "bundle"
03631             pack .field.fr_type.label .field.fr_type.entry -side left -padx 5
03632 
03633             # Affiche les champs de saisie des composantes du "bundle"
03634             set r $Row_in_field_page(other_std_names)
03635             for {set i 1} {$i <= $Field_bundle_size} {incr i} {
03636                 grid .field.label_std$i .field.cmb_std$i -row $r -sticky w
03637                 incr r
03638             }
03639             # N'affiche pas les champs des autres composantes
03640             for {} {$i < 3} {incr i} {
03641                 grid forget .field.label_std$i .field.cmb_std$i
03642             }
03643         }
03644     }
03645 }
03646 
03647 /*  CF_bundle_size_change*/
03648 /* */
03649 /*  Procedure appelee quand l'operateur change le nombre de composantes d'un "bundle"*/
03650 /* */
03651 ret  {CF_bundle_size_change} (type name , type el , type op) {
03652     global Row_in_field_page
03653     global Field_bundle_size
03654 
03655     # Affiche les champs de saisie des composantes du "bundle"
03656     set r $Row_in_field_page(other_std_names)
03657     for {set i 1} {$i <= $Field_bundle_size} {incr i} {
03658         grid .field.label_std$i .field.cmb_std$i -row $r -sticky w
03659         incr r
03660     }
03661     # N'affiche pas les champs des autres composantes
03662     for {} {$i < 11} {incr i} {
03663         grid forget .field.label_std$i .field.cmb_std$i
03664     }
03665 }
03666 
03667 /*  Récupere par liaison HTTP sur le site du PCMDI (LLNL, Californie)*/
03668 /*  le fichier XML contenant la table des noms de champs standard CF*/
03669 /*  PAS UTILISEE*/
03670 /*  */
03671 /*  - filename : nom du fichier a créer*/
03672 /* */
03673 ret  CF_update_standard_name_table (type filename) {
03674     package require http
03675     set f [open $filename w]
03676     set si_erreur [catch { http::geturl
03677         set token [http://cf-pcmdi.llnl.gov/documents/cf-standard-names/standard-name-table/12/cf-standard-name-table.xml -channel $f]
03678          } erreur]
03679 
03680 puts "http::ncode http::code : [http::ncode $token] [http::code $token]"
03681     if {! $si_erreur} {
03682         http::cleanup $token
03683     }
03684     close $f
03685 }
03686 
03687 
03688 ret  {help_attach_balloon} (type win , type help_, type text) {
03689 # Programme l'affichage d'un texte d'aide pour la zone de saisie 'win'
03690     global no_help_balloon
03691     
03692     if { $no_help_balloon == 0 } {
03693         set help_action "help_object %W [list $help_text] message"
03694         # Le message d'aide s'affiche quand la souris survole la zone de saisie
03695         bind $win <Any-Enter> $help_action
03696         # et quand le curseur entre dans la zone de saisie
03697         bind $win <FocusIn>   $help_action
03698         # Le message d'aide disparait quand la souris ne survole plus la zone de saisie
03699         bind $win <Any-Leave>    "help_object_hide"
03700         bind $win <FocusOut>     "help_object_hide"
03701         # ou quand l'utilisateur a frappe une touche
03702         bind $win <Any-KeyPress> "help_object_hide"
03703     }
03704 }
03705 
03706 ret  {help_object} (type w , type text , type type , optional x ={) {y {}} {color /* fefed4}} {*/
03707 /*  Affiche une fenetre ballon*/
03708 /*  Parametre d'entree :*/
03709 /*    - w :     fenetre pres de laquelle doit s'afficher le message*/
03710 /*    - text :  texte du message a afficher*/
03711 /*    - type :  label ou message*/
03712 /*    - x, y :  coordonnees a l'ecran*/
03713 /*    - color : couleur du fond*/
03714 /* */
03715     if {$x == ""} {
03716          x =  [expr [winfo rootx $w]]
03717          y =  [expr [winfo rooty $w] + [winfo height $w] - 10]
03718     }
03719     /*   after 500*/
03720      u =  .ballon_object
03721     destroy $u.text
03722     switch $type {
03723         message {message $u.text -text $text -padx 15 -pady 10 -justify left}
03724         label   {label $u.text -text $text -padx 15 -pady 10 -wraplength 140  -justify left}
03725     }
03726     $u.text configure -foreground black -font {helvetica 10} -background $color
03727     pack $u.text -side left
03728 
03729     /*  Place la bulle d'aide au dessus de la zone de saisie*/
03730     incr x [winfo width $w]
03731     incr x 5
03732     wm geometry $u +$x+$y
03733 
03734     wm deiconify $u
03735     raise $u
03736 }
03737 
03738 ret  {help_object_hide} () {
03739     catch {wm withdraw .ballon_object}
03740 }
03741 
03742 ret  {dialog_controls} (type win) {
03743 return "$win.controls"
03744 }
03745 
03746 ret  {dialog_create} (type class , optional win =auto) {
03747 #
03748     if {$win == "auto"} {
03749         set count 0
03750         set win ".dialog[incr count]"
03751         while {[winfo exists $win]} {
03752             set win ".dialog[incr count]"
03753         }
03754     }
03755     catch {destroy $win}
03756     toplevel $win -class $class
03757 
03758     frame $win.info
03759     pack $win.info -expand yes -fill both -padx 4 -pady 4
03760 
03761     frame $win.sep -height 2 -borderwidth 1 -relief sunken
03762     pack $win.sep -fill x -pady 4
03763 
03764     frame $win.controls
03765     pack $win.controls -fill x -padx 4 -pady 4
03766 
03767     wm title $win $class
03768     wm group $win .
03769 
03770     after idle [format {
03771         update idletasks
03772         wm minsize %s [winfo reqwidth %s] [winfo reqheight %s]
03773     } $win $win $win]
03774 
03775     return $win
03776 }
03777 
03778 ret  {dialog_info} (type win) {
03779 return "$win.info"
03780 }
03781 
03782 ret  {dialog_safeguard} (type win) {
03783 if {[lsearch [bindtags $win] modalDialog] < 0} {
03784         bindtags $win [linsert [bindtags $win] 0 modalDialog]
03785     }
03786 }
03787 
03788 ret  {dialog_wait} (type win , type varName) {
03789 #
03790     dialog_safeguard $win
03791 
03792     set x [expr [winfo rootx .]+50]
03793     set y [expr [winfo rooty .]+50]
03794     wm geometry $win "+$x+$y"
03795 
03796     wm deiconify $win
03797     grab set $win
03798 
03799     vwait $varName
03800 
03801     grab release $win
03802     wm withdraw $win
03803 }
03804 
03805 
03806 /* **** dialog_notice_show*/
03807 /* */
03808 /*      Purpose:*/
03809 /*      --------*/
03810 /*      Show a dialog box with a message and chosen icon*/
03811 /* */
03812 /*      Interface:*/
03813 /*      ----------*/
03814 /*          dialog_notice_show $mesg info*/
03815 /* */
03816 /*          Inputs :*/
03817 /*          --------*/
03818 /*            mesg : variable containing the message to be written*/
03819 /*            info : the chosen icon (info, error)*/
03820 /* */
03821 /*      References:*/
03822 /*      -----------*/
03823 /*      Effective Tcl/Tk Programming, M. Harrison and M. McLennan,*/
03824 /*      Addison-Wesley professional computing series, 1998*/
03825 /* *----------------------------------------------------------------*/
03826 /* */
03827 ret  {dialog_notice_show} (type mesg , optional icon =info) {
03828     set top [dialog_create Notice]
03829     set x [expr [winfo rootx .]+200]
03830     set y [expr [winfo rooty .]+200]
03831     wm geometry $top "+$x+$y"
03832     set info [dialog_info $top]
03833     label $info.icon -bitmap $icon
03834     pack $info.icon -side left -padx 8 -pady 8
03835     label $info.mesg -text $mesg -wraplength 4i
03836     pack $info.mesg -side right -expand yes -fill both  -padx 8 -pady 8
03837     set cntls [dialog_controls $top]
03838     button $cntls.dismiss -command "destroy $top" -text OK
03839     pack $cntls.dismiss -pady 4
03840     return $top
03841 }
03842 
03843 /*  on_terminate : Procedure appelee a la fin de la saisie*/
03844 /* */
03845 /*  Elle ecrit toutes les donnees saisies dans des fichiers XML :*/
03846 /*    - 1 fichier AD*/
03847 /*    - des fichiers PMIOD, un par composant saisi*/
03848 /* */
03849 ret  {on_terminate} () {
03850     global Data
03851     
03852     # Nom du fichier AD : dérivé du nom de l'appli
03853     set nom_fichier_AD "$Data(name)_ad.xml"
03854     # Genere ce fichier
03855     XML_genere_Appli_Desc $nom_fichier_AD
03856     
03857     # Pour tous les composants de l'appli
03858     foreach nom_comp $Data(l_composants) {
03859         # Nom du fichier PMIOD : dérivé du nom de l'appli et du composant
03860         set nom_fichier_PMIOD "$Data(name)_${nom_comp}_pmiod.xml"
03861         # Genere ce fichier
03862         XML_genere_PMIOD $nom_comp $nom_fichier_PMIOD
03863     }
03864 }
03865 
03866 /*  Genere le fichier AD de l'application*/
03867 /*  a partir des donnees saisies*/
03868 /* */
03869 ret  {XML_genere_Appli_Desc} (type nom_, type fichier_, type AD) {
03870     global Data
03871     
03872     # Crée le document XML
03873     set doc [dom::DOMImplementation create]
03874 
03875     # Crée l'élément racine <application>
03876     set appli_element [::dom::document createElement $doc application]
03877     # Ses attributs
03878     dom::element setAttribute $appli_element xmlns "http://www.cerfacs.fr/PRISM/XML/1.1"
03879     dom::element setAttribute $appli_element "xmlns:xsi" "http://www.w3.org/2001/XMLSchema-instance"
03880     dom::element setAttribute $appli_element "xsi:schemaLocation" "http://www.cerfacs.fr/PRISM/XML/1.1"
03881     dom::element setAttribute $appli_element local_name $Data(name)
03882     if {$Data(long_name) != ""} {
03883         dom::element setAttribute $appli_element  long_name $Data(long_name)
03884     }
03885     dom::element setAttribute $appli_element oasis4_version OASIS4_0_2
03886     dom::element setAttribute $appli_element start_mode $Data(start_mode)
03887     dom::element setAttribute $appli_element coupling_mode $Data(coupling_mode)
03888         
03889     # Crée l'élément "nbr_procs"
03890     set nbr_procs_elt [::dom::document createElement $appli_element nbr_procs]
03891     # Crée l'élément "min_value"
03892     set node [::dom::document createElement $nbr_procs_elt min_value]
03893     ::dom::document createTextNode $node $Data(nb_procs_min)
03894     # Crée l'élément "max_value"
03895     set node [::dom::document createElement $nbr_procs_elt max_value]
03896     ::dom::document createTextNode $node $Data(nb_procs_max)
03897     # Crée l'élément "increment"
03898     set node [::dom::document createElement $nbr_procs_elt increment]
03899     ::dom::document createTextNode $node $Data(nb_procs_incr)
03900 
03901     # Pour tous les composants saisis
03902     foreach nom_comp $Data(l_composants) {
03903         # Crée l'élément "component"
03904         set component_elt [::dom::document createElement $appli_element component]
03905         # Ses attributs
03906         dom::element setAttribute $component_elt local_name $nom_comp
03907         if {$Data(comp.$nom_comp.long_name) != ""} {
03908             dom::element setAttribute $component_elt long_name  $Data(comp.$nom_comp.long_name)
03909         }
03910         dom::element setAttribute $component_elt simulated  $Data(comp.$nom_comp.simul)
03911         set is_default $Data(comp.$nom_comp.default)
03912         if {$is_default == "yes"} { set is_default "true"} else { set is_default "false"}
03913         dom::element setAttribute $component_elt default $is_default
03914 
03915         # Crée l'élément "nbr_procs"
03916         set nbr_procs_elt [::dom::document createElement $component_elt nbr_procs]
03917         # Crée l'élément "min_value"
03918         set node [::dom::document createElement $nbr_procs_elt min_value]
03919         ::dom::document createTextNode $node $Data(comp.$nom_comp.procs_min)
03920         # Crée l'élément "max_value"
03921         set node [::dom::document createElement $nbr_procs_elt max_value]
03922         ::dom::document createTextNode $node $Data(comp.$nom_comp.procs_max)
03923         # Crée l'élément "increment"
03924         set node [::dom::document createElement $nbr_procs_elt increment]
03925         ::dom::document createTextNode $node $Data(comp.$nom_comp.procs_incr)
03926     }
03927 
03928     # Traduit en texte à syntaxe XML
03929     set texte_xml [::dom::DOMImplementation serialize $doc -indent 1]
03930     # Libère la mémoirein
03931     ::dom::DOMImplementation destroy $doc
03932     # Ecrit le texte dans le fichier
03933     set fichier_XML [open $nom_fichier_AD w]
03934     puts $fichier_XML $texte_xml
03935     close $fichier_XML
03936 }
03937 
03938 /*  Génère un fichier PMIOD pour un composant saisi*/
03939 /* */
03940 /*    - nom_comp : nom symbolique du composant*/
03941 /*    - nom_fichier_PMIOD : nom du fichier a créer*/
03942 /* */
03943 ret  {XML_genere_PMIOD} (type nom_, type comp , type nom_, type fichier_, type PMIOD) {
03944     global Data
03945     global Grid_names
03946     
03947     # Crée le document XML
03948     set doc [dom::DOMImplementation create]
03949 
03950     # Crée l'élément racine <prismcomponent>
03951     set component_elt [::dom::document createElement $doc prismcomponent]
03952     # Ses attributs
03953     dom::element setAttribute $component_elt xmlns "http://www.cerfacs.fr/PRISM/XML/1.1"
03954     dom::element setAttribute $component_elt "xmlns:xsi" "http://www.w3.org/2001/XMLSchema-instance"
03955     dom::element setAttribute $component_elt "xsi:schemaLocation" "http://www.cerfacs.fr/PRISM/XML/1.1"
03956     dom::element setAttribute $component_elt local_name $nom_comp
03957     if {$Data(comp.$nom_comp.long_name) != ""} {
03958         dom::element setAttribute $component_elt  long_name $Data(comp.$nom_comp.long_name)
03959     }
03960     dom::element setAttribute $component_elt oasis4_version OASIS4_0_2
03961         dom::element setAttribute $component_elt simulated  $Data(comp.$nom_comp.simul)
03962     # Ecriture des caracteristiques des grilles
03963     foreach id_grid $Grid_names($nom_comp.lgrid) {
03964         # Nom de la grille
03965         set grid_name $Grid_names($nom_comp.grid.$id_grid.name)
03966         # Si il y a un nom de grille saisi
03967         if {$grid_name != ""} {
03968             set grid_elt [::dom::document createElement $component_elt grid]
03969             dom::element setAttribute $grid_elt local_name $grid_name
03970             # Indexing dimensions
03971             set periodi_elt [::dom::document createElement $grid_elt indexing_dimension]
03972             dom::element setAttribute $periodi_elt index 1
03973             dom::element setAttribute $periodi_elt periodic $Grid_names($nom_comp.grid.$id_grid.periodi)
03974             set periodj_elt [::dom::document createElement $grid_elt indexing_dimension]
03975             dom::element setAttribute $periodj_elt index 2
03976             dom::element setAttribute $periodj_elt periodic $Grid_names($nom_comp.grid.$id_grid.periodj)
03977             set periodk_elt [::dom::document createElement $grid_elt indexing_dimension]
03978             dom::element setAttribute $periodk_elt index 3
03979             dom::element setAttribute $periodk_elt periodic $Grid_names($nom_comp.grid.$id_grid.periodk)
03980         }
03981     }    
03982     # Pour tous les champs de couplage
03983     foreach nom_champ $Data(comp.$nom_comp.lchamps) {
03984         # Clef d'acces aux attributs du champ
03985         set clef_champ comp.$nom_comp.field.$nom_champ
03986     
03987         # Crée l'élément <transient>
03988         set transient_elt [::dom::document createElement $component_elt transient]
03989         # Ses attributs
03990         dom::element setAttribute $transient_elt local_name $nom_champ
03991         if {$Data($clef_champ.long_name) != ""} {
03992             dom::element setAttribute $transient_elt long_name  $Data($clef_champ.long_name)
03993         }
03994         
03995         # Crée l'élément <transient_standard_name>
03996         set node [::dom::document createElement $transient_elt transient_standard_name]
03997         ::dom::document createTextNode $node $Data($clef_champ.std_name)
03998     
03999         # Si le champ de couplage est "bundle"
04000         if {$Data($clef_champ.type) == "bundle"} {
04001             # Bundle size
04002             set nb_elem $Data($clef_champ.bundle_size)
04003             # Pour tous les elements du "bundle"
04004             for {set i 1} {$i <= $nb_elem} {incr i} {
04005                 # ajoute un élément <transient_standard_name>
04006                 set node [::dom::document createElement $transient_elt transient_standard_name]
04007                 ::dom::document createTextNode $node $Data($clef_champ.std_name_$i)
04008             }
04009         }
04010         
04011         # Crée un sous-élément <physics>
04012         set physics_elt [::dom::document createElement $transient_elt physics]
04013         # Son attribut "transient_type"
04014         dom::element setAttribute $physics_elt transient_type $Data($clef_champ.type)
04015         
04016         # Si les unités physiques sont renseignées
04017         if {$Data($clef_champ.units) != "" } {
04018             # Crée l'élément <physical_units>
04019             set node [::dom::document createElement $physics_elt physical_units]
04020             ::dom::document createTextNode $node $Data($clef_champ.units)
04021         }
04022         # Si le champ est un "bundle"
04023         if {$Data($clef_champ.type) == "bundle"} {
04024             # Crée l'élément <nbr_bundles>
04025             set node [::dom::document createElement $physics_elt nbr_bundles]
04026             ::dom::document createTextNode $node $Data($clef_champ.bundle_size)
04027         }
04028 
04029         # Crée un sous-élément <numerics>
04030         set numerics_elt [::dom::document createElement $transient_elt numerics]
04031         # Son attribut "datatype"
04032         dom::element setAttribute $numerics_elt datatype xs:$Data($clef_champ.datatype)
04033 
04034         # Crée un sous-élément <intent>
04035         set intent_elt [::dom::document createElement $transient_elt intent]
04036 
04037         # Si le champ est en "input" ou "input and output"
04038         if {$Data($clef_champ.intent) != "output"} {
04039             # Crée un sous-élément <input>
04040             set input_elt [::dom::document createElement $intent_elt input]
04041             # Son attribut "required_but_changeable"
04042             if {$Data($clef_champ.req_but_change) == "yes"} {set req_but_change "true"} else {set req_but_change "false"}
04043             dom::element setAttribute $input_elt required_but_changeable $req_but_change
04044             # Crée un sous-élément <minimal_period>
04045             XML_genere_element_min_period $input_elt $Data($clef_champ.period_unit_in) $Data($clef_champ.period_value_in)
04046 
04047             # Si le champ a l'option "non masked value gathering"
04048             if { $Data($clef_champ.gather_scatter) == "yes" } {
04049                 # Crée un arborescence de sous-éléments
04050                 # <target_transformation>
04051                 #    <target_local_transformation>
04052                 #       <gathering\>
04053                 set node [::dom::document createElement $input_elt target_transformation]
04054                 set node [::dom::document createElement $node      target_local_transformation]
04055                 set node [::dom::document createElement $node      gathering]
04056             }
04057         }
04058 
04059         # Si le champ est en "output" ou "input and output"
04060         if {$Data($clef_champ.intent) != "input"} {
04061             # Crée un sous-élément <output>
04062             set output_elt [::dom::document createElement $intent_elt output]
04063             # Son attribut "transi_out_name"
04064             dom::element setAttribute $output_elt transi_out_name ${nom_champ}_out1
04065             # Crée un sous-élément <minimal_period>
04066             XML_genere_element_min_period $output_elt $Data($clef_champ.period_unit_out) $Data($clef_champ.period_value_out)
04067 
04068             # Si le champ a l'option "non masked value scattering"
04069             if { $Data($clef_champ.gather_scatter) == "yes" } {
04070                 # Crée un arborescence de sous-éléments
04071                 # <source_transformation>
04072                 #    <source_local_transformation>
04073                 #       <scattering>
04074                 set node [::dom::document createElement $output_elt source_transformation]
04075                 set node [::dom::document createElement $node      source_local_transformation]
04076                 set node [::dom::document createElement $node      scattering]
04077             }
04078         }
04079     }
04080     
04081     # Traduit en texte à syntaxe XML
04082     set texte_xml [::dom::DOMImplementation serialize $doc -indent 1]
04083     # Libère la mémoire
04084     ::dom::DOMImplementation destroy $doc
04085     # Ecrit le texte dans le fichier
04086     set fichier_XML [open $nom_fichier_PMIOD w]
04087     puts $fichier_XML $texte_xml
04088     close $fichier_XML
04089 }
04090 
04091 /*  Génère un sous-élément XML de type <minimal_period>*/
04092 /* */
04093 /*  - element_XML    :   element parent*/
04094 /*  - unit_temps     :   unité de temps de la min period*/
04095 /*  - valeur         :   nombre d'unités de temps de la min period*/
04096 /* */
04097 ret  {XML_genere_element_min_period} (type element_, type XML , type unit_, type temps , type valeur) {
04098 
04099     # Crée un sous-élément <minimal_period>
04100     set min_period_elt [::dom::document createElement $element_XML minimal_period]
04101     # Traduit l'unité de temps en une abbréviation
04102     switch $unit_temps  {
04103         seconds { set unit_temps "secs" }
04104         minutes { set unit_temps "mins" }
04105         default {}
04106     }
04107     # Crée un sous-élément correspondant à l'unité de temps
04108     set sous_element [::dom::document createElement $min_period_elt nbr_$unit_temps]
04109     ::dom::document createTextNode $sous_element $valeur
04110 }
04111 
04112 ret  {read_CF_standard_name_table} () {
04113 # Lit la liste des "CF Standard names" dans un fichier XML
04114 # Ce fichier s'appelle CF_standard_name_table.xml dans le repertoire courant
04115 #
04116 # Met les infos dans une variable globale de type liste : CF_standard_name_list
04117 #
04118     global CF_standard_name_list 
04119     set CF_standard_name_list {}
04120     # Liste des "unites canoniques"
04121     set canonical_units_list {}
04122     # Liste des descriptions
04123     set description_list {}
04124     
04125     # Lit le document à manipuler
04126     set nom_fichier cf-standard-name-table.xml
04127     if { [file exists $nom_fichier] } {
04128         set f_handle [open $nom_fichier]
04129     
04130         set text [read $f_handle]
04131         if { [string trim $text] != "" } {
04132             set code_erreur [catch {set doc [dom::parse $text]} msg_erreur]
04133             if {$code_erreur} {
04134                 dialog_notice_show "Error parsing file \"$nom_fichier\" :\n $msg_erreur"
04135                 return 1
04136             }
04137         } else {
04138             # Fichier vide
04139             dialog_notice_show "Error parsing file \"$nom_fichier\" :\n file is empty"
04140             return 1
04141         }
04142         
04143         # Récupère le premier élément du document
04144         # C'est l'élément principal (qui est de type <standard_name_table>)
04145         set element1 [dom::document configure $doc -documentElement]
04146         if { [dom::node configure $element1 -nodeName] != "standard_name_table" } {
04147             # Fichier vide
04148             dialog_notice_show "Content of file \"$nom_fichier\" is unexpected"
04149             return 1
04150         }
04151         
04152         # Lit les sous-éléments de l'élément <application>
04153         set ss_elem_list [dom::node children $element1]
04154         foreach element $ss_elem_list  {
04155             set name [dom::node configure $element -nodeName]
04156             if {$name != "#text" && $name != "#comment"} {
04157                 switch $name {
04158                     
04159                     entry {
04160                         set entry_OK 0
04161                         # Lit les attributs de l'élément <entry>
04162                         set att [dom::node configure $element -attributes]
04163                         # Pour tous les attributs (normalement, il n'y en a qu'un : id)
04164                         foreach {name value} [array get $att] {
04165                             if {$name == "id"} {
04166                                 lappend CF_standard_name_list $value
04167                                 set entry_OK 1
04168                             }
04169                         }
04170                         
04171                         if {$entry_OK} {
04172                             # Lit les sous-elements de l'element <entry>
04173                             set paire [lit_sous_element_entry $element]
04174                             lappend canonical_units_list [lindex $paire 0]
04175                             lappend description_list     [lindex $paire 1]
04176                         }
04177                     }
04178         
04179                     # Info inutilisée pour l'instant
04180                     version_number {
04181                         set version_number [lit_XML_texte $element]
04182                     }
04183                     # Info inutilisée pour l'instant
04184                     last_modified {
04185                         set last_modified [lit_XML_texte $element]
04186                     }
04187                     # Info inutilisée pour l'instant
04188                     institution {
04189                         set institution [lit_XML_texte $element]
04190                     }
04191                     # Info inutilisée pour l'instant
04192                     contact {
04193                         set contact [lit_XML_texte $element]
04194                     }
04195                 }
04196             }
04197         }
04198     } else {
04199         # Fichier XML manquant
04200         set CF_standard_name_list { {} "Missing file \"$nom_fichier\"..."}
04201     }
04202 }
04203 
04204 ret  {lit_sous_element_entry} (type element) {
04205 # Lit certains sous-éléments d'un élément <entry>
04206 # à savoir <canonical_units> et <description>
04207 # Retourne une liste composée de ces deux infos
04208 
04209     # Lit les sous-éléments de l'élément <entry>
04210     set ss_elem_list [dom::node children $element]
04211     foreach element $ss_elem_list  {
04212         set name [dom::node configure $element -nodeName]
04213         if {$name != "#text" && $name != "#comment"} {
04214             switch $name {
04215                 
04216                 canonical_units {
04217                     set canonical_units [lit_XML_texte $element]
04218                 }
04219                 description {
04220                     set description [lit_XML_texte $element]
04221                 }
04222             }
04223         }
04224     }
04225     
04226     return [list $canonical_units $description]
04227 }
04228 
04229 ret  {lit_XML_texte} (type element_, type XML) {
04230 # Lit le texte de\'un élément XML atomique
04231 # c.a.d qui ne contient pas de sous-éléments mais du texte
04232     
04233     # Lit les sous-éléments de l'élément XML
04234     # jusqu'a un sous-élément de type Texte
04235     set ss_elem_list [dom::node children $element_XML]
04236     foreach element $ss_elem_list  {
04237         set name [dom::node configure $element -nodeName]
04238         if {$name == "#text"} {
04239             set value [dom::node configure $element -nodeValue]
04240             return $value
04241         }
04242     }
04243 }
04244 
04245 /* */
04246 
04247 /* BEGIN main*/
04248 
04249 /*  Variables globales*/
04250 
04251 /*  Numero de la page d'ecran en cours de saisie*/
04252  Num = _page 1
04253 /*  Type de page d'ecran en cours :*/
04254 /*    0: page initiale (attributs de l'appli)*/
04255 /*    1: page d'un composant*/
04256 /*    2: page d'un champ*/
04257 /*    3: page terminale (de confirmation)*/
04258  Type = _page 0
04259 
04260 /*  Numero de composant en cours de saisie*/
04261  Num = _composant 0
04262 /*  Numero de champ de couplage en cours de saisie*/
04263  Num = _champ 0
04264 
04265 /*  Liste des noms des composants de l'appli*/
04266  Data = (l_composants) {}
04267 /*  Nombre de processus de l'appli*/
04268  Data = (nb_ret s_min) 1
04269 set Data(nb_procs_max) 1
04270 set Data(nb_procs_incr) 1
04271 # autres attributs
04272 set Data(start_mode) ""
04273 set Data(coupling_mode) ""
04274 
04275 # Liste des noms standard de champs
04276 set CF_standard_name_list ()
04277 
04278 # Initialisation
04279 # --------------
04280 
04281 # Initialise le module combobox (liste deroulante)
04282 combobox_init
04283 
04284 # Determine le chemin du script en cours d'execution
04285 global argv0
04286 set script_file [file normalize [file join [pwd]  $argv0]]
04287 # Prend le repertoire
04288 set OASIS_GUI_DIR [file dirname $script_file]
04289 unset script_file
04290 
04291 # packages : they are in subdirectory 'packages/lib'
04292 lappend auto_path [file join $OASIS_GUI_DIR packages/lib]
04293 
04294 # Lit les paramètres de la ligne de commande
04295 package require cmdline
04296 set options {
04297     {silent          "silent mode : no help balloon"}
04298 }
04299  usage =  ": wizard.tcl \[options] \noptions:"
04300 array  params =  [::cmdline::getoptions argv $options $usage]
04301 /*  Vérifie si l'option -silent a été mise sur la ligne de commande*/
04302  no = _help_balloon $params(silent)
04303 
04304 /*  Load "XML support" packages : they are in subdirectory 'packages'*/
04305 global auto_path
04306 lappend auto_path [file join $OASIS_GUI_DIR packages/lib]
04307 package require xml
04308 package require xml::libxml2
04309 package require dom
04310 
04311 /*  Lit dans un fichier XML la liste des noms standard de champs*/
04312 read_CF_standard_name_table
04313 /* puts "CF_standard_name_list : "*/
04314 /* foreach n $CF_standard_name_list {*/
04315 /*     puts $n*/
04316 /* }*/
04317 
04318 
04319 /*  Main window*/
04320 /*  -----------*/
04321 
04322 wm title . "OASIS 4 application definition wizard"
04323 wm iconname . "OASIS 4 application definition wizard"
04324     
04325 wm geometry . "+200+20"
04326 
04327 label .intro -justify left -text "This dialog based wizard helps you define your model application parameters
04328 so that XML configuration files are automatically generated at the end from your input.
04329 
04330 XML files include :
04331    - Application Description (AD) file
04332    - Potential Model Input/Output Description (PMIOD) files"
04333    
04334 pack .intro -padx 30 -pady 5 -side top
04335 
04336 /*  Cadre des boutons <NEXT> et <PREVIOUS>*/
04337 /*  --------------------------------------*/
04338 
04339 /*  Separateur*/
04340 frame .sep1 -width 200 -height 2 -borderwidth 1 -relief sunken
04341 pack .sep1 -side top -fill x -pady 2m -expand 1
04342 
04343 /*  Cadre*/
04344 frame .buttons
04345 pack .buttons -side bottom -fill x -pady 2m
04346 
04347 button .buttons.previous  -text Previous -command "on_previous_page"
04348 button .buttons.next  -text Next -command "on_next_page"
04349 button .buttons.ok  -text OK -command "on_terminate; destroy ."
04350 
04351 /*  pack .buttons.previous -side left -expand 1 -padx 10*/
04352 /*  pack .buttons.next -side right -expand 1 -padx 10*/
04353 /*  pack .buttons.ok -side right -expand 1 -padx 10*/
04354 
04355 /*  Ecran initial*/
04356 create_application_page
04357 
04358 /*  En tete de certains ecrans: le nom du composant*/
04359 /*  -----------------------------------------------*/
04360 
04361 frame .comp_name
04362 label .comp_name.label1 -text "Component : "
04363 label .comp_name.label11 -relief sunken
04364 pack .comp_name.label1 .comp_name.label11 -side left -padx 5 -pady 5
04365 
04366 /*  Ecran initial d'un composant*/
04367 create_component_page
04368 
04369 /*  Ecran d'un champ de couplage*/
04370 create_field_page
04371 
04372 /*  Ecran terminal*/
04373 create_terminal_page
04374 
04375 /* -------------------------------------*/
04376 /* ** bulle aide objet*/
04377 /* -------------------------------------*/
04378  u =  .ballon_object
04379 toplevel $u -borderwidth 1 -relief flat
04380 wm iconify $u
04381 label $u.text -text "" -background /* fefed4 -foreground black -font {helvetica 10} -justify left*/
04382 pack $u.text -side left -anchor w
04383 wm overrideredirect $u 1
04384 wm withdraw $u
04385 
04386 /*  Display first page*/
04387 display_page
04388 
04389 /*  END main*/
04390 /* */
04391 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1