home *** CD-ROM | disk | FTP | other *** search
/ H4CK3R 5 / hacker05 / 05_HACK_05.ISO / programacao / freewrap / TCLLIBsampleApp.exe / sample / tcllib / tcllib1.0 / struct / matrix.tcl < prev    next >
Encoding:
Text File  |  2001-08-17  |  50.3 KB  |  1,853 lines

  1. # matrix.tcl --
  2. #
  3. #    Implementation of a matrix data structure for Tcl.
  4. #
  5. # Copyright (c) 2001 by Andreas Kupries <a.kupries@westend.com>
  6. #
  7. # See the file "license.terms" for information on usage and redistribution
  8. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  9. # RCS: @(#) $Id: matrix.tcl,v 1.3 2001/07/10 20:39:47 andreas_kupries Exp $
  10.  
  11. namespace eval ::struct {}
  12.  
  13. namespace eval ::struct::matrix {
  14.     # Data storage in the matrix module
  15.     # -------------------------------
  16.     #
  17.     # One namespace per object, containing
  18.     #
  19.     # - Two scalar variables containing the current number of rows and columns.
  20.     # - Four array variables containing the array data, the caches for
  21.     #   rowheights and columnwidths and the information about linked arrays.
  22.     #
  23.     # The variables are
  24.     # - columns #columns in data
  25.     # - rows    #rows in data
  26.     # - data    cell contents
  27.     # - colw    cache of columnwidths
  28.     # - rowh    cache of rowheights
  29.     # - link    information about linked arrays
  30.     
  31.     # counter is used to give a unique name for unnamed matrixs
  32.     variable counter 0
  33.  
  34.     # commands is the list of subcommands recognized by the matrix
  35.     variable commands
  36.     set      commands(.) [list    \
  37.         "add"        \
  38.         "cells"        \
  39.         "cellsize"        \
  40.         "columns"        \
  41.         "columnwidth"    \
  42.         "delete"        \
  43.         "destroy"        \
  44.         "format"        \
  45.         "get"        \
  46.         "insert"        \
  47.         "link"        \
  48.         "rowheight"        \
  49.         "rows"        \
  50.         "set"        \
  51.         "swap"        \
  52.         "unlink"
  53.         ]
  54.  
  55.     # Some subcommands have their own subcommands.
  56.     set commands(add)    [list "column" "columns" "row" "rows"]
  57.     set commands(delete) [list "column" "row"]
  58.     set commands(format) [list "2chan" "2string"]
  59.     set commands(get)    [list "cell" "column" "rect" "row"]
  60.     set commands(insert) [list "column" "row"]
  61.     set commands(set)    [list "cell" "column" "rect" "row"]
  62.     set commands(swap)   [list "columns" "rows"]
  63.  
  64.     # Only export one command, the one used to instantiate a new matrix
  65.     namespace export matrix
  66. }
  67.  
  68. # ::struct::matrix::matrix --
  69. #
  70. #    Create a new matrix with a given name; if no name is given, use
  71. #    matrixX, where X is a number.
  72. #
  73. # Arguments:
  74. #    name    Optional name of the matrix; if null or not given, generate one.
  75. #
  76. # Results:
  77. #    name    Name of the matrix created
  78.  
  79. proc ::struct::matrix::matrix {{name ""}} {
  80.     variable counter
  81.     
  82.     if { [llength [info level 0]] == 1 } {
  83.     incr counter
  84.     set name "matrix${counter}"
  85.     }
  86.  
  87.     if { [llength [info commands ::$name]] } {
  88.     error "command \"$name\" already exists, unable to create matrix"
  89.     }
  90.  
  91.     # Set up the namespace
  92.     namespace eval ::struct::matrix::matrix$name {
  93.     variable columns 0
  94.     variable rows    0
  95.  
  96.     variable data
  97.     variable colw
  98.     variable rowh
  99.     variable link
  100.  
  101.     array set data {}
  102.     array set colw {}
  103.     array set rowh {}
  104.     array set link {}
  105.     }
  106.  
  107.     # Create the command to manipulate the matrix
  108.     interp alias {} ::$name {} ::struct::matrix::MatrixProc $name
  109.  
  110.     return $name
  111. }
  112.  
  113. ##########################
  114. # Private functions follow
  115.  
  116. # ::struct::matrix::MatrixProc --
  117. #
  118. #    Command that processes all matrix object commands.
  119. #
  120. # Arguments:
  121. #    name    Name of the matrix object to manipulate.
  122. #    cmd    Subcommand to invoke.
  123. #    args    Arguments for subcommand.
  124. #
  125. # Results:
  126. #    Varies based on command to perform
  127.  
  128. proc ::struct::matrix::MatrixProc {name {cmd ""} args} {
  129.     # Do minimal args checks here
  130.     if { [llength [info level 0]] == 2 } {
  131.     error "wrong # args: should be \"$name option ?arg arg ...?\""
  132.     }
  133.     
  134.     # Split the args into command and args components
  135.     if { [llength [info commands ::struct::matrix::_$cmd]] == 0 } {
  136.     variable commands
  137.     set optlist [join $commands(.) ", "]
  138.     set optlist [linsert $optlist "end-1" "or"]
  139.     error "bad option \"$cmd\": must be $optlist"
  140.     }
  141.     eval [list ::struct::matrix::_$cmd $name] $args
  142. }
  143.  
  144. # ::struct::matrix::_add --
  145. #
  146. #    Command that processes all 'add' subcommands.
  147. #
  148. # Arguments:
  149. #    name    Name of the matrix object to manipulate.
  150. #    cmd    Subcommand of 'add' to invoke.
  151. #    args    Arguments for subcommand of 'add'.
  152. #
  153. # Results:
  154. #    Varies based on command to perform
  155.  
  156. proc ::struct::matrix::_add {name {cmd ""} args} {
  157.     # Do minimal args checks here
  158.     if { [llength [info level 0]] == 2 } {
  159.     error "wrong # args: should be \"$name add option ?arg arg ...?\""
  160.     }
  161.     
  162.     # Split the args into command and args components
  163.     if { [llength [info commands ::struct::matrix::__add_$cmd]] == 0 } {
  164.     variable commands
  165.     set optlist [join $commands(add) ", "]
  166.     set optlist [linsert $optlist "end-1" "or"]
  167.     error "bad option \"$cmd\": must be $optlist"
  168.     }
  169.     eval [list ::struct::matrix::__add_$cmd $name] $args
  170. }
  171.  
  172. # ::struct::matrix::_delete --
  173. #
  174. #    Command that processes all 'delete' subcommands.
  175. #
  176. # Arguments:
  177. #    name    Name of the matrix object to manipulate.
  178. #    cmd    Subcommand of 'delete' to invoke.
  179. #    args    Arguments for subcommand of 'delete'.
  180. #
  181. # Results:
  182. #    Varies based on command to perform
  183.  
  184. proc ::struct::matrix::_delete {name {cmd ""} args} {
  185.     # Do minimal args checks here
  186.     if { [llength [info level 0]] == 2 } {
  187.     error "wrong # args: should be \"$name delete option ?arg arg ...?\""
  188.     }
  189.     
  190.     # Split the args into command and args components
  191.     if { [llength [info commands ::struct::matrix::__delete_$cmd]] == 0 } {
  192.     variable commands
  193.     set optlist [join $commands(delete) ", "]
  194.     set optlist [linsert $optlist "end-1" "or"]
  195.     error "bad option \"$cmd\": must be $optlist"
  196.     }
  197.     eval [list ::struct::matrix::__delete_$cmd $name] $args
  198. }
  199.  
  200. # ::struct::matrix::_format --
  201. #
  202. #    Command that processes all 'format' subcommands.
  203. #
  204. # Arguments:
  205. #    name    Name of the matrix object to manipulate.
  206. #    cmd    Subcommand of 'format' to invoke.
  207. #    args    Arguments for subcommand of 'format'.
  208. #
  209. # Results:
  210. #    Varies based on command to perform
  211.  
  212. proc ::struct::matrix::_format {name {cmd ""} args} {
  213.     # Do minimal args checks here
  214.     if { [llength [info level 0]] == 2 } {
  215.     error "wrong # args: should be \"$name format option ?arg arg ...?\""
  216.     }
  217.     
  218.     # Split the args into command and args components
  219.     if { [llength [info commands ::struct::matrix::__format_$cmd]] == 0 } {
  220.     variable commands
  221.     set optlist [join $commands(format) ", "]
  222.     set optlist [linsert $optlist "end-1" "or"]
  223.     error "bad option \"$cmd\": must be $optlist"
  224.     }
  225.     eval [list ::struct::matrix::__format_$cmd $name] $args
  226. }
  227.  
  228. # ::struct::matrix::_get --
  229. #
  230. #    Command that processes all 'get' subcommands.
  231. #
  232. # Arguments:
  233. #    name    Name of the matrix object to manipulate.
  234. #    cmd    Subcommand of 'get' to invoke.
  235. #    args    Arguments for subcommand of 'get'.
  236. #
  237. # Results:
  238. #    Varies based on command to perform
  239.  
  240. proc ::struct::matrix::_get {name {cmd ""} args} {
  241.     # Do minimal args checks here
  242.     if { [llength [info level 0]] == 2 } {
  243.     error "wrong # args: should be \"$name get option ?arg arg ...?\""
  244.     }
  245.     
  246.     # Split the args into command and args components
  247.     if { [llength [info commands ::struct::matrix::__get_$cmd]] == 0 } {
  248.     variable commands
  249.     set optlist [join $commands(get) ", "]
  250.     set optlist [linsert $optlist "end-1" "or"]
  251.     error "bad option \"$cmd\": must be $optlist"
  252.     }
  253.     eval [list ::struct::matrix::__get_$cmd $name] $args
  254. }
  255.  
  256. # ::struct::matrix::_insert --
  257. #
  258. #    Command that processes all 'insert' subcommands.
  259. #
  260. # Arguments:
  261. #    name    Name of the matrix object to manipulate.
  262. #    cmd    Subcommand of 'insert' to invoke.
  263. #    args    Arguments for subcommand of 'insert'.
  264. #
  265. # Results:
  266. #    Varies based on command to perform
  267.  
  268. proc ::struct::matrix::_insert {name {cmd ""} args} {
  269.     # Do minimal args checks here
  270.     if { [llength [info level 0]] == 2 } {
  271.     error "wrong # args: should be \"$name insert option ?arg arg ...?\""
  272.     }
  273.     
  274.     # Split the args into command and args components
  275.     if { [llength [info commands ::struct::matrix::__insert_$cmd]] == 0 } {
  276.     variable commands
  277.     set optlist [join $commands(insert) ", "]
  278.     set optlist [linsert $optlist "end-1" "or"]
  279.     error "bad option \"$cmd\": must be $optlist"
  280.     }
  281.     eval [list ::struct::matrix::__insert_$cmd $name] $args
  282. }
  283.  
  284. # ::struct::matrix::_set --
  285. #
  286. #    Command that processes all 'set' subcommands.
  287. #
  288. # Arguments:
  289. #    name    Name of the matrix object to manipulate.
  290. #    cmd    Subcommand of 'set' to invoke.
  291. #    args    Arguments for subcommand of 'set'.
  292. #
  293. # Results:
  294. #    Varies based on command to perform
  295.  
  296. proc ::struct::matrix::_set {name {cmd ""} args} {
  297.     # Do minimal args checks here
  298.     if { [llength [info level 0]] == 2 } {
  299.     error "wrong # args: should be \"$name set option ?arg arg ...?\""
  300.     }
  301.     
  302.     # Split the args into command and args components
  303.     if { [llength [info commands ::struct::matrix::__set_$cmd]] == 0 } {
  304.     variable commands
  305.     set optlist [join $commands(set) ", "]
  306.     set optlist [linsert $optlist "end-1" "or"]
  307.     error "bad option \"$cmd\": must be $optlist"
  308.     }
  309.     eval [list ::struct::matrix::__set_$cmd $name] $args
  310. }
  311.  
  312. # ::struct::matrix::_swap --
  313. #
  314. #    Command that processes all 'swap' subcommands.
  315. #
  316. # Arguments:
  317. #    name    Name of the matrix object to manipulate.
  318. #    cmd    Subcommand of 'swap' to invoke.
  319. #    args    Arguments for subcommand of 'swap'.
  320. #
  321. # Results:
  322. #    Varies based on command to perform
  323.  
  324. proc ::struct::matrix::_swap {name {cmd ""} args} {
  325.     # Do minimal args checks here
  326.     if { [llength [info level 0]] == 2 } {
  327.     error "wrong # args: should be \"$name swap option ?arg arg ...?\""
  328.     }
  329.     
  330.     # Split the args into command and args components
  331.     if { [llength [info commands ::struct::matrix::__swap_$cmd]] == 0 } {
  332.     variable commands
  333.     set optlist [join $commands(swap) ", "]
  334.     set optlist [linsert $optlist "end-1" "or"]
  335.     error "bad option \"$cmd\": must be $optlist"
  336.     }
  337.     eval [list ::struct::matrix::__swap_$cmd $name] $args
  338. }
  339.  
  340. # ::struct::matrix::__add_column --
  341. #
  342. #    Extends the matrix by one column and then acts like
  343. #    "setcolumn" (see below) on this new column if there were
  344. #    "values" supplied. Without "values" the new cells will be set
  345. #    to the empty string. The new column is appended immediately
  346. #    behind the last existing column.
  347. #
  348. # Arguments:
  349. #    name    Name of the matrix object.
  350. #    values    Optional values to set into the new row.
  351. #
  352. # Results:
  353. #    None.
  354.  
  355. proc ::struct::matrix::__add_column {name {values {}}} {
  356.     upvar ::struct::matrix::matrix${name}::data    data
  357.     upvar ::struct::matrix::matrix${name}::columns cols
  358.     upvar ::struct::matrix::matrix${name}::rows    rows
  359.     upvar ::struct::matrix::matrix${name}::rowh    rh
  360.  
  361.     if {[set l [llength $values]] < $rows} {
  362.     # Missing values. Fill up with empty strings
  363.  
  364.     for {} {$l < $rows} {incr l} {
  365.         lappend values {}
  366.     }
  367.     } elseif {[llength $values] > $rows} {
  368.     # To many values. Remove the superfluous items
  369.     set values [lrange $values 0 [expr {$rows - 1}]]
  370.     }
  371.  
  372.     # "values" now contains the information to set into the array.
  373.     # Regarding the width and height caches:
  374.  
  375.     # - The new column is not added to the width cache, the other
  376.     #   columns are not touched, the cache therefore unchanged.
  377.     # - The rows are either removed from the height cache or left
  378.     #   unchanged, depending on the contents set into the cell.
  379.  
  380.     set r 0
  381.     foreach v $values {
  382.     if {$v != {}} {
  383.         # Data changed unpredictably, invalidate cache
  384.         catch {unset rh($r)}
  385.     } ; # {else leave the row unchanged}
  386.     set data($cols,$r) $v
  387.     incr r
  388.     }
  389.     incr cols
  390.     return
  391. }
  392.  
  393. # ::struct::matrix::__add_row --
  394. #
  395. #    Extends the matrix by one row and then acts like "setrow" (see
  396. #    below) on this new row if there were "values"
  397. #    supplied. Without "values" the new cells will be set to the
  398. #    empty string. The new row is appended immediately behind the
  399. #    last existing row.
  400. #
  401. # Arguments:
  402. #    name    Name of the matrix object.
  403. #    values    Optional values to set into the new row.
  404. #
  405. # Results:
  406. #    None.
  407.  
  408. proc ::struct::matrix::__add_row {name {values {}}} {
  409.     upvar ::struct::matrix::matrix${name}::data    data
  410.     upvar ::struct::matrix::matrix${name}::columns cols
  411.     upvar ::struct::matrix::matrix${name}::rows    rows
  412.     upvar ::struct::matrix::matrix${name}::colw    cw
  413.  
  414.     if {[set l [llength $values]] < $cols} {
  415.     # Missing values. Fill up with empty strings
  416.  
  417.     for {} {$l < $cols} {incr l} {
  418.         lappend values {}
  419.     }
  420.     } elseif {[llength $values] > $cols} {
  421.     # To many values. Remove the superfluous items
  422.     set values [lrange $values 0 [expr {$cols - 1}]]
  423.     }
  424.  
  425.     # "values" now contains the information to set into the array.
  426.     # Regarding the width and height caches:
  427.  
  428.     # - The new row is not added to the height cache, the other
  429.     #   rows are not touched, the cache therefore unchanged.
  430.     # - The columns are either removed from the width cache or left
  431.     #   unchanged, depending on the contents set into the cell.
  432.  
  433.     set c 0
  434.     foreach v $values {
  435.     if {$v != {}} {
  436.         # Data changed unpredictably, invalidate cache
  437.         catch {unset cw($c)}
  438.     } ; # {else leave the row unchanged}
  439.     set data($c,$rows) $v
  440.     incr c
  441.     }
  442.     incr rows
  443.     return
  444. }
  445.  
  446. # ::struct::matrix::__add_columns --
  447. #
  448. #    Extends the matrix by "n" columns. The new cells will be set
  449. #    to the empty string. The new columns are appended immediately
  450. #    behind the last existing column. A value of "n" equal to or
  451. #    smaller than 0 is not allowed.
  452. #
  453. # Arguments:
  454. #    name    Name of the matrix object.
  455. #    n    The number of new columns to create.
  456. #
  457. # Results:
  458. #    None.
  459.  
  460. proc ::struct::matrix::__add_columns {name n} {
  461.     if {$n <= 0} {
  462.     return -code error "A value of n <= 0 is not allowed"
  463.     }
  464.  
  465.     upvar ::struct::matrix::matrix${name}::data    data
  466.     upvar ::struct::matrix::matrix${name}::columns cols
  467.     upvar ::struct::matrix::matrix${name}::rows    rows
  468.  
  469.     # The new values set into the cell is always the empty
  470.     # string. These have a length and height of 0, i.e. the don't
  471.     # influence cached widths and heights as they are at least that
  472.     # big. IOW there is no need to touch and change the width and
  473.     # height caches.
  474.  
  475.     while {$n > 0} {
  476.     for {set r 0} {$r < $rows} {incr r} {
  477.         set data($cols,$r) ""
  478.     }
  479.     incr cols
  480.     incr n -1
  481.     }
  482.  
  483.     return
  484. }
  485.  
  486. # ::struct::matrix::__add_rows --
  487. #
  488. #    Extends the matrix by "n" rows. The new cells will be set to
  489. #    the empty string. The new rows are appended immediately behind
  490. #    the last existing row. A value of "n" equal to or smaller than
  491. #    0 is not allowed.
  492. #
  493. # Arguments:
  494. #    name    Name of the matrix object.
  495. #    n    The number of new rows to create.
  496. #
  497. # Results:
  498. #    None.
  499.  
  500. proc ::struct::matrix::__add_rows {name n} {
  501.     if {$n <= 0} {
  502.     return -code error "A value of n <= 0 is not allowed"
  503.     }
  504.  
  505.     upvar ::struct::matrix::matrix${name}::data    data
  506.     upvar ::struct::matrix::matrix${name}::columns cols
  507.     upvar ::struct::matrix::matrix${name}::rows    rows
  508.  
  509.     # The new values set into the cell is always the empty
  510.     # string. These have a length and height of 0, i.e. the don't
  511.     # influence cached widths and heights as they are at least that
  512.     # big. IOW there is no need to touch and change the width and
  513.     # height caches.
  514.  
  515.     while {$n > 0} {
  516.     for {set c 0} {$c < $cols} {incr c} {
  517.         set data($rows,$c) ""
  518.     }
  519.     incr rows
  520.     incr n -1
  521.     }
  522.     return
  523. }
  524.  
  525. # ::struct::matrix::_cells --
  526. #
  527. #    Returns the number of cells currently managed by the
  528. #    matrix. This is the product of "rows" and "columns".
  529. #
  530. # Arguments:
  531. #    name    Name of the matrix object.
  532. #
  533. # Results:
  534. #    The number of cells in the matrix.
  535.  
  536. proc ::struct::matrix::_cells {name} {
  537.     upvar ::struct::matrix::matrix${name}::rows    rows
  538.     upvar ::struct::matrix::matrix${name}::columns columns
  539.     return [expr {$rows * $columns}]
  540. }
  541.  
  542. # ::struct::matrix::_cellsize --
  543. #
  544. #    Returns the length of the string representation of the value
  545. #    currently contained in the addressed cell.
  546. #
  547. # Arguments:
  548. #    name    Name of the matrix object.
  549. #    column    Column index of the cell to query
  550. #    row    Row index of the cell to query
  551. #
  552. # Results:
  553. #    The number of cells in the matrix.
  554.  
  555. proc ::struct::matrix::_cellsize {name column row} {
  556.     set column [ChkColumnIndex $name $column]
  557.     set row    [ChkRowIndex    $name $row]
  558.  
  559.     upvar ::struct::matrix::matrix${name}::data data
  560.     return [string length $data($column,$row)]
  561. }
  562.  
  563. # ::struct::matrix::_columns --
  564. #
  565. #    Returns the number of columns currently managed by the
  566. #    matrix.
  567. #
  568. # Arguments:
  569. #    name    Name of the matrix object.
  570. #
  571. # Results:
  572. #    The number of columns in the matrix.
  573.  
  574. proc ::struct::matrix::_columns {name} {
  575.     upvar ::struct::matrix::matrix${name}::columns columns
  576.     return $columns
  577. }
  578.  
  579. # ::struct::matrix::_columnwidth --
  580. #
  581. #    Returns the length of the longest string representation of all
  582. #    the values currently contained in the cells of the addressed
  583. #    column if these are all spanning only one line. For cell
  584. #    values spanning multiple lines the length of their longest
  585. #    line goes into the computation.
  586. #
  587. # Arguments:
  588. #    name    Name of the matrix object.
  589. #    column    The index of the column whose width is asked for.
  590. #
  591. # Results:
  592. #    See description.
  593.  
  594. proc ::struct::matrix::_columnwidth {name column} {
  595.     set column [ChkColumnIndex $name $column]
  596.  
  597.     upvar ::struct::matrix::matrix${name}::colw cw
  598.  
  599.     if {![info exists cw($column)]} {
  600.     upvar ::struct::matrix::matrix${name}::rows rows
  601.     upvar ::struct::matrix::matrix${name}::data data
  602.  
  603.     set width 0
  604.     for {set r 0} {$r < $rows} {incr r} {
  605.         foreach line [split $data($column,$r) \n] {
  606.         set len [string length $line]
  607.         if {$len > $width} {
  608.             set width $len
  609.         }
  610.         }
  611.     }
  612.  
  613.     set cw($column) $width
  614.     }
  615.  
  616.     return $cw($column)
  617. }
  618.  
  619. # ::struct::matrix::__delete_column --
  620. #
  621. #    Deletes the specified column from the matrix and shifts all
  622. #    columns with higher indices one index down.
  623. #
  624. # Arguments:
  625. #    name    Name of the matrix.
  626. #    column    The index of the column to delete.
  627. #
  628. # Results:
  629. #    None.
  630.  
  631. proc ::struct::matrix::__delete_column {name column} {
  632.     set column [ChkColumnIndex $name $column]
  633.  
  634.     upvar ::struct::matrix::matrix${name}::data    data
  635.     upvar ::struct::matrix::matrix${name}::rows    rows
  636.     upvar ::struct::matrix::matrix${name}::columns cols
  637.     upvar ::struct::matrix::matrix${name}::colw    cw
  638.     upvar ::struct::matrix::matrix${name}::rowh    rh
  639.  
  640.     # Move all data from the higher columns down and then delete the
  641.     # superfluous data in the old last column. Move the data in the
  642.     # width cache too, take partial fill into account there too.
  643.     # Invalidate the height cache for all rows.
  644.  
  645.     for {set r 0} {$r < $rows} {incr r} {
  646.     for {set c $column; set cn [expr {$c + 1}]} {$cn < $cols} {incr c ; incr cn} {
  647.         set data($c,$r) $data($cn,$r)
  648.         if {[info exists cw($cn)]} {
  649.         set cw($c) $cw($cn)
  650.         unset cw($cn)
  651.         }
  652.     }
  653.     unset data($c,$r)
  654.     catch {unset rh($r)}
  655.     }
  656.     incr cols -1
  657.     return
  658. }
  659.  
  660. # ::struct::matrix::__delete_row --
  661. #
  662. #    Deletes the specified row from the matrix and shifts all
  663. #    row with higher indices one index down.
  664. #
  665. # Arguments:
  666. #    name    Name of the matrix.
  667. #    row    The index of the row to delete.
  668. #
  669. # Results:
  670. #    None.
  671.  
  672. proc ::struct::matrix::__delete_row {name row} {
  673.     set row [ChkRowIndex $name $row]
  674.  
  675.     upvar ::struct::matrix::matrix${name}::data    data
  676.     upvar ::struct::matrix::matrix${name}::rows    rows
  677.     upvar ::struct::matrix::matrix${name}::columns cols
  678.     upvar ::struct::matrix::matrix${name}::colw    cw
  679.     upvar ::struct::matrix::matrix${name}::rowh    rh
  680.  
  681.     # Move all data from the higher rows down and then delete the
  682.     # superfluous data in the old last row. Move the data in the
  683.     # height cache too, take partial fill into account there too.
  684.     # Invalidate the width cache for all columns.
  685.  
  686.     for {set c 0} {$c < $cols} {incr c} {
  687.     for {set r $row; set rn [expr {$r + 1}]} {$rn < $rows} {incr r ; incr rn} {
  688.         set data($c,$r) $data($c,$rn)
  689.         if {[info exists rh($rn)]} {
  690.         set rh($r) $rh($rn)
  691.         unset rh($rn)
  692.         }
  693.     }
  694.     unset data($c,$r)
  695.     catch {unset cw($c)}
  696.     }
  697.     incr rows -1
  698.     return
  699. }
  700.  
  701. # ::struct::matrix::_destroy --
  702. #
  703. #    Destroy a matrix, including its associated command and data storage.
  704. #
  705. # Arguments:
  706. #    name    Name of the matrix to destroy.
  707. #
  708. # Results:
  709. #    None.
  710.  
  711. proc ::struct::matrix::_destroy {name} {
  712.     upvar ::struct::matrix::matrix${name}::link link
  713.  
  714.     # Unlink all existing arrays before destroying the object so that
  715.     # we don't leave dangling references / traces.
  716.  
  717.     foreach avar [array names link] {
  718.     _unlink $name $avar
  719.     }
  720.  
  721.     namespace delete ::struct::matrix::matrix$name
  722.     interp alias {} ::$name {}
  723. }
  724.  
  725. # ::struct::matrix::__format_2string --
  726. #
  727. #    Formats the matrix using the specified report object and
  728. #    returns the string containing the result of this
  729. #    operation. The report has to support the "printmatrix" method.
  730. #
  731. # Arguments:
  732. #    name    Name of the matrix.
  733. #    report    Name of the report object specifying the formatting.
  734. #
  735. # Results:
  736. #    A string containing the formatting result.
  737.  
  738. proc ::struct::matrix::__format_2string {name report} {
  739.     return [$report printmatrix $name]
  740. }
  741.  
  742. # ::struct::matrix::__format_2chan --
  743. #
  744. #    Formats the matrix using the specified report object and
  745. #    writes the string containing the result of this operation into
  746. #    the channel. The report has to support the
  747. #    "printmatrix2channel" method.
  748. #
  749. # Arguments:
  750. #    name    Name of the matrix.
  751. #    report    Name of the report object specifying the formatting.
  752. #    chan    Handle of the channel to write to.
  753. #
  754. # Results:
  755. #    None.
  756.  
  757. proc ::struct::matrix::__format_2chan {name report chan} {
  758.     $report printmatrix2channel $name $chan
  759.     return
  760. }
  761.  
  762. # ::struct::matrix::__get_dell --
  763. #
  764. #    Returns the value currently contained in the cell identified
  765. #    by row and column index.
  766. #
  767. # Arguments:
  768. #    name    Name of the matrix.
  769. #    column    Column index of the addressed cell.
  770. #    row    Row index of the addressed cell.
  771. #
  772. # Results:
  773. #    value    Value currently stored in the addressed cell.
  774.  
  775. proc ::struct::matrix::__get_cell {name column row} {
  776.     set column [ChkColumnIndex $name $column]
  777.     set row    [ChkRowIndex    $name $row]
  778.  
  779.     upvar ::struct::matrix::matrix${name}::data data
  780.     return $data($column,$row)
  781. }
  782.  
  783. # ::struct::matrix::__get_column --
  784. #
  785. #    Returns a list containing the values from all cells in the
  786. #    column identified by the index. The contents of the cell in
  787. #    row 0 are stored as the first element of this list.
  788. #
  789. # Arguments:
  790. #    name    Name of the matrix.
  791. #    column    Column index of the addressed cell.
  792. #
  793. # Results:
  794. #    List of values stored in the addressed row.
  795.  
  796. proc ::struct::matrix::__get_column {name column} {
  797.     set column [ChkColumnIndex $name $column]
  798.  
  799.     upvar ::struct::matrix::matrix${name}::data data
  800.     upvar ::struct::matrix::matrix${name}::rows rows
  801.  
  802.     set result [list]
  803.     for {set r 0} {$r < $rows} {incr r} {
  804.     lappend result $data($column,$r)
  805.     }
  806.     return $result
  807. }
  808.  
  809. # ::struct::matrix::__get_rect --
  810. #
  811. #    Returns a list of lists of cell values. The values stored in
  812. #    the result come from the submatrix whose top-left and
  813. #    bottom-right cells are specified by "column_tl", "row_tl" and
  814. #    "column_br", "row_br" resp. Note that the following equations
  815. #    have to be true: column_tl <= column_br and row_tl <= row_br.
  816. #    The result is organized as follows: The outer list is the list
  817. #    of rows, its elements are lists representing a single row. The
  818. #    row with the smallest index is the first element of the outer
  819. #    list. The elements of the row lists represent the selected
  820. #    cell values. The cell with the smallest index is the first
  821. #    element in each row list.
  822. #
  823. # Arguments:
  824. #    name        Name of the matrix.
  825. #    column_tl    Column index of the top-left cell of the area.
  826. #    row_tl        Row index of the top-left cell of the the area
  827. #    column_br    Column index of the bottom-right cell of the area.
  828. #    row_br        Row index of the bottom-right cell of the the area
  829. #
  830. # Results:
  831. #    List of a list of values stored in the addressed area.
  832.  
  833. proc ::struct::matrix::__get_rect {name column_tl row_tl column_br row_br} {
  834.     set column_tl [ChkColumnIndex $name $column_tl]
  835.     set row_tl    [ChkRowIndex    $name $row_tl]
  836.     set column_br [ChkColumnIndex $name $column_br]
  837.     set row_br    [ChkRowIndex    $name $row_br]
  838.  
  839.     if {
  840.     ($column_tl > $column_br) ||
  841.     ($row_tl    > $row_br)
  842.     } {
  843.     return -code error "Invalid cell indices, wrong ordering"
  844.     }
  845.  
  846.     upvar ::struct::matrix::matrix${name}::data data
  847.     set result [list]
  848.  
  849.     for {set r $row_tl} {$r <= $row_br} {incr r} {
  850.     set row [list]
  851.     for {set c $column_tl} {$c <= $column_br} {incr c} {
  852.         lappend row $data($c,$r)
  853.     }
  854.     lappend result $row
  855.     }
  856.  
  857.     return $result
  858. }
  859.  
  860. # ::struct::matrix::__get_row --
  861. #
  862. #    Returns a list containing the values from all cells in the
  863. #    row identified by the index. The contents of the cell in
  864. #    column 0 are stored as the first element of this list.
  865. #
  866. # Arguments:
  867. #    name    Name of the matrix.
  868. #    row    Row index of the addressed cell.
  869. #
  870. # Results:
  871. #    List of values stored in the addressed row.
  872.  
  873. proc ::struct::matrix::__get_row {name row} {
  874.     set row [ChkRowIndex $name $row]
  875.  
  876.     upvar ::struct::matrix::matrix${name}::data    data
  877.     upvar ::struct::matrix::matrix${name}::columns cols
  878.  
  879.     set result [list]
  880.     for {set c 0} {$c < $cols} {incr c} {
  881.     lappend result $data($c,$row)
  882.     }
  883.     return $result
  884. }
  885.  
  886. # ::struct::matrix::__insert_column --
  887. #
  888. #    Extends the matrix by one column and then acts like
  889. #    "setcolumn" (see below) on this new column if there were
  890. #    "values" supplied. Without "values" the new cells will be set
  891. #    to the empty string. The new column is inserted just before
  892. #    the column specified by the given index. This means, if
  893. #    "column" is less than or equal to zero, then the new column is
  894. #    inserted at the beginning of the matrix, before the first
  895. #    column. If "column" has the value "Bend", or if it is greater
  896. #    than or equal to the number of columns in the matrix, then the
  897. #    new column is appended to the matrix, behind the last
  898. #    column. The old column at the chosen index and all columns
  899. #    with higher indices are shifted one index upward.
  900. #
  901. # Arguments:
  902. #    name    Name of the matrix.
  903. #    column    Index of the column where to insert.
  904. #    values    Optional values to set the cells to.
  905. #
  906. # Results:
  907. #    None.
  908.  
  909. proc ::struct::matrix::__insert_column {name column {values {}}} {
  910.     # Allow both negative and too big indices.
  911.     set column [ChkColumnIndexAll $name $column]
  912.  
  913.     upvar ::struct::matrix::matrix${name}::data    data
  914.     upvar ::struct::matrix::matrix${name}::columns cols
  915.     upvar ::struct::matrix::matrix${name}::rows    rows
  916.     upvar ::struct::matrix::matrix${name}::rowh    rh
  917.  
  918.     if {$column > $cols} {
  919.     # Same as 'addcolumn'
  920.     __add_column $name $values
  921.     return
  922.     }
  923.  
  924.     set firstcol $column
  925.     if {$firstcol < 0} {
  926.     set firstcol 0
  927.     }
  928.  
  929.     if {[set l [llength $values]] < $rows} {
  930.     # Missing values. Fill up with empty strings
  931.  
  932.     for {} {$l < $rows} {incr l} {
  933.         lappend values {}
  934.     }
  935.     } elseif {[llength $values] > $rows} {
  936.     # To many values. Remove the superfluous items
  937.     set values [lrange $values 0 [expr {$rows - 1}]]
  938.     }
  939.  
  940.     # "values" now contains the information to set into the array.
  941.     # Regarding the width and height caches:
  942.     # Invalidate all rows, move all columns
  943.  
  944.     # Move all data from the higher columns one up and then insert the
  945.     # new data into the freed space. Move the data in the
  946.     # width cache too, take partial fill into account there too.
  947.     # Invalidate the height cache for all rows.
  948.  
  949.     for {set r 0} {$r < $rows} {incr r} {
  950.     for {set cn $cols ; set c [expr {$cn - 1}]} {$c >= $firstcol} {incr c -1 ; incr cn -1} {
  951.         set data($cn,$r) $data($c,$r)
  952.         if {[info exists cw($c)]} {
  953.         set cw($cn) $cw($c)
  954.         unset cw($c)
  955.         }
  956.     }
  957.     set data($firstcol,$r) [lindex $values $r]
  958.     catch {unset rh($r)}
  959.     }
  960.     incr cols
  961.     return
  962. }
  963.  
  964. # ::struct::matrix::__insert_row --
  965. #
  966. #    Extends the matrix by one row and then acts like "setrow" (see
  967. #    below) on this new row if there were "values"
  968. #    supplied. Without "values" the new cells will be set to the
  969. #    empty string. The new row is inserted just before the row
  970. #    specified by the given index. This means, if "row" is less
  971. #    than or equal to zero, then the new row is inserted at the
  972. #    beginning of the matrix, before the first row. If "row" has
  973. #    the value "end", or if it is greater than or equal to the
  974. #    number of rows in the matrix, then the new row is appended to
  975. #    the matrix, behind the last row. The old row at that index and
  976. #    all rows with higher indices are shifted one index upward.
  977. #
  978. # Arguments:
  979. #    name    Name of the matrix.
  980. #    row    Index of the row where to insert.
  981. #    values    Optional values to set the cells to.
  982. #
  983. # Results:
  984. #    None.
  985.  
  986. proc ::struct::matrix::__insert_row {name row {values {}}} {
  987.     # Allow both negative and too big indices.
  988.     set row [ChkRowIndexAll $name $row]
  989.  
  990.     upvar ::struct::matrix::matrix${name}::data    data
  991.     upvar ::struct::matrix::matrix${name}::columns cols
  992.     upvar ::struct::matrix::matrix${name}::rows    rows
  993.     upvar ::struct::matrix::matrix${name}::rowh    rh
  994.  
  995.     if {$row > $rows} {
  996.     # Same as 'addrow'
  997.     __add_row $name $values
  998.     return
  999.     }
  1000.  
  1001.     set firstrow $row
  1002.     if {$firstrow < 0} {
  1003.     set firstrow 0
  1004.     }
  1005.  
  1006.     if {[set l [llength $values]] < $cols} {
  1007.     # Missing values. Fill up with empty strings
  1008.  
  1009.     for {} {$l < $cols} {incr l} {
  1010.         lappend values {}
  1011.     }
  1012.     } elseif {[llength $values] > $cols} {
  1013.     # To many values. Remove the superfluous items
  1014.     set values [lrange $values 0 [expr {$cols - 1}]]
  1015.     }
  1016.  
  1017.     # "values" now contains the information to set into the array.
  1018.     # Regarding the width and height caches:
  1019.     # Invalidate all columns, move all rows
  1020.  
  1021.     # Move all data from the higher rows one up and then insert the
  1022.     # new data into the freed space. Move the data in the
  1023.     # height cache too, take partial fill into account there too.
  1024.     # Invalidate the width cache for all columns.
  1025.  
  1026.     for {set c 0} {$c < $cols} {incr c} {
  1027.     for {set rn $rows ; set r [expr {$rn - 1}]} {$r >= $firstrow} {incr r -1 ; incr rn -1} {
  1028.         set data($c,$rn) $data($c,$r)
  1029.         if {[info exists rh($r)]} {
  1030.         set rh($rn) $rh($r)
  1031.         unset rh($r)
  1032.         }
  1033.     }
  1034.     set data($c,$firstrow) [lindex $values $c]
  1035.     catch {unset cw($c)}
  1036.     }
  1037.     incr rows
  1038.     return
  1039. }
  1040.  
  1041. # ::struct::matrix::_link --
  1042. #
  1043. #    Links the matrix to the specified array variable. This means
  1044. #    that the contents of all cells in the matrix is stored in the
  1045. #    array too, with all changes to the matrix propagated there
  1046. #    too. The contents of the cell "(column,row)" is stored in the
  1047. #    array using the key "column,row". If the option "-transpose"
  1048. #    is specified the key "row,column" will be used instead. It is
  1049. #    possible to link the matrix to more than one array. Note that
  1050. #    the link is bidirectional, i.e. changes to the array are
  1051. #    mirrored in the matrix too.
  1052. #
  1053. # Arguments:
  1054. #    name    Name of the matrix object.
  1055. #    option    Either empty of '-transpose'.
  1056. #    avar    Name of the variable to link to
  1057. #
  1058. # Results:
  1059. #    None
  1060.  
  1061. proc ::struct::matrix::_link {name args} {
  1062.     switch -exact -- [llength $args] {
  1063.     0 {
  1064.         return -code error "wrong # args: link ?-transpose? arrayvariable"
  1065.     }
  1066.     1 {
  1067.         set transpose 0
  1068.         set variable  [lindex $args 0]
  1069.     }
  1070.     2 {
  1071.         foreach {t variable} $args break
  1072.         if {[string compare $t -transpose]} {
  1073.         return -code error "$name: illegal syntax: link ?-transpose? arrayvariable"
  1074.         }
  1075.         set transpose 1
  1076.     }
  1077.     default {
  1078.         return -code error "$name: wrong # args: link ?-transpose? arrayvariable"
  1079.     }
  1080.     }
  1081.  
  1082.     upvar ::struct::matrix::matrix${name}::link link
  1083.  
  1084.     if {[info exists link($variable)]} {
  1085.     return -code error "$name link: Variable \"$variable\" already linked to matrix"
  1086.     }
  1087.  
  1088.     # Ok, a new variable we are linked to. Record this information,
  1089.     # dump our current contents into the array, at last generate the
  1090.     # traces actually performing the link.
  1091.  
  1092.     set link($variable) $transpose
  1093.  
  1094.     upvar #0 $variable array
  1095.     upvar ::struct::matrix::matrix${name}::data data
  1096.  
  1097.     foreach key [array names data] {
  1098.     foreach {c r} [split $key ,] break
  1099.     if {$transpose} {
  1100.         set array($r,$c) $data($key)
  1101.     } else {
  1102.         set array($c,$r) $data($key)
  1103.     }
  1104.     }
  1105.  
  1106.     trace variable array wu [list ::struct::matrix::MatTraceIn  $variable $name]
  1107.     trace variable date  w  [list ::struct::matrix::MatTraceOut $variable $name]
  1108.     return
  1109. }
  1110.  
  1111. # ::struct::matrix::_rowheight --
  1112. #
  1113. #    Returns the height of the specified row in lines. This is the
  1114. #    highest number of lines spanned by a cell over all cells in
  1115. #    the row.
  1116. #
  1117. # Arguments:
  1118. #    name    Name of the matrix
  1119. #    row    Index of the row queried for its height
  1120. #
  1121. # Results:
  1122. #    The height of the specified row in lines.
  1123.  
  1124. proc ::struct::matrix::_rowheight {name row} {
  1125.     set row [ChkRowIndex $name $row]
  1126.  
  1127.     upvar ::struct::matrix::matrix${name}::rowh rh
  1128.  
  1129.     if {![info exists rh($row)]} {
  1130.     upvar ::struct::matrix::matrix${name}::columns cols
  1131.     upvar ::struct::matrix::matrix${name}::data data
  1132.  
  1133.     set height 1
  1134.     for {set c 0} {$c < $cols} {incr c} {
  1135.         set cheight [llength [split $data($c,$row) \n]]
  1136.         if {$cheight > $height} {
  1137.         set height $cheight
  1138.         }
  1139.     }
  1140.  
  1141.     set rh($row) $height
  1142.     }
  1143.  
  1144.     return $rh($row)
  1145. }
  1146.  
  1147. # ::struct::matrix::_rows --
  1148. #
  1149. #    Returns the number of rows currently managed by the matrix.
  1150. #
  1151. # Arguments:
  1152. #    name    Name of the matrix object.
  1153. #
  1154. # Results:
  1155. #    The number of rows in the matrix.
  1156.  
  1157. proc ::struct::matrix::_rows {name} {
  1158.     upvar ::struct::matrix::matrix${name}::rows rows
  1159.     return $rows
  1160. }
  1161.  
  1162. # ::struct::matrix::__set_cell --
  1163. #
  1164. #    Sets the value in the cell identified by row and column index
  1165. #    to the data in the third argument.
  1166. #
  1167. # Arguments:
  1168. #    name    Name of the matrix object.
  1169. #    column    Column index of the cell to set.
  1170. #    row    Row index of the cell to set.
  1171. #    value    THe new value of the cell.
  1172. #
  1173. # Results:
  1174. #    None.
  1175.  
  1176. proc ::struct::matrix::__set_cell {name column row value} {
  1177.     set column [ChkColumnIndex $name $column]
  1178.     set row    [ChkRowIndex    $name $row]
  1179.  
  1180.     upvar ::struct::matrix::matrix${name}::data data
  1181.  
  1182.     if {![string compare $value $data($column,$row)]} {
  1183.     # No change, ignore call!
  1184.     return
  1185.     }
  1186.  
  1187.     set data($column,$row) $value
  1188.  
  1189.     if {$value != {}} {
  1190.     upvar ::struct::matrix::matrix${name}::colw colw
  1191.     upvar ::struct::matrix::matrix${name}::rowh rowh
  1192.     catch {unset colw($column)}
  1193.     catch {unset rowh($row)}
  1194.     }
  1195.     return
  1196. }
  1197.  
  1198. # ::struct::matrix::__set_column --
  1199. #
  1200. #    Sets the values in the cells identified by the column index to
  1201. #    the elements of the list provided as the third argument. Each
  1202. #    element of the list is assigned to one cell, with the first
  1203. #    element going into the cell in row 0 and then upward. If there
  1204. #    are less values in the list than there are rows the remaining
  1205. #    rows are set to the empty string. If there are more values in
  1206. #    the list than there are rows the superfluous elements are
  1207. #    ignored. The matrix is not extended by this operation.
  1208. #
  1209. # Arguments:
  1210. #    name    Name of the matrix.
  1211. #    column    Index of the column to set.
  1212. #    values    Values to set into the column.
  1213. #
  1214. # Results:
  1215. #    None.
  1216.  
  1217. proc ::struct::matrix::__set_column {name column values} {
  1218.     set column [ChkColumnIndex $name $column]
  1219.  
  1220.     upvar ::struct::matrix::matrix${name}::data    data
  1221.     upvar ::struct::matrix::matrix${name}::columns cols
  1222.     upvar ::struct::matrix::matrix${name}::rows    rows
  1223.     upvar ::struct::matrix::matrix${name}::rowh    rh
  1224.     upvar ::struct::matrix::matrix${name}::colw    cw
  1225.  
  1226.     if {[set l [llength $values]] < $rows} {
  1227.     # Missing values. Fill up with empty strings
  1228.  
  1229.     for {} {$l < $rows} {incr l} {
  1230.         lappend values {}
  1231.     }
  1232.     } elseif {[llength $values] > $rows} {
  1233.     # To many values. Remove the superfluous items
  1234.     set values [lrange $values 0 [expr {$rows - 1}]]
  1235.     }
  1236.  
  1237.     # "values" now contains the information to set into the array.
  1238.     # Regarding the width and height caches:
  1239.  
  1240.     # - Invalidate the column in the width cache.
  1241.     # - The rows are either removed from the height cache or left
  1242.     #   unchanged, depending on the contents set into the cell.
  1243.  
  1244.     set r 0
  1245.     foreach v $values {
  1246.     if {$v != {}} {
  1247.         # Data changed unpredictably, invalidate cache
  1248.         catch {unset rh($r)}
  1249.     } ; # {else leave the row unchanged}
  1250.     set data($column,$r) $v
  1251.     incr r
  1252.     }
  1253.     catch {unset cw($column)}
  1254.     return
  1255. }
  1256.  
  1257. # ::struct::matrix::__set_rect --
  1258. #
  1259. #    Takes a list of lists of cell values and writes them into the
  1260. #    submatrix whose top-left cell is specified by the two
  1261. #    indices. If the sublists of the outerlist are not of equal
  1262. #    length the shorter sublists will be filled with empty strings
  1263. #    to the length of the longest sublist. If the submatrix
  1264. #    specified by the top-left cell and the number of rows and
  1265. #    columns in the "values" extends beyond the matrix we are
  1266. #    modifying the over-extending parts of the values are ignored,
  1267. #    i.e. essentially cut off. This subcommand expects its input in
  1268. #    the format as returned by "getrect".
  1269. #
  1270. # Arguments:
  1271. #    name    Name of the matrix object.
  1272. #    column    Column index of the topleft cell to set.
  1273. #    row    Row index of the topleft cell to set.
  1274. #    values    Values to set.
  1275. #
  1276. # Results:
  1277. #    None.
  1278.  
  1279. proc ::struct::matrix::__set_rect {name column row values} {
  1280.     # Allow negative indices!
  1281.     set column [ChkColumnIndexNeg $name $column]
  1282.     set row    [ChkRowIndexNeg    $name $row]
  1283.  
  1284.     upvar ::struct::matrix::matrix${name}::data    data
  1285.     upvar ::struct::matrix::matrix${name}::columns cols
  1286.     upvar ::struct::matrix::matrix${name}::rows    rows
  1287.     upvar ::struct::matrix::matrix${name}::colw    colw
  1288.     upvar ::struct::matrix::matrix${name}::rowh    rowh
  1289.  
  1290.     if {$row < 0} {
  1291.     # Remove rows from the head of values to restrict it to the
  1292.     # overlapping area.
  1293.  
  1294.     set values [lrange $values [expr {0 - $row}] end]
  1295.     set row 0
  1296.     }
  1297.  
  1298.     # Restrict it at the end too.
  1299.     if {($row + [llength $values]) > $rows} {
  1300.     set values [lrange $values 0 [expr {$rows - $row - 1}]]
  1301.     }
  1302.  
  1303.     # Same for columns, but store it in some vars as this is required
  1304.     # in a loop.
  1305.     set firstcol 0
  1306.     if {$column < 0} {
  1307.     set firstcol [expr {0 - $column}]
  1308.     set column 0
  1309.     }
  1310.  
  1311.     # Now pan through values and area and copy the external data into
  1312.     # the matrix.
  1313.  
  1314.     set r $row
  1315.     foreach line $values {
  1316.     set line [lrange $line $firstcol end]
  1317.  
  1318.     set l [expr {$column + [llength $line]}]
  1319.     if {$l > $cols} {
  1320.         set line [lrange $line 0 [expr {$cols - $column - 1}]]
  1321.     } elseif {$l < [expr {$cols - $firstcol}]} {
  1322.         # We have to take the offeset into the line intou account
  1323.         # or we add fillers we don't need, overwriting part of the
  1324.         # data array we shouldn't.
  1325.  
  1326.         for {} {$l < [expr {$cols - $firstcol}]} {incr l} {
  1327.         lappend line {}
  1328.         }
  1329.     }
  1330.  
  1331.     set c $column
  1332.     foreach cell $line {
  1333.         if {$cell != {}} {
  1334.         catch {unset rh($r)}
  1335.         catch {unset cw($c)}
  1336.         }
  1337.         set data($c,$r) $cell
  1338.         incr c
  1339.     }
  1340.     incr r
  1341.     }
  1342.     return
  1343. }
  1344.  
  1345. # ::struct::matrix::__set_row --
  1346. #
  1347. #    Sets the values in the cells identified by the row index to
  1348. #    the elements of the list provided as the third argument. Each
  1349. #    element of the list is assigned to one cell, with the first
  1350. #    element going into the cell in column 0 and then upward. If
  1351. #    there are less values in the list than there are columns the
  1352. #    remaining columns are set to the empty string. If there are
  1353. #    more values in the list than there are columns the superfluous
  1354. #    elements are ignored. The matrix is not extended by this
  1355. #    operation.
  1356. #
  1357. # Arguments:
  1358. #    name    Name of the matrix.
  1359. #    row    Index of the row to set.
  1360. #    values    Values to set into the row.
  1361. #
  1362. # Results:
  1363. #    None.
  1364.  
  1365. proc ::struct::matrix::__set_row {name row values} {
  1366.     set row [ChkRowIndex $name $row]
  1367.  
  1368.     upvar ::struct::matrix::matrix${name}::data    data
  1369.     upvar ::struct::matrix::matrix${name}::columns cols
  1370.     upvar ::struct::matrix::matrix${name}::rows    rows
  1371.     upvar ::struct::matrix::matrix${name}::colw    cw
  1372.     upvar ::struct::matrix::matrix${name}::rowh    rh
  1373.  
  1374.     if {[set l [llength $values]] < $cols} {
  1375.     # Missing values. Fill up with empty strings
  1376.  
  1377.     for {} {$l < $cols} {incr l} {
  1378.         lappend values {}
  1379.     }
  1380.     } elseif {[llength $values] > $cols} {
  1381.     # To many values. Remove the superfluous items
  1382.     set values [lrange $values 0 [expr {$cols - 1}]]
  1383.     }
  1384.  
  1385.     # "values" now contains the information to set into the array.
  1386.     # Regarding the width and height caches:
  1387.  
  1388.     # - Invalidate the row in the height cache.
  1389.     # - The columns are either removed from the width cache or left
  1390.     #   unchanged, depending on the contents set into the cell.
  1391.  
  1392.     set c 0
  1393.     foreach v $values {
  1394.     if {$v != {}} {
  1395.         # Data changed unpredictably, invalidate cache
  1396.         catch {unset cw($c)}
  1397.     } ; # {else leave the row unchanged}
  1398.     set data($c,$row) $v
  1399.     incr c
  1400.     }
  1401.     catch {unset rh($row)}
  1402.     return
  1403. }
  1404.  
  1405. # ::struct::matrix::__swap_columns --
  1406. #
  1407. #    Swaps the contents of the two specified columns.
  1408. #
  1409. # Arguments:
  1410. #    name        Name of the matrix.
  1411. #    column_a    Index of the first column to swap
  1412. #    column_b    Index of the second column to swap
  1413. #
  1414. # Results:
  1415. #    None.
  1416.  
  1417. proc ::struct::matrix::__swap_columns {name column_a column_b} {
  1418.     set column_a [ChkColumnIndex $name $column_a]
  1419.     set column_b [ChkColumnIndex $name $column_b]
  1420.  
  1421.     upvar ::struct::matrix::matrix${name}::data data
  1422.     upvar ::struct::matrix::matrix${name}::rows rows
  1423.     upvar ::struct::matrix::matrix${name}::colw colw
  1424.  
  1425.     # Note: This operation does not influence the height cache for all
  1426.     # rows and the width cache only insofar as its contents has to be
  1427.     # swapped too for the two columns we are touching. Note that the
  1428.     # cache might be partially filled or not at all, so we don't have
  1429.     # to "swap" in some situations.
  1430.  
  1431.     for {set r 0} {$r < $rows} {incr r} {
  1432.     set tmp                $data($column_a,$r)
  1433.     set data($column_a,$r) $data($column_b,$r)
  1434.     set data($column_b,$r) $tmp
  1435.     }
  1436.  
  1437.     set cwa [info exists colw($column_a)]
  1438.     set cwb [info exists colw($column_b)]
  1439.  
  1440.     if {$cwa && $cwb} {
  1441.     set tmp             $colw($column_a)
  1442.     set colw($column_a) $colw($column_b)
  1443.     set colw($column_b) $tmp
  1444.     } elseif {$cwa} {
  1445.     # Move contents, don't swap.
  1446.     set   colw($column_b) $colw($column_a)
  1447.     unset colw($column_a)
  1448.     } elseif {$cwb} {
  1449.     # Move contents, don't swap.
  1450.     set   colw($column_a) $colw($column_b)
  1451.     unset colw($column_b)
  1452.     } ; # else {nothing to do at all}
  1453.     return
  1454. }
  1455.  
  1456. # ::struct::matrix::__swap_rows --
  1457. #
  1458. #    Swaps the contents of the two specified rows.
  1459. #
  1460. # Arguments:
  1461. #    name    Name of the matrix.
  1462. #    row_a    Index of the first row to swap
  1463. #    row_b    Index of the second row to swap
  1464. #
  1465. # Results:
  1466. #    None.
  1467.  
  1468. proc ::struct::matrix::__swap_rows {name row_a row_b} {
  1469.     set row_a [ChkRowIndex $name $row_a]
  1470.     set row_b [ChkRowIndex $name $row_b]
  1471.  
  1472.     upvar ::struct::matrix::matrix${name}::data    data
  1473.     upvar ::struct::matrix::matrix${name}::columns cols
  1474.     upvar ::struct::matrix::matrix${name}::rowh    rowh
  1475.  
  1476.     # Note: This operation does not influence the width cache for all
  1477.     # columns and the height cache only insofar as its contents has to be
  1478.     # swapped too for the two rows we are touching. Note that the
  1479.     # cache might be partially filled or not at all, so we don't have
  1480.     # to "swap" in some situations.
  1481.  
  1482.     for {set c 0} {$c < $cols} {incr c} {
  1483.     set tmp             $data($c,$row_a)
  1484.     set data($c,$row_a) $data($c,$row_b)
  1485.     set data($c,$row_b) $tmp
  1486.     }
  1487.  
  1488.     set rha [info exists rowh($row_a)]
  1489.     set rhb [info exists rowh($row_b)]
  1490.  
  1491.     if {$rha && $rhb} {
  1492.     set tmp          $rowh($row_a)
  1493.     set rowh($row_a) $rowh($row_b)
  1494.     set rowh($row_b) $tmp
  1495.     } elseif {$rha} {
  1496.     # Move contents, don't swap.
  1497.     set   rowh($row_b) $rowh($row_a)
  1498.     unset rowh($row_a)
  1499.     } elseif {$rhb} {
  1500.     # Move contents, don't swap.
  1501.     set   rowh($row_a) $rowh($row_b)
  1502.     unset rowh($row_b)
  1503.     } ; # else {nothing to do at all}
  1504.     return
  1505. }
  1506.  
  1507. # ::struct::matrix::_unlink --
  1508. #
  1509. #    Removes the link between the matrix and the specified
  1510. #    arrayvariable, if there is one.
  1511. #
  1512. # Arguments:
  1513. #    name    Name of the matrix.
  1514. #    avar    Name of the linked array.
  1515. #
  1516. # Results:
  1517. #    None.
  1518.  
  1519. proc ::struct::matrix::_unlink {name avar} {
  1520.  
  1521.     upvar ::struct::matrix::matrix${name}::link link
  1522.  
  1523.     if {![info exists link($avar)]} {
  1524.     # Ignore unlinking of unkown variables.
  1525.     return
  1526.     }
  1527.  
  1528.     # Delete the traces first, then remove the link management
  1529.     # information from the object.
  1530.  
  1531.     upvar #0 $avar array
  1532.     upvar ::struct::matrix::matrix${name}::data data
  1533.  
  1534.     trace vdelete array wu [list ::struct::matrix::MatTraceIn  $avar $name]
  1535.     trace vdelete date  w  [list ::struct::matrix::MatTraceOut $avar $name]
  1536.  
  1537.     unset link($avar)
  1538.     return
  1539. }
  1540.  
  1541. # ::struct::matrix::ChkColumnIndex --
  1542. #
  1543. #    Helper to check and transform column indices. Returns the
  1544. #    absolute index number belonging to the specified
  1545. #    index. Rejects indices out of the valid range of columns.
  1546. #
  1547. # Arguments:
  1548. #    matrix    Matrix to look at
  1549. #    column    The incoming index to check and transform
  1550. #
  1551. # Results:
  1552. #    The absolute index to the column
  1553.  
  1554. proc ::struct::matrix::ChkColumnIndex {name column} {
  1555.     upvar ::struct::matrix::matrix${name}::columns c
  1556.  
  1557.     switch -regex -- $column {
  1558.     {end-[0-9]+} {
  1559.         regsub -- {end-} $column {} column
  1560.         set cc [expr {$c - 1 - $column}]
  1561.         if {($cc < 0) || ($cc >= $c)} {
  1562.         return -code error "bad column index end-$column, column does not exist"
  1563.         }
  1564.         return $cc
  1565.     }
  1566.     end {
  1567.         if {$c <= 0} {
  1568.         return -code error "bad column index $column, column does not exist"
  1569.         }
  1570.         return [expr {$c - 1}]
  1571.     }
  1572.     {[0-9]+} {
  1573.         if {($column < 0) || ($column >= $c)} {
  1574.         return -code error "bad column index $column, column does not exist"
  1575.         }
  1576.         return $column
  1577.     }
  1578.     default {
  1579.         return -code error "bad column index \"$column\", syntax error"
  1580.     }
  1581.     }
  1582.     # Will not come to this place
  1583. }
  1584.  
  1585. # ::struct::matrix::ChkRowIndex --
  1586. #
  1587. #    Helper to check and transform row indices. Returns the
  1588. #    absolute index number belonging to the specified
  1589. #    index. Rejects indices out of the valid range of rows.
  1590. #
  1591. # Arguments:
  1592. #    matrix    Matrix to look at
  1593. #    row    The incoming index to check and transform
  1594. #
  1595. # Results:
  1596. #    The absolute index to the row
  1597.  
  1598. proc ::struct::matrix::ChkRowIndex {name row} {
  1599.     upvar ::struct::matrix::matrix${name}::rows r
  1600.  
  1601.     switch -regex -- $row {
  1602.     {end-[0-9]+} {
  1603.         regsub -- {end-} $row {} row
  1604.         set rr [expr {$r - 1 - $row}]
  1605.         if {($rr < 0) || ($rr >= $r)} {
  1606.         return -code error "bad row index end-$row, row does not exist"
  1607.         }
  1608.         return $rr
  1609.     }
  1610.     end {
  1611.         if {$r <= 0} {
  1612.         return -code error "bad row index $row, row does not exist"
  1613.         }
  1614.         return [expr {$r - 1}]
  1615.     }
  1616.     {[0-9]+} {
  1617.         if {($row < 0) || ($row >= $r)} {
  1618.         return -code error "bad row index $row, row does not exist"
  1619.         }
  1620.         return $row
  1621.     }
  1622.     default {
  1623.         return -code error "bad row index \"$row\", syntax error"
  1624.     }
  1625.     }
  1626.     # Will not come to this place
  1627. }
  1628.  
  1629. # ::struct::matrix::ChkColumnIndexNeg --
  1630. #
  1631. #    Helper to check and transform column indices. Returns the
  1632. #    absolute index number belonging to the specified
  1633. #    index. Rejects indices out of the valid range of columns
  1634. #    (Accepts negative indices).
  1635. #
  1636. # Arguments:
  1637. #    matrix    Matrix to look at
  1638. #    column    The incoming index to check and transform
  1639. #
  1640. # Results:
  1641. #    The absolute index to the column
  1642.  
  1643. proc ::struct::matrix::ChkColumnIndexNeg {name column} {
  1644.     upvar ::struct::matrix::matrix${name}::columns c
  1645.  
  1646.     switch -regex -- $column {
  1647.     {end-[0-9]+} {
  1648.         regsub -- {end-} $column {} column
  1649.         set cc [expr {$c - 1 - $column}]
  1650.         if {$cc >= $c} {
  1651.         return -code error "bad column index end-$column, column does not exist"
  1652.         }
  1653.         return $cc
  1654.     }
  1655.     end {
  1656.         return [expr {$c - 1}]
  1657.     }
  1658.     {[0-9]+} {
  1659.         if {$column >= $c} {
  1660.         return -code error "bad column index $column, column does not exist"
  1661.         }
  1662.         return $column
  1663.     }
  1664.     default {
  1665.         return -code error "bad column index \"$column\", syntax error"
  1666.     }
  1667.     }
  1668.     # Will not come to this place
  1669. }
  1670.  
  1671. # ::struct::matrix::ChkRowIndexNeg --
  1672. #
  1673. #    Helper to check and transform row indices. Returns the
  1674. #    absolute index number belonging to the specified
  1675. #    index. Rejects indices out of the valid range of rows
  1676. #    (Accepts negative indices).
  1677. #
  1678. # Arguments:
  1679. #    matrix    Matrix to look at
  1680. #    row    The incoming index to check and transform
  1681. #
  1682. # Results:
  1683. #    The absolute index to the row
  1684.  
  1685. proc ::struct::matrix::ChkRowIndexNeg {name row} {
  1686.     upvar ::struct::matrix::matrix${name}::rows r
  1687.  
  1688.     switch -regex -- $row {
  1689.     {end-[0-9]+} {
  1690.         regsub -- {end-} $row {} row
  1691.         set rr [expr {$r - 1 - $row}]
  1692.         if {$rr >= $r} {
  1693.         return -code error "bad row index end-$row, row does not exist"
  1694.         }
  1695.         return $rr
  1696.     }
  1697.     end {
  1698.         return [expr {$r - 1}]
  1699.     }
  1700.     {[0-9]+} {
  1701.         if {$row >= $r} {
  1702.         return -code error "bad row index $row, row does not exist"
  1703.         }
  1704.         return $row
  1705.     }
  1706.     default {
  1707.         return -code error "bad row index \"$row\", syntax error"
  1708.     }
  1709.     }
  1710.     # Will not come to this place
  1711. }
  1712.  
  1713. # ::struct::matrix::ChkColumnIndexAll --
  1714. #
  1715. #    Helper to transform column indices. Returns the
  1716. #    absolute index number belonging to the specified
  1717. #    index.
  1718. #
  1719. # Arguments:
  1720. #    matrix    Matrix to look at
  1721. #    column    The incoming index to check and transform
  1722. #
  1723. # Results:
  1724. #    The absolute index to the column
  1725.  
  1726. proc ::struct::matrix::ChkColumnIndexAll {name column} {
  1727.     upvar ::struct::matrix::matrix${name}::columns c
  1728.  
  1729.     switch -regex -- $column {
  1730.     {end-[0-9]+} {
  1731.         regsub -- {end-} $column {} column
  1732.         set cc [expr {$c - 1 - $column}]
  1733.         return $cc
  1734.     }
  1735.     end {
  1736.         return $c
  1737.     }
  1738.     {[0-9]+} {
  1739.         return $column
  1740.     }
  1741.     default {
  1742.         return -code error "bad column index \"$column\", syntax error"
  1743.     }
  1744.     }
  1745.     # Will not come to this place
  1746. }
  1747.  
  1748. # ::struct::matrix::ChkRowIndexAll --
  1749. #
  1750. #    Helper to transform row indices. Returns the
  1751. #    absolute index number belonging to the specified
  1752. #    index.
  1753. #
  1754. # Arguments:
  1755. #    matrix    Matrix to look at
  1756. #    row    The incoming index to check and transform
  1757. #
  1758. # Results:
  1759. #    The absolute index to the row
  1760.  
  1761. proc ::struct::matrix::ChkRowIndexAll {name row} {
  1762.     upvar ::struct::matrix::matrix${name}::rows r
  1763.  
  1764.     switch -regex -- $row {
  1765.     {end-[0-9]+} {
  1766.         regsub -- {end-} $row {} row
  1767.         set rr [expr {$r - 1 - $row}]
  1768.         return $rr
  1769.     }
  1770.     end {
  1771.         return $r
  1772.     }
  1773.     {[0-9]+} {
  1774.         return $row
  1775.     }
  1776.     default {
  1777.         return -code error "bad row index \"$row\", syntax error"
  1778.     }
  1779.     }
  1780.     # Will not come to this place
  1781. }
  1782.  
  1783. # ::struct::matrix::MatTraceIn --
  1784. #
  1785. #    Helper propagating changes made to an array
  1786. #    into the matrix the array is linked to.
  1787. #
  1788. # Arguments:
  1789. #    avar        Name of the array which was changed.
  1790. #    name        Matrix to write the changes to.
  1791. #    var,idx,op    Standard trace arguments
  1792. #
  1793. # Results:
  1794. #    None.
  1795.  
  1796. proc ::struct::matrix::MatTraceIn {avar name var idx op} {
  1797.     # Propagate changes in the linked array back into the matrix.
  1798.  
  1799.     if {![string compare $op u]} {
  1800.     # External array was destroyed, perform automatic unlink.
  1801.     $name unlink $avar
  1802.     return
  1803.     }
  1804.  
  1805.     upvar #0 $avar                              array
  1806.     upvar ::struct::matrix::matrix${name}::data data
  1807.     upvar ::struct::matrix::matrix${name}::link link
  1808.  
  1809.     set transpose $link($avar)
  1810.     if {$transpose} {
  1811.     foreach {r c} [split $idx ,] break
  1812.     } else {
  1813.     foreach {c r} [split $idx ,] break
  1814.     }
  1815.  
  1816.     # Use standard method to propagate the change.
  1817.     # => Get automatically index checks, cache updates, ...
  1818.  
  1819.     $name set cell $c $r $array($idx)
  1820.     return
  1821. }
  1822.  
  1823. # ::struct::matrix::MatTraceOut --
  1824. #
  1825. #    Helper propagating changes made to the matrix into the linked arrays.
  1826. #
  1827. # Arguments:
  1828. #    avar        Name of the array to write the changes to.
  1829. #    name        Matrix which was changed.
  1830. #    var,idx,op    Standard trace arguments
  1831. #
  1832. # Results:
  1833. #    None.
  1834.  
  1835. proc ::struct::matrix::MatTraceOut {avar name var idx op} {
  1836.     # Propagate changes in the matrix data array into the linked array.
  1837.  
  1838.     upvar #0 $avar                              array
  1839.     upvar ::struct::matrix::matrix${name}::data data
  1840.     upvar ::struct::matrix::matrix${name}::link link
  1841.  
  1842.     set transpose $link($avar)
  1843.     if {$transpose} {
  1844.     foreach {r c} [split $idx ,] break
  1845.     } else {
  1846.     foreach {c r} [split $idx ,] break
  1847.     }
  1848.  
  1849.     set array($c,$r) $data($idx)
  1850.     return
  1851. }
  1852.