00001
00002
00003 exec wish "$0" "$@"
00004
00005
00006
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
02074
02075
02076
02077
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
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
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
02170
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
02226
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
02292
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
02376
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
02604
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
02792
02793
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
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
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
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
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
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
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
03346
03347
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
03502
03503
03504
03505
03506
03507
03508
03509
03510
03511
03512
03513
03514
03515 catch {namespace import combobox::*}
03516 edita = 1
03517 if {$lim == 1} { edita = 0}
03518 upvar
03519 textvar = $cb_opt
03520 combobox $window -editable $edita -textvariable $cb_textvar -width $cb_width -background white
03521
03522 foreach elem $loclist {
03523 $window list insert end $elem
03524 }
03525
03526 indice = [lsearch $loclist $cb_opt]
03527 if {$indice != -1} { $window select $indice }
03528
03529 $window configure -command $cmdret
03530 }
03531
03532 proc (type scrollform_, type create) {win} {
03533
03534
03535
03536
03537
03538
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
03549
03550
03551
03552
03553
03554 ret scrollform_interior (type win) {
03555 return "$win.vport.form"
03556 }
03557
03558
03559
03560
03561
03562
03563
03564
03565
03566
03567
03568
03569
03570
03571
03572
03573
03574
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
03593
03594
03595
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
03609
03610
03611
03612
03613
03614
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
03648
03649
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
03668
03669
03670
03671
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:
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
03707
03708
03709
03710
03711
03712
03713
03714
03715 if {$x == ""} {
03716 x = [expr [winfo rootx $w]]
03717 y = [expr [winfo rooty $w] + [winfo height $w] - 10]
03718 }
03719
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
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
03807
03808
03809
03810
03811
03812
03813
03814
03815
03816
03817
03818
03819
03820
03821
03822
03823
03824
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
03844
03845
03846
03847
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
03867
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:
03879 dom::element setAttribute $appli_element "xmlns:xsi" "http:
03880 dom::element setAttribute $appli_element "xsi:schemaLocation" "http:
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
03939
03940
03941
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:
03954 dom::element setAttribute $component_elt "xmlns:xsi" "http:
03955 dom::element setAttribute $component_elt "xsi:schemaLocation" "http:
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
04092
04093
04094
04095
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
04248
04249
04250
04251
04252 Num = _page 1
04253
04254
04255
04256
04257
04258 Type = _page 0
04259
04260
04261 Num = _composant 0
04262
04263 Num = _champ 0
04264
04265
04266 Data = (l_composants) {}
04267
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
04302 no = _help_balloon $params(silent)
04303
04304
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
04312 read_CF_standard_name_table
04313
04314
04315
04316
04317
04318
04319
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
04337
04338
04339
04340 frame .sep1 -width 200 -height 2 -borderwidth 1 -relief sunken
04341 pack .sep1 -side top -fill x -pady 2m -expand 1
04342
04343
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