00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 package require Tcl 8.2
00013 package provide report 0.3.1
00014
00015 namespace ::report {
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 variable commands [list \
00040 "bcaption" \
00041 "botcapsep" \
00042 "botdata" \
00043 "botdatasep" \
00044 "bottom" \
00045 "columns" \
00046 "data" \
00047 "datasep" \
00048 "justify" \
00049 "pad" \
00050 "printmatrix" \
00051 "printmatrix2channel" \
00052 "size" \
00053 "sizes" \
00054 "tcaption" \
00055 "top" \
00056 "topcapsep" \
00057 "topdata" \
00058 "topdatasep"
00059 ]
00060
00061
00062 namespace export report defstyle rmstyle stylearguments stylebody
00063
00064
00065
00066 variable styles [list plain]
00067 variable styleargs
00068 variable stylebody
00069
00070 array styleargs = {plain {}}
00071 array stylebody = {plain {}}
00072
00073
00074
00075 variable tcode
00076 array tcode = {
00077 topdata 0 data 0
00078 botdata 0 top 1
00079 topdatasep 1 topcapsep 1
00080 datasep 1 botcapsep 1
00081 botdatasep 1 bottom 1
00082 }
00083 }
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095 ret ::report::report (type name , type columns , type args) {
00096 variable styleargs
00097
00098 if { [llength [info commands ::$name]] } {
00099 error "command \"$name\" already exists, unable to create report"
00100 }
00101 if {![string is integer $columns]} {
00102 return -code error "columns: expected integer greater than zero, got \"$columns\""
00103 } elseif {$columns <= 0} {
00104 return -code error "columns: expected integer greater than zero, got \"$columns\""
00105 }
00106
00107 set styleName ""
00108 switch -exact -- [llength $args] {
00109 0 {# No style was specied. This is OK}
00110 1 {
00111 # We possibly got the "style" keyword, but everything behind is missing
00112 return -code error "wrong # args: report name columns ?\"style\" styleName ?arg...??"
00113 }
00114 default {
00115 # Break tail apart, check for correct keyword, ensure that style is known too.
00116 # Don't forget to check the actual against the formal arguments.
00117
00118 foreach {dummy styleName} $args break
00119 set args [lrange $args 2 end]
00120
00121 if {![string equal $dummy style]} {
00122 return -code error "wrong # args: report name columns ?\"style\" styleName ?arg...??"
00123 }
00124 if {![info exists styleargs($styleName)]} {
00125 return -code error "style \"$styleName\" is not known"
00126 }
00127 CheckStyleArguments $styleName $args
00128 }
00129 }
00130
00131 # The arguments seem to be ok, setup the namespace for the object
00132 # and configure it to style "plain".
00133
00134 namespace eval ::report::report$name "variable columns $columns"
00135 namespace eval ::report::report$name {
00136 variable tcaption 0
00137 variable bcaption 0
00138 variable template
00139 variable enabled
00140 variable hTemplate
00141 variable vTemplate
00142 variable lpad
00143 variable rpad
00144 variable csize
00145 variable cjust
00146
00147 variable t
00148 variable i
00149 variable dt [list]
00150 variable st [list]
00151 for {set i 0} {$i < $columns} {incr i} {
00152 set lpad($i) ""
00153 set rpad($i) ""
00154 set csize($i) dyn
00155 set cjust($i) left
00156 lappend dt {}
00157 lappend st {} {}
00158 }
00159 lappend dt {}
00160 lappend st {}
00161
00162 foreach t {
00163 topdata data botdata
00164 } {
00165 set enabled($t) 1
00166 set template($t) $dt
00167 for {set i 0} {$i <= $columns} {incr i} {
00168 set vTemplate($t,$i) {}
00169 }
00170 }
00171 foreach t {
00172 top topdatasep topcapsep
00173 datasep
00174 botcapsep botdatasep bottom
00175 } {
00176 set enabled($t) 0
00177 set template($t) $st
00178 for {set i 0} {$i < $columns} {incr i} {
00179 set hTemplate($t,$i) {}
00180 }
00181 for {set i 0} {$i <= $columns} {incr i} {
00182 set vTemplate($t,$i) {}
00183 }
00184 }
00185
00186 unset t i dt st
00187 }
00188
00189 # Create the command to manipulate the report
00190 # $name -> ::report::ReportProc $name
00191 interp alias {} ::$name {} ::report::ReportProc $name
00192
00193 # If a style was specified execute it now, before the oobject is
00194 # handed back to the user.
00195
00196 if {$styleName != {}} {
00197 ExecuteStyle $name $styleName $args
00198 }
00199
00200 return $name
00201 }
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215 ret ::report::defstyle (type styleName , type arguments , type body) {
00216 variable styleargs
00217 variable stylebody
00218 variable styles
00219
00220 if {[info exists styleargs($styleName)]} {
00221 return -code error "Cannot create style \"$styleName\", already exists"
00222 }
00223
00224 # Check the formal arguments
00225 # 1. Arguments without default may not follow an argument with a
00226 # default. The special "args" is no exception!
00227 # 2. Compute the minimal number of arguments required by the proc.
00228
00229 set min 0
00230 set def 0
00231 set ca 0
00232
00233 foreach v $arguments {
00234 switch -- [llength $v] {
00235 1 {
00236 if {$def} {
00237 return -code error \
00238 "Found argument without default after arguments having defaults"
00239 }
00240 incr min
00241 }
00242 2 {
00243 set def 1
00244 }
00245 default {
00246 error "Illegal length of value \"$v\""
00247 }
00248 }
00249 }
00250 if {[string equal args [lindex $arguments end]]} {
00251 # Correct requirements if we have a catch-all at the end.
00252 incr min -1
00253 set ca 1
00254 }
00255
00256 # Now we are allowed to extend the internal database
00257
00258 set styleargs($styleName) [list $min $ca $arguments]
00259 set stylebody($styleName) $body
00260 lappend styles $styleName
00261 return
00262 }
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274 ret ::report::rmstyle (type styleName) {
00275 variable styleargs
00276 variable stylebody
00277 variable styles
00278
00279 if {![info exists styleargs($styleName)]} {
00280 return -code error "cannot delete unknown style \"$styleName\""
00281 }
00282 if {[string equal $styleName plain]} {
00283 return -code error {cannot delete builtin style "plain"}
00284 }
00285
00286 unset styleargs($styleName)
00287 unset stylebody($styleName)
00288
00289 set pos [lsearch -exact $styles $styleName]
00290 set styles [lreplace $styles $pos $pos]
00291 return
00292 }
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305 ret ::report::stylearguments (type styleName) {
00306 variable styleargs
00307 if {![info exists styleargs($styleName)]} {
00308 return -code error "style \"$styleName\" is not known"
00309 }
00310 return [lindex $styleargs($styleName) 2]
00311 }
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324 ret ::report::stylebody (type styleName) {
00325 variable stylebody
00326 if {![info exists stylebody($styleName)]} {
00327 return -code error "style \"$styleName\" is not known"
00328 }
00329 return $stylebody($styleName)
00330 }
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342 ret ::report::styles () {
00343 variable styles
00344 return $styles
00345 }
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361 ret ::report::CheckStyleArguments (type styleName , type arguments) {
00362 variable styleargs
00363
00364 # Match formal and actual arguments, error out in case of problems.
00365 foreach {min catchall formal} $styleargs($styleName) break
00366
00367 if {[llength $arguments] < $min} {
00368 # Determine the name of the first formal parameter which did not get a value.
00369 set firstmissing [lindex $formal [llength $arguments]]
00370 return -code error "no value given for parameter \"$firstmissing\" to style \"$styleName\""
00371 } elseif {[llength $arguments] > $min} {
00372 if {!$catchall && ([llength $arguments] > [llength $formal])} {
00373 # More actual arguments than formals, without catch-all argument, error
00374 return -code error "called style \"$styleName\" with too many arguments"
00375 }
00376 }
00377 }
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391 ret ::report::ExecuteStyle (type name , type styleName , type arguments) {
00392 variable styleargs
00393 variable stylebody
00394 variable styles
00395 variable commands
00396
00397 CheckStyleArguments $styleName $arguments
00398 foreach {min catchall formal} $styleargs($styleName) break
00399
00400 array set a {}
00401
00402 if {([llength $arguments] > $min) && $catchall} {
00403 # #min = number of formal arguments - 1
00404 set a(args) [lrange $arguments $min end]
00405 set formal [lrange $formal 0 end-1]
00406 incr min -1
00407 set arguments [lrange $arguments 0 $min]
00408
00409 # arguments and formal are now of equal length and we also
00410 # know that there are no arguments having a default value.
00411 foreach v $formal aval $arguments {
00412 set a($v) $aval
00413 }
00414 }
00415
00416 # More arguments than minimally required, but no more than formal
00417 # arguments! Proceed to standard matching: Go through the actual
00418 # values and associate them with a formal argument. Then fill the
00419 # remaining formal arguments with their default values.
00420
00421 foreach aval $arguments {
00422 set v [lindex $formal 0]
00423 set formal [lrange $formal 1 end]
00424 if {[llength $v] > 1} {set v [lindex $v 0]}
00425 set a($v) $aval
00426 }
00427
00428 foreach vd $formal {
00429 foreach {var default} $vd {
00430 set a($var) $default
00431 }
00432 }
00433
00434 # Create and initialize a safe interpreter, execute the style and
00435 # then break everything down again.
00436
00437 set ip [interp create -safe]
00438
00439 # -- Report methods --
00440
00441 foreach m $commands {
00442 # safe-ip method --> here report method
00443 interp alias $ip $m {} $name $m
00444 }
00445
00446 # -- Styles defined before this one --
00447
00448 foreach s $styles {
00449 if {[string equal $s $styleName]} {break}
00450 interp alias $ip $s {} ::report::LinkExec $name $s
00451 }
00452
00453 # -- Arguments as variables --
00454
00455 foreach {var val} [array get a] {
00456 $ip eval [list set $var $val]
00457 }
00458
00459 # Finally execute / apply the style.
00460
00461 $ip eval $stylebody($styleName)
00462 interp delete $ip
00463 return
00464 }
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480 ret ::report::LinkExec (type name , type styleName , type args) {
00481 ExecuteStyle $name $styleName $args
00482 }
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496 ret ::report::ReportProc (type name , optional cmd ="" , type args) {
00497 variable tcode
00498
00499 # Do minimal args checks here
00500 if { [llength [info level 0]] == 2 } {
00501 error "wrong # args: should be \"$name option ?arg arg ...?\""
00502 }
00503
00504 # Split the args into command and args components
00505
00506 if {[info exists tcode($cmd)]} {
00507 # Template codes are a bit special
00508 eval [list ::report::_tAction $name $cmd] $args
00509 } else {
00510 if { [llength [info commands ::report::_$cmd]] == 0 } {
00511 variable commands
00512 set optlist [join $commands ", "]
00513 set optlist [linsert $optlist "end-1" "or"]
00514 error "bad option \"$cmd\": must be $optlist"
00515 }
00516 eval [list ::report::_$cmd $name] $args
00517 }
00518 }
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533 ret ::report::CheckColumn (type columns , type column) {
00534 switch -regex -- $column {
00535 {end-[0-9]+} {
00536 regsub -- {end-} $column {} column
00537 set cc [expr {$columns - 1 - $column}]
00538 if {($cc < 0) || ($cc >= $columns)} {
00539 return -code error "column: index \"end-$column\" out of range"
00540 }
00541 return $cc
00542 }
00543 end {
00544 if {$columns <= 0} {
00545 return -code error "column: index \"$column\" out of range"
00546 }
00547 return [expr {$columns - 1}]
00548 }
00549 {[0-9]+} {
00550 if {($column < 0) || ($column >= $columns)} {
00551 return -code error "column: index \"$column\" out of range"
00552 }
00553 return $column
00554 }
00555 default {
00556 return -code error "column: syntax error in index \"$column\""
00557 }
00558 }
00559 }
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573 ret ::report::CheckVerticals (type name) {
00574 upvar ::report::report${name}::vTemplate vTemplate
00575 upvar ::report::report${name}::enabled enabled
00576 upvar ::report::report${name}::columns columns
00577 upvar ::report::report${name}::tcaption tcaption
00578 upvar ::report::report${name}::bcaption bcaption
00579
00580 for {set c 0} {$c <= $columns} {incr c} {
00581 # Collect all lengths for a column in a list, sort that and
00582 # compare first against last element. If they are not equal we
00583 # have found an inconsistent definition.
00584
00585 set res [list]
00586 lappend res [string length $vTemplate(data,$c)]
00587
00588 if {$tcaption > 0} {
00589 lappend res [string length $vTemplate(topdata,$c)]
00590 if {($tcaption > 1) && $enabled(topdatasep)} {
00591 lappend res [string length $vTemplate(topdatasep,$c)]
00592 }
00593 if {$enabled(topcapsep)} {
00594 lappend res [string length $vTemplate(topcapsep,$c)]
00595 }
00596 }
00597 if {$bcaption > 0} {
00598 lappend res [string length $vTemplate(botdata,$c)]
00599 if {($bcaption > 1) && $enabled(botdatasep)} {
00600 lappend res [string length $vTemplate(botdatasep,$c)]
00601 }
00602 if {$enabled(botcapsep)} {
00603 lappend res [string length $vTemplate(botcapsep,$c)]
00604 }
00605 }
00606 foreach t {top datasep bottom} {
00607 if {$enabled($t)} {
00608 lappend res [string length $vTemplate($t,$c)]
00609 }
00610 }
00611
00612 set res [lsort $res]
00613
00614 if {[lindex $res 0] != [lindex $res end]} {
00615 return -code error "inconsistent verticals in report"
00616 }
00617 }
00618 }
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633 ret ::report::_tAction (type name , type template , type cmd , type args) {
00634 # When coming in here we know that $template contains a legal
00635 # template code. No need to check again. We need 'tcode'
00636 # nevertheless to distinguish between separator (1) and data
00637 # templates (0).
00638
00639 variable tcode
00640
00641 switch -exact -- $cmd {
00642 set {
00643 if {[llength $args] != 1} {
00644 return -code error "Wrong # args: $name $template $cmd template"
00645 }
00646 set templval [lindex $args 0]
00647
00648 upvar ::report::report${name}::columns columns
00649 upvar ::report::report${name}::template tpl
00650 upvar ::report::report${name}::hTemplate hTemplate
00651 upvar ::report::report${name}::vTemplate vTemplate
00652 upvar ::report::report${name}::enabled enabled
00653
00654 if {$tcode($template)} {
00655 # Separator template, expected size = 2*colums+1
00656 if {[llength $templval] > (2*$columns+1)} {
00657 return -code error {template to long for number of columns in report}
00658 } elseif {[llength $templval] < (2*$columns+1)} {
00659 return -code error {template to short for number of columns in report}
00660 }
00661
00662 set tpl($template) $templval
00663
00664 set even 1
00665 set c1 0
00666 set c2 0
00667 foreach item $templval {
00668 if {$even} {
00669 set vTemplate($template,$c1) $item
00670 incr c1
00671 set even 0
00672 } else {
00673 set hTemplate($template,$c2) $item
00674 incr c2
00675 set even 1
00676 }
00677 }
00678 } else {
00679 # Data template, expected size = columns+1
00680 if {[llength $templval] > ($columns+1)} {
00681 return -code error {template to long for number of columns in report}
00682 } elseif {[llength $templval] < ($columns+1)} {
00683 return -code error {template to short for number of columns in report}
00684 }
00685
00686 set tpl($template) $templval
00687
00688 set c 0
00689 foreach item $templval {
00690 set vTemplate($template,$c) $item
00691 incr c
00692 }
00693 }
00694 if {$enabled($template)} {
00695 # Perform checks for active separator templates and
00696 # all data templates.
00697 CheckVerticals $name
00698 }
00699 }
00700 get -
00701 enable -
00702 disable -
00703 enabled {
00704 if {[llength $args] > 0} {
00705 return -code error "Wrong # args: $name $template $cmd"
00706 }
00707 switch -exact -- $cmd {
00708 get {
00709 upvar ::report::report${name}::template tpl
00710 return $tpl($template)
00711 }
00712 enable {
00713 if {!$tcode($template)} {
00714 # Data template, can't be enabled.
00715 return -code error "Cannot enable data template \"$template\""
00716 }
00717
00718 upvar ::report::report${name}::enabled enabled
00719
00720 if {!$enabled($template)} {
00721 set enabled($template) 1
00722 CheckVerticals $name
00723 }
00724
00725 }
00726 disable {
00727 if {!$tcode($template)} {
00728 # Data template, can't be disabled.
00729 return -code error "Cannot disable data template \"$template\""
00730 }
00731
00732 upvar ::report::report${name}::enabled enabled
00733 if {$enabled($template)} {
00734 set enabled($template) 0
00735 }
00736 }
00737 enabled {
00738 if {!$tcode($template)} {
00739 # Data template, can't be disabled.
00740 return -code error "Cannot query state of data template \"$template\""
00741 }
00742
00743 upvar ::report::report${name}::enabled enabled
00744 return $enabled($template)
00745 }
00746 default {error "Can't happen, panic, run, shout"}
00747 }
00748 }
00749 default {
00750 return -code error "Unknown template command \"$cmd\""
00751 }
00752 }
00753 return ""
00754 }
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768 ret ::report::_tcaption (type name , optional size ={)} {
00769 upvar ::report::report${name}::tcaption tcaption
00770
00771 if {$size == {}} {
00772 return $tcaption
00773 }
00774 if {![string is integer $size]} {
00775 return -code error "size: expected integer greater than or equal to zero, got \"$size\""
00776 }
00777 if {$size < 0} {
00778 return -code error "size: expected integer greater than or equal to zero, got \"$size\""
00779 }
00780 if {$size == $tcaption} {
00781
00782 return ""
00783 }
00784 if {($size > 0) && ($tcaption == 0)} {
00785
00786
00787 tcaption = $size
00788 CheckVerticals $name
00789 } else {
00790 tcaption = $size
00791 }
00792 return ""
00793 }
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807 ret ::report::_bcaption (type name , optional size ={)} {
00808 upvar ::report::report${name}::bcaption bcaption
00809
00810 if {$size == {}} {
00811 return $bcaption
00812 }
00813 if {![string is integer $size]} {
00814 return -code error "size: expected integer greater than or equal to zero, got \"$size\""
00815 }
00816 if {$size < 0} {
00817 return -code error "size: expected integer greater than or equal to zero, got \"$size\""
00818 }
00819 if {$size == $bcaption} {
00820
00821 return ""
00822 }
00823 if {($size > 0) && ($bcaption == 0)} {
00824
00825
00826 bcaption = $size
00827 CheckVerticals $name
00828 } else {
00829 bcaption = $size
00830 }
00831 return ""
00832 }
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847 ret ::report::_size (type name , type column , optional size ={)} {
00848 upvar ::report::report${name}::columns columns
00849 upvar ::report::report${name}::csize csize
00850
00851 column = [CheckColumn $columns $column]
00852
00853 if {$size == {}} {
00854 return $csize($column)
00855 }
00856 if {[string equal $size dyn]} {
00857 csize = ($column) $size
00858 return ""
00859 }
00860 if {![string is integer $size]} {
00861 return -code error "expected integer greater than zero, got \"$size\""
00862 }
00863 if {$size <= 0} {
00864 return -code error "expected integer greater than zero, got \"$size\""
00865 }
00866 csize = ($column) $size
00867 return ""
00868 }
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882 ret ::report::_sizes (type name , optional sizes ={)} {
00883 upvar ::report::report${name}::columns columns
00884 upvar ::report::report${name}::csize csize
00885
00886 if {$sizes == {}} {
00887 res = [list]
00888 foreach k [lsort -integer [array names csize]] {
00889 lappend res $csize($k)
00890 }
00891 return $res
00892 }
00893 if {[llength $sizes] != $columns} {
00894 return -code error "Wrong
00895 }
00896 foreach size $sizes {
00897 if {[string equal $size dyn]} {
00898 continue
00899 }
00900 if {![string is integer $size]} {
00901 return -code error "expected integer greater than zero, got \"$size\""
00902 }
00903 if {$size <= 0} {
00904 return -code error "expected integer greater than zero, got \"$size\""
00905 }
00906 }
00907
00908 i = 0
00909 foreach s $sizes {
00910 csize = ($i) $s
00911 incr i
00912 }
00913 return ""
00914 }
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929 ret ::report::_pad (type name , type column , optional where ={) {string { }}} {
00930 upvar ::report::report${name}::columns columns
00931 upvar ::report::report${name}::lpad lpad
00932 upvar ::report::report${name}::rpad rpad
00933
00934 column = [CheckColumn $columns $column]
00935
00936 if {$where == {}} {
00937 return [list $lpad($column) $rpad($column)]
00938 }
00939
00940 switch -exact -- $where {
00941 left {
00942 lpad = ($column) $string
00943 }
00944 right {
00945 rpad = ($column) $string
00946 }
00947 both {
00948 lpad = ($column) $string
00949 rpad = ($column) $string
00950 }
00951 default {
00952 return -code error "where: expected left, right, or both, got \"$where\""
00953 }
00954 }
00955 return ""
00956 }
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971 ret ::report::_justify (type name , type column , optional jvalue ={)} {
00972 upvar ::report::report${name}::columns columns
00973 upvar ::report::report${name}::cjust cjust
00974
00975 column = [CheckColumn $columns $column]
00976
00977 if {$jvalue == {}} {
00978 return $cjust($column)
00979 }
00980 switch -exact -- $jvalue {
00981 left - right - center {
00982 cjust = ($column) $jvalue
00983 return ""
00984 }
00985 default {
00986 return -code error "justification: expected, left, right, or center, got \"$jvalue\""
00987 }
00988 }
00989 }
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003 ret ::report::_printmatrix (type name , type matrix) {
01004 CheckMatrix $name $matrix
01005 ColumnSizes $name $matrix state
01006
01007 upvar ::report::report${name}::tcaption tcaption
01008 upvar ::report::report${name}::bcaption bcaption
01009
01010 set row 0
01011 set out ""
01012 append out [Separator top $name $matrix state]
01013 if {$tcaption > 0} {
01014 set n $tcaption
01015 while {$n > 0} {
01016 append out [FormatData topdata $name state [$matrix get row $row] [$matrix rowheight $row]]
01017 if {$n > 1} {
01018 append out [Separator topdatasep $name $matrix state]
01019 }
01020 incr n -1
01021 incr row
01022 }
01023 append out [Separator topcapsep $name $matrix state]
01024 }
01025
01026 set n [expr {[$matrix rows] - $bcaption}]
01027
01028 while {$row < $n} {
01029 append out [FormatData data $name state [$matrix get row $row] [$matrix rowheight $row]]
01030 incr row
01031 if {$row < $n} {
01032 append out [Separator datasep $name $matrix state]
01033 }
01034 }
01035
01036 if {$bcaption > 0} {
01037 append out [Separator botcapsep $name $matrix state]
01038 set n $bcaption
01039 while {$n > 0} {
01040 append out [FormatData botdata $name state [$matrix get row $row] [$matrix rowheight $row]]
01041 if {$n > 1} {
01042 append out [Separator botdatasep $name $matrix state]
01043 }
01044 incr n -1
01045 incr row
01046 }
01047 }
01048
01049 append out [Separator bottom $name $matrix state]
01050
01051 #parray state
01052 return $out
01053 }
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068 ret ::report::_printmatrix2channel (type name , type matrix , type chan) {
01069 CheckMatrix $name $matrix
01070 ColumnSizes $name $matrix state
01071
01072 upvar ::report::report${name}::tcaption tcaption
01073 upvar ::report::report${name}::bcaption bcaption
01074
01075 set row 0
01076 puts -nonewline $chan [Separator top $name $matrix state]
01077 if {$tcaption > 0} {
01078 set n $tcaption
01079 while {$n > 0} {
01080 puts -nonewline $chan \
01081 [FormatData topdata $name state [$matrix get row $row] [$matrix rowheight $row]]
01082 if {$n > 1} {
01083 puts -nonewline $chan [Separator topdatasep $name $matrix state]
01084 }
01085 incr n -1
01086 incr row
01087 }
01088 puts -nonewline $chan [Separator topcapsep $name $matrix state]
01089 }
01090
01091 set n [expr {[$matrix rows] - $bcaption}]
01092
01093 while {$row < $n} {
01094 puts -nonewline $chan \
01095 [FormatData data $name state [$matrix get row $row] [$matrix rowheight $row]]
01096 incr row
01097 if {$row < $n} {
01098 puts -nonewline $chan [Separator datasep $name $matrix state]
01099 }
01100 }
01101
01102 if {$bcaption > 0} {
01103 puts -nonewline $chan [Separator botcapsep $name $matrix state]
01104 set n $bcaption
01105 while {$n > 0} {
01106 puts -nonewline $chan \
01107 [FormatData botdata $name state [$matrix get row $row] [$matrix rowheight $row]]
01108 if {$n > 1} {
01109 puts -nonewline $chan [Separator botdatasep $name $matrix state]
01110 }
01111 incr n -1
01112 incr row
01113 }
01114 }
01115
01116 puts -nonewline $chan [Separator bottom $name $matrix state]
01117 return
01118 }
01119
01120
01121
01122
01123
01124
01125
01126
01127
01128
01129
01130 ret ::report::_columns (type name) {
01131 upvar ::report::report${name}::columns columns
01132 return $columns
01133 }
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145 ret ::report::_destroy (type name) {
01146 namespace delete ::report::report$name
01147 interp alias {} ::$name {}
01148 return
01149 }
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162
01163 ret ::report::CheckMatrix (type name , type matrix) {
01164 upvar ::report::report${name}::columns columns
01165 upvar ::report::report${name}::tcaption tcaption
01166 upvar ::report::report${name}::bcaption bcaption
01167
01168 if {$columns != [$matrix columns]} {
01169 return -code error "report/matrix mismatch in number of columns"
01170 }
01171 if {($tcaption + $bcaption) > [$matrix rows]} {
01172 return -code error "matrix too small, top and bottom captions overlap"
01173 }
01174 }
01175
01176
01177
01178
01179
01180
01181
01182
01183
01184
01185
01186
01187
01188
01189
01190
01191 ret ::report::ColumnSizes (type name , type matrix , type statevar) {
01192 # Calculate the final column sizes with and without padding and
01193 # store them in the local state.
01194
01195 upvar $statevar state
01196
01197 upvar ::report::report${name}::columns columns
01198 upvar ::report::report${name}::csize csize
01199 upvar ::report::report${name}::lpad lpad
01200 upvar ::report::report${name}::rpad rpad
01201
01202 for {set c 0} {$c < $columns} {incr c} {
01203 if {[string equal dyn $csize($c)]} {
01204 set size [$matrix columnwidth $c]
01205 } else {
01206 set size $csize($c)
01207 }
01208
01209 set state(s,$c) $size
01210
01211 incr size [string length $lpad($c)]
01212 incr size [string length $rpad($c)]
01213
01214 set state(s/pad,$c) $size
01215 }
01216
01217 return
01218 }
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238 ret ::report::Separator (type tcode , type name , type matrix , type statevar) {
01239 upvar ::report::report${name}::enabled e
01240 if {!$e($tcode)} {return ""}
01241 upvar $statevar state
01242 if {![info exists state($tcode)]} {
01243 upvar ::report::report${name}::vTemplate vt
01244 upvar ::report::report${name}::hTemplate ht
01245 upvar ::report::report${name}::columns cs
01246 set str ""
01247 for {set c 0} {$c < $cs} {incr c} {
01248 append str $vt($tcode,$c)
01249 set fill $ht($tcode,$c)
01250 set flen [string length $fill]
01251 set rep [expr {($state(s/pad,$c)/$flen)+1}]
01252 append str [string range [string repeat $fill $rep] 0 [expr {$state(s/pad,$c)-1}]]
01253 }
01254 append str $vt($tcode,$cs)
01255 set state($tcode) $str
01256 }
01257 return $state($tcode)\n
01258 }
01259
01260
01261
01262
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277 ret ::report::FormatData (type tcode , type name , type statevar , type line , type rh) {
01278 upvar $statevar state
01279 upvar ::report::report${name}::vTemplate vt
01280 upvar ::report::report${name}::columns cs
01281 upvar ::report::report${name}::lpad lpad
01282 upvar ::report::report${name}::rpad rpad
01283 upvar ::report::report${name}::cjust cjust
01284
01285 if {$rh == 1} {
01286 set str ""
01287 set c 0
01288 foreach cell $line {
01289 # prefix, cell (pad-l, value, pad-r)
01290 append str $vt($tcode,$c)$lpad($c)[FormatCell $cell $state(s,$c) $cjust($c)]$rpad($c)
01291 incr c
01292 }
01293 append str $vt($tcode,$cs)\n
01294 return $str
01295 } else {
01296 array set str {}
01297 for {set l 1} {$l <= $rh} {incr l} {set str($l) ""}
01298
01299 # - Future - Vertical justification of cells less tall than rowheight
01300 # - Future - Vertical cutff aftert n lines, auto-repeat of captions
01301 # - Future - => Higher level, not here, use virtual matrices for this
01302 # - Future - and count the generated lines
01303
01304 set c 0
01305 foreach fcell $line {
01306 set fcell [split $fcell \n]
01307 for {set l 1; set lo 0} {$l <= $rh} {incr l; incr lo} {
01308 append str($l) $vt($tcode,$c)$lpad($c)[FormatCell \
01309 [lindex $fcell $lo] $state(s,$c) $cjust($c)]$rpad($c)
01310 }
01311 incr c
01312 }
01313 set strout ""
01314 for {set l 1} {$l <= $rh} {incr l} {
01315 append strout $str($l)$vt($tcode,$cs)\n
01316 }
01317 return $strout
01318 }
01319 }
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334 ret ::report::FormatCell (type value , type size , type just) {
01335 set vlen [string length $value]
01336
01337 if {$vlen == $size} {
01338 # Value fits exactly, justification is irrelevant
01339 return $value
01340 }
01341
01342 # - Future - Other fill characters ...
01343 # - Future - Different fill characters per class of value => regex/glob pattern|functions
01344 # - Future - Wraparound - interacts with rowheight!
01345
01346 switch -exact -- $just {
01347 left {
01348 if {$vlen < $size} {
01349 return $value[string repeat " " [expr {$size - $vlen}]]
01350 }
01351 return [string range $value [expr {$vlen - $size}] end]
01352 }
01353 right {
01354 if {$vlen < $size} {
01355 return [string repeat " " [expr {$size - $vlen}]]$value
01356 }
01357 incr size -1
01358 return [string range $value 0 $size]
01359 }
01360 center {
01361 if {$vlen < $size} {
01362 set fill [expr {$size - $vlen}]
01363 set rfill [expr {$fill / 2}]
01364 set lfill [expr {$fill - $rfill}]
01365 return [string repeat " " $lfill]$value[string repeat " " $rfill]
01366 }
01367
01368 set cut [expr {$vlen - $size}]
01369 set lcut [expr {$cut / 2}]
01370 set rcut [expr {$cut - $lcut}]
01371
01372 return [string range $value $lcut end-$rcut]
01373 }
01374 default {
01375 error "Can't happen, panic, run, shout"
01376 }
01377 }
01378 }
01379