REBOL [
  Title: "VID LIST-VIEW Face"
  File: %list-view.r
  Author: ["Henrik Mikael Kristensen"]
  Copyright: "2005, 2006 - HMK Design"
  Created: 2005-12-29
  Date: 2006-01-21
  Version: 0.0.28
  License: {
    BSD (www.opensource.org/licenses/bsd-license.php)
    Use at your own risk.
  }
  Purpose: {General purpose listview with many features for use in VID.}
  Note: {
    This file is available at:
    http://www.hmkdesign.dk/rebol/list-view/list-view.r
    Demo and testcases available at:
    http://www.hmkdesign.dk/rebol/list-view/list-demo.r
    Docs are available in makedoc2 format at:
    http://www.hmkdesign.dk/rebol/list-view/list-view.txt
    http://www.hmkdesign.dk/rebol/list-view/list-view.html
    http://www.hmkdesign.dk/rebol/list-view/list-view-history.txt
    http://www.hmkdesign.dk/rebol/list-view/list-view-history.html
  }
  History: [
    See: http://www.hmkdesign.dk/rebol/list-view/list-view-history.html
  ]
library: [ level: 'intermidiate 
platform: 'all 
type: [ tool] 
domain: [gui  graphics] 
tested-under: [windows linux] 
support: none 
license: [bsd] 
see-also: none 
] 
]

stylize/master [
  list-field: BOX with [
    size: 0x20
    edge: make edge [size: 1x1 effect: 'ibevel color: 240.240.240]
    color: 240.240.240
    font: none
    para: make para [wrap?: false]
    flags: [field tabbed return on-unfocus input]
    feel: make ctx-text/edit bind [
      redraw: func [face act pos][
        if all [in face 'colors block? face/colors] [
          face/color: pick face/colors face <> focal-face
        ]
      ]
      detect: none
      over: none
      engage: func [face act event][
        lv: get in f: face/parent-face/parent-face 'parent-face
        switch act [
          down [
            either equal? face view*/focal-face [unlight-text] [
              focus/no-show face]
              view*/caret: offset-to-caret face event/offset
              show face
          ]
          over [
            if not-equal? view*/caret offset-to-caret face event/offset [
              if not view*/highlight-start [view*/highlight-start: view*/caret]
                view*/highlight-end: view*/caret:
                  offset-to-caret face event/offset
                show face
            ]
          ]
          key [
            edit-text face event get in face 'action
            if event/key = #"^-" [
              either equal?
                index? find face/parent-face/pane face
                length? face/parent-face/pane [
                do f/finish-edit-action
              ][
                do f/tab-edit-action
              ]
            ]
          ]
        ]
      ]
    ] in ctx-text 'self
  ]
  list-text: BOX with [
    size: 0x20
    font: make font [
      size: 11 shadow: none style: none align: 'left color: black
    ]
    para: make para [wrap?: false]
    flags: [text]
    truncated?: false
    range: func [pair1 pair2 /local r p1 p2] [
      p1: min pair1 pair2
      p2: max pair1 pair2
      r: copy []
      for x p1/1 p2/1 1 [
        for y p1/2 p2/2 1 [insert tail r as-pair x y]
      ] r
    ]
    full-text: none
    pane: none
    feel: make feel [
      over: func [face ovr /local f lv pos] [
        lv: get in f: face/parent-face/parent-face 'parent-face
        if ovr [
          lv/over-cell-text: face/full-text
          all [lv/over-row-action do bind lv/over-row-action lv]
          if face/truncated? [
            ; Show tool tip
          ]
        ]
;        either all [
;          ovr lv/ovr-cnt <> face/data; long-enough
;        ][
;          lv/ovr: true
;          f/parent-face/ovr-cnt: face/data
;          ; delay-show f
;        ][lv/ovr: none]
      ]
      engage: func [face act evt /local f lv p1 p2 r fd] [
        lv: get in f: face/parent-face/parent-face 'parent-face
        if all [
          lv/lock-list = false
          any [act = 'down act = 'alt-down]
          face
        ][
          if lv/editable? [lv/hide-edit]
          if fd: face/data [
            pos: as-pair index? find face/parent-face/pane face face/data
            either all [
              evt/shift
              any [lv/select-mode = 'multi lv/select-mode = 'multi-row]
              lv/sel-cnt
              lv/selected-column
            ] [
              lv/range: copy reduce switch lv/select-mode [
                multi [[
                  as-pair
                    index? find lv/viewed-columns lv/selected-column lv/sel-cnt
                    pos
                ]]
                multi-row [[
                  as-pair 1 lv/sel-cnt as-pair
                    length? lv/viewed-columns
                    face/data
                ]]
              ]
              p1: min lv/range/1 lv/range/2
              p2: max lv/range/1 lv/range/2
              lv/range: copy []
              r: copy sort reduce [
                index? find lv/sort-index p1/2
                index? find lv/sort-index p2/2
              ]
              for x p1/1 p2/1 1 [
                for y r/1 r/2 1 [
                  insert tail lv/range as-pair x lv/sort-index/:y
                ]
              ]
            ][
; possibility to select a row or a column by setting the coordinate to zero
; a row would have the x set to zero, such as 0x6
; a column would have the y set to zero, such as 3x0
; this would eliminate the need for sel-cnt
; and base selections solely on coordinates and ranges
; this should be a future version...
; requires rewrite of the face iteration routine
              lv/range: copy []
              lv/selected-column: pick lv/viewed-columns pos/1
              lv/old-sel-cnt: lv/sel-cnt
              lv/sel-cnt: face/data
            ]
            switch act [
              down [all [lv/list-action do bind lv/list-action lv]]
              alt-down [all [lv/alt-list-action do bind lv/alt-list-action lv]]
            ]
            show f
          ]
          if act = 'up [row: face/row]
          if evt/double-click [
            if lv/lock-list = false [
              all [
                lv/doubleclick-list-action
                do bind lv/doubleclick-list-action lv
              ]
              if all [fd lv/editable?] [lv/show-edit]
            ]
          ]
        ]
      ]
    ]
    data: 0
    row: 0
  ]
  list-view: FACE with [
    hdr: hdr-btn: hdr-fill-btn: hdr-corner-btn: lst: lst-fld: scr: edt: none
    size: 300x200
    dirty?: fill: true
    click: none
    edge: make edge [size: 0x0 color: 140.140.140 effect: 'ibevel]
    ; even, odd, select, background
    colors: [240.240.240 220.230.220 180.200.180 140.140.140]
    color: does [either fill [first colors][last colors]]
    spacing-color: third colors
    old-data-columns: copy data-columns: copy indices: copy conditions: []
    old-viewed-columns: viewed-columns: header-columns: none
    old-widths: widths: px-widths: none
    old-fonts: fonts: none
    over-cell-text: none
    types: none
    truncate: false
    drag: false
    fit: true
    scroller-width: row-height: 20
    col-widths: h-fill: 0
    spacing: 0x0
    range: copy data: []
    resize-column: selected-column: sort-column: none
    editable?: false
    last-edit: none
    h-scroll: false
    sort-index: []
    sort-modes: [asc desc nosort]
    select-modes: [single multi row multi-row column]
    select-mode: third select-modes
    drag-modes: [drag-select drag-move]
    drag-mode: first drag-modes
    sort-direction: first sort-modes
    tri-state-sort: true
    paint-columns: false
    ovr-cnt: old-sel-cnt: sel-cnt: none
    cnt: ovr: 0
    idx: 1
    lock-list: false
    follow?: true
    row-face: none
    standard-font: make system/standard/face/font [
      size: 11 shadow: none style: none align: 'left color: black
    ]
    acquire-func: []
    import: func [data [object!]] [
    ]
    export: does [
      make object! third self
    ]
    list-size: value-size: 0
    resize: func [sz] [size: sz update]
    follow: does [either follow? [scroll-here][show lst]]
    list-action: over-row-action: alt-list-action: doubleclick-list-action:
      finish-edit-action: tab-edit-action: none

    block-data?: does [not all [not empty? data not block? first data]]

    ; navigation functions

    first-cnt: does [
      either empty? filter-string [sel-cnt: 1][
        all [sel-cnt sel-cnt: sort-index/1]
      ] follow
    ]
    prev-page-cnt: does [prev-cnt/skip-size list-size]
    prev-cnt: func [/skip-size size /local sz] [
      sz: either skip-size [size][1]
      all [sel-cnt sel-cnt:
        either empty? filter-string [
          max 1 sel-cnt - sz
        ][first skip find sort-index sel-cnt negate list-size]
      ] follow
    ]
    next-cnt: func [/skip-size size /local sz] [
      sz: either skip-size [size][1]
      all [sel-cnt sel-cnt:
        either empty? filter-string [
          min length? sort-index sel-cnt + sz
        ][
          either tail? skip find sort-index sel-cnt sz [
            last sort-index
          ][
            first skip find sort-index sel-cnt sz
          ]
        ]
      ] follow
    ]
    next-page-cnt: does [next-cnt/skip-size list-size]
    last-cnt: does [
      either empty? sort-index [sel-cnt: none][
        sel-cnt: either empty? filter-string [
          length? sort-index
        ][
          last sort-index
        ] follow
      ]
    ]
    limit-sel-cnt: does [
      if all [sel-cnt not found? find sort-index sel-cnt] [last-cnt]
    ]
    selected?: does [not none? sel-cnt]

    ; filtering functions

    old-filter-string: copy filter-string: copy ""
    filter-pos: func [pos] [attempt [index? find sort-index pos]]
    filter-sel-cnt: does [all [sel-cnt filter-pos sel-cnt]]
    filter-index: copy sort-index: copy []

; in the future there should be the possibility of chaining filters and making them column specific

    filter: has [default-i i w string result g-length] [
      filter-index: copy []
      either not none? data [
        g-length: length? g: parse to-string filter-string none
        either g-length > 0 [
          ; the size of the bitset must be multipliable by 8
          result: copy i: copy
            default-i: make bitset! (g-length + 8 - (g-length // 8))
          w: 1
          until [
            insert result w
            w: w + 1
            w > g-length
          ]
          ; handle index skipping here
          repeat j length? data [
            string: mold data/:j
            ; handle column distinction here
            repeat num g-length [if find string g/:num [insert i num]]
            if i = result [
              i: copy default-i ; clear i causes a bug!
              insert tail filter-index j
            ]
          ] filter-index
        ][copy []]
      ][copy []]
    ]

    scrolling?: none
    list-sort: has [i vals] [
      sort-index: either not empty? data [
        head repeat i length? data [insert tail [] i]
      ][copy []]
      vals: copy []
      either sort-column [
        i: index? find data-columns sort-column
        repeat j length? data [
          insert tail vals reduce [data/:j/:i j copy data/:j]
        ]
        sort-index: extract/index either sort-direction = 'asc [
          sort/skip vals 3
        ][
          sort/skip/reverse vals 3
        ] 3 2
      ][sort-index]
    ]
    reset-sort: does [
      sort-column: none
      sort-direction: 'nosort
      list-sort
      foreach p hdr/pane [if p/style = 'hdr-btn [p/effect: none]]
      update
      follow
    ]
    filter-list: has [/no-show] [
      sort-index: either empty? filter [
        either any [dirty? empty? filter-string] [dirty?: false list-sort][[]]
      ][
        if any [dirty? old-filter-string <> filter-string] [
          cnt: 0
          old-filter-string: copy filter-string
          list-sort
        ]
        intersect sort-index filter-index
      ]
      if not no-show [set-scr]
    ]
    set-filter: func [string] [
      filter-string: copy string
      update
    ]
    reset-filter: does [
      old-filter-string: copy filter-string: copy ""
      update
    ]
    scroll-here: has [sl] [
      if all [
        sel-cnt
        not empty? sort-index
        select-mode <> 'column
        select-mode <> 'multi
        select-mode <> 'multi-row
      ] [
        limit-sel-cnt
        sl: index? find sort-index sel-cnt
        cnt: min sl - 1 cnt
        cnt: (max sl cnt + list-size) - list-size
        if list-size < length? sort-index [
          cnt: (min cnt + list-size value-size) - list-size
        ]
        set-scr
      ]
    ]
    set-scr: does [
      scr/redrag list-size / max 1 value-size
      scr/data: either value-size = list-size [0][
        cnt / (value-size - list-size)]
      show self
    ]

    ; data retrieval functions

    get-id: func [pos rpos h r /inserting] [
      either r [rpos][either h [filter-pos pos][
        either sel-cnt [sel-cnt][1]]]
    ]
    row: does [
      make object! insert tail foreach c data-columns [
        insert tail [] either block-data? [to-set-word c][
          to-set-word first parse c none]
      ] reduce ['copy copy ""]
    ]
    get-row: func [/over /here pos /raw rpos /keys /local id] [
      id: get-id pos rpos here raw
      if all [id select-mode <> 'multi select-mode <> 'multi-row] [
        either keys [
          obj: make row [] set obj pick data id obj][pick data id]
      ]
    ]
    find-row: func [value /col colname /local i fnd?] [
      i: 0
      fnd?: false
      either empty? data [none][
        either col [
          c: col-idx colname
          until [
            i: i + 1
            any [
              all [i = length? data data/:i/:c value <> data/:i/:c]
              all [data/:i/:c value = data/:i/:c fnd?: true]
            ]
          ]
        ][
          until [
            i: i + 1
            any [
              all [i = length? data value <> pick data i]
              all [value = pick data i fnd?: true]
            ]
          ]
        ]
        either fnd? [sel-cnt: i follow get-row][none]
      ]
    ]
    get-cell: func [cell [integer! word!] /here pos /raw rpos /local id] [
      id: get-id pos rpos here raw
      if all [id not empty? data-columns not empty? data] [
        attempt [
          pick get-row/raw id
            either word? cell [index? find data-columns cell][cell]
        ]
      ]
    ]
    get-block: has [ar r] [
      if all [not empty? range] [
        ar: 1 + abs subtract first range last range
        ar-blk: array/initial ar/2 array/initial ar/1 copy ""
        r: range/1 - 1
        repeat i length? range [
          poke pick ar-blk range/:i/2 - r/2 range/:i/1 - r/1
            pick pick data range/:i/2 range/:i/1
        ] ar-blk
      ] copy []
    ]
    unique-values: func [column [word!]] [get-block as-pair col-idx column 0]

    ; data manipulation functions

;when starting to append empty rows, the size of the list is not known

    unkey: func [vals] [
      copy/deep either all [block? vals find vals set-word!] [
        extract/index vals 2 2][vals]
    ]
    col-idx: func [word] [index? find data-columns word]
    clear: does [data: copy [] dirty?: true filter-list]
    insert-row: func [
      /here pos [integer!] /raw rpos [integer!] /values vals /local id
    ] [
      id: get-id pos rpos here raw
      either empty? data [
        insert/only data either values [unkey vals][make-row]
      ][
        all [
          id data/:id insert/only at data id
            either values [unkey vals][make-row]
        ]
      ]
      dirty?: true
      filter-list
      get-row/raw id
    ]
    insert-block: func [pos [integer!] vals] [
      all [pos data/:pos insert at data pos vals filter-list]
    ]
    append-row: func [/values vals /no-select] [
      insert/only tail data either values [unkey vals][make-row]
      dirty?: true
      filter-list/no-show if not no-select [last-cnt] show lst
      get-row/raw length? data
    ]
    append-block: func [vals][
      insert tail data vals
      dirty?: true
      filter-list/no-show last-cnt show lst
    ]
    remove-row: func [/here pos [integer!] /raw rpos [integer!] /local id] [
      id: get-id pos rpos here raw
      all [id data/:id remove at data id dirty?: true filter-list]
    ]
    remove-block: func [pos range] [
      for i pos range 1 [remove at data pick sort-index i]
      dirty?: true
      filter-list
    ]
    remove-block-here: func [range] [remove-block range filter-sel-cnt]
    change-row: func [
      vals /here pos [integer!] /raw rpos [integer!] /top /local id tmp
    ] [
      id: get-id pos rpos here raw
      all [id data/:id change/only at data id unkey vals]
      if top [
        tmp: copy get-row/raw id
        remove-row/raw id
        insert-row/values/raw tmp 1
        first-cnt
      ]
      dirty?: true
      filter-list
      get-row/raw id
    ]
    change-block: func [pos [integer! pair!] vals [block!]] [
      either pair? pos [][
        for i sel-cnt length? vals 1 [change at data pick sort-index i]
      ]
      dirty?: true
      filter-list
    ]
    change-block-here: func [vals [block!]] [
      switch select-mode [
        single [change-block as-pair sel-cnt col-idx selected-column vals]
        row [change-block sel-cnt reduce [vals]]
        multi [change-block range/1 vals]
        multi-row [change-block range/1 vals]
        column [change-block range/1 vals]
      ]
    ]
    change-cell: func [
      col val /here pos [integer!] /raw rpos [integer!] /top /local id tmp
    ] [
      id: get-id pos rpos here raw
      if all [id data/:id] [
        change at pick data id col-idx col val filter-list
        if top [
          tmp: copy data/:id
          remove at data id
          data/1: tmp
        ]
        get-row/raw id
      ]
    ]
    make-row: does [
      either block-data? [array/initial length? data-columns copy ""][copy ""]
    ]
    acquire: does [
      if not empty? acquire-func [append-row/values do acquire-func]
    ]

    ; visual editing functions

    show-edit: func [/column col /local vals] [
      if sel-cnt [
        edt/offset/y:
          (lst/subface/size/y) * filter-sel-cnt
        vals: get-row
        repeat i length? viewed-columns [
          edt/pane/:i/text: edt/pane/:i/data: pick vals indices/:i
        ]
        show edt
        if not selected-column [selected-column: first viewed-columns]
        focus pick edt/pane index? find viewed-columns
          either column [col][selected-column]
      ]
    ]
    hide-edit: has [vals] [
      last-edit: either edt/show? [
        vals: copy get-row
        repeat i length? edt/pane [
          change/only at vals indices/:i get in pick edt/pane i 'text
        ]
        change-row vals
        hide edt
        get-row
      ][none]
    ]

    ; initialization

    init-code: has [o-set e-size val resize-column-index no-header-columns] [
      if none? data [data: copy []]
      if empty? data-columns [
        data-columns: either empty? data [
          copy [column1]
        ][
          either block-data? [
            foreach d first data [
              append [] either attempt [to-integer d]['Number][to-word d]
            ]
          ][copy [column1]]
        ]
      ]
      if none? viewed-columns [viewed-columns: copy data-columns]
      no-header-columns: false
      if none? header-columns [
        no-header-columns: true header-columns: copy data-columns]
      if all [fit none? resize-column] [resize-column: first viewed-columns]
      if none? types [types: copy array/initial length? data-columns 'text]

      indices: copy []
      either empty? viewed-columns [
        repeat i length? data-columns [insert tail indices i]
      ][
        foreach f viewed-columns [
          all [val: find data-columns f insert tail indices index? val]
        ]
      ]

      ; set panes up here
      hdr: make face [
        edge: none
        size: 0x20
        pane: copy []
      ]
      hdr-fill-btn: make face [
        style: hdr-fill-btn
        color: 120.120.120
        var: none
        edge: make edge [size: 0x1 color: 140.140.140 effect: 'bevel]
      ]
      hdr-btn: make face [
        edge: none
        style: 'hdr-btn
        size: 20x20
        color: 140.140.140
        var: none
        eff-blk: copy/deep [draw [
          pen none fill-pen white polygon 3x5 7x14 11x5] flip
        ]
        show-sort-hdr: func [face] [
          if all [sort-column face/var = sort-column] [
            face/effect: switch sort-direction [
              asc [head insert tail copy eff-blk 1x1]
              desc [head insert tail copy eff-blk 1x0]
            ][none]
          ]
        ]
        corner: none
        font: make font [align: 'left shadow: 0x1 color: white]
        feel: make feel [
          engage: func [face act evt][
            if editable? [hide-edit]
            switch act [
              down [
                foreach h hdr/pane [all [h/style = 'hdr-btn h/effect: none]]
                either face/corner [sort-column: none][
                  sort-column: face/var
                  either tri-state-sort [
                    sort-modes: either tail? next sort-modes [
                      head sort-modes][next sort-modes]
                    if 'nosort = sort-direction: first sort-modes [
                      sort-column: none
                    ]
                    show-sort-hdr face
                  ][
                    sort-direction:
                      either sort-direction = 'asc ['desc]['asc]
                    face/effect: head insert tail copy eff-blk
                      either sort-direction = 'asc [1x1][1x0]
                  ]
                ]
              ]
              alt-down [
                foreach h hdr/pane [all [h/style = 'hdr-btn h/effect: none]]
                sort-column: none
              ]
            ]
            either any [act = 'down act = 'alt-down][
              face/edge/effect: 'ibevel
              list-sort
              if not empty? filter [
                sort-index: intersect sort-index filter-index
              ]
              follow
            ][face/edge/effect: 'bevel]
            show face/parent-face/parent-face
          ]
        ]
      ]
      hdr-corner-btn: make face [
        edge: none
        style: 'hdr-corner-btn
        size: 20x20
        color: 140.140.140
        effect: none
        var: none
        feel: make feel [
          engage: func [face act evt][
            if editable? [hide-edit]
            either any [act = 'down act = 'alt-down] [
              face/edge/effect: 'ibevel
              repeat i subtract length? hdr/pane 1 [hdr/pane/:i/effect: none]
              sort-column: none
              sort-direction: 'nosort
              list-sort
              follow
            ][face/edge/effect: 'bevel]
            show face/parent-face/parent-face
          ]
        ]
      ]
      lst: make face [
        edge: none
        size: 100x100
        subface: none
        feel: make feel [
          over: func [face ovr /local f lv] [
          ]
        ]
      ]
      scr: make-face get-style 'scroller
      hscr: make-face get-style 'scroller
      edt: make face [
        edge: none
        text: ""
        pane: none
        show?: false
      ]
 
      ; initialize widths, cols and fonts

      if any [
        none? px-widths
        old-widths <> widths
        old-size <> size
        old-viewed-columns <> viewed-columns
      ] [
        if any [
          none? widths
          all [old-viewed-columns old-viewed-columns <> viewed-columns]
        ] [
          widths: array/initial
            length? viewed-columns to-decimal 1 / length? viewed-columns
        ]
        px-widths: copy widths
        ; Calculate this properly!
        repeat i length? widths [
          if decimal? widths/:i [
            px-widths/:i: to-integer widths/:i * (size/x - scr/size/x)
          ]
        ]
        if any [
          none? fonts
          all [old-fonts old-fonts <> fonts]
        ] [
          fonts: array/initial length? viewed-columns make standard-font []
        ]
        old-viewed-columns: copy viewed-columns
        old-widths: copy widths
      ]

      e-size: size - (2 * edge/size)
      hdr/size/x: e-size/x
      scr/resize/x scroller-width
      lst/size: as-pair
        e-size/x - scr/size/x
        e-size/y - add
          either h-scroll [scroller-width][0]
          lst/offset/y: either empty? header-columns [0][hdr/size/y] 

      scr/resize/y lst/size/y
      col-widths: do replace/all trim/with mold px-widths "[]" " " " + "
      either h-scroll [
        hscr/offset/y: lst/size/y + lst/offset/y
        hscr/axis: 'x
        hscr/resize as-pair lst/size/x either h-scroll [scroller-width][0]
        hscr/redrag divide (size/x - scroller-width) col-widths
;        hscr/redrag lst/size/x / (size/x - scroller-width)
      ][hscr/size: 0x0]
      scr/offset: as-pair lst/size/x lst/offset/y

      either fit [
        resize-column-index: any [
          attempt [index? find viewed-columns resize-column] 1]
        sz: lst/size/x
        repeat i length? px-widths [
          all [resize-column-index <> i sz: sz - px-widths/:i]
        ]
        if resize-column-index [px-widths/:resize-column-index: sz]
      ][
        if col-widths < lst/size/x [h-fill: lst/size/x - col-widths]
      ]
      
      lst-lo: has [lo edt-lo f sp] [
        lst/subface: layout/tight either row-face [row-face][
          lo: copy compose [across space 0 pad (as-pair 0 spacing/y)]
          repeat i length? viewed-columns [
            sp: either i = length? viewed-columns [0][spacing/x]
            insert tail lo compose [
              list-text (as-pair px-widths/:i - sp row-height)
              pad (as-pair sp 0)
            ]
          ]
          if h-fill > 0 [
            insert insert tail lo 'list-text as-pair h-fill row-height
          ]
          lo
        ]
        either row-face [row-height: lst/subface/size/y][
          fonts: reduce fonts
          repeat i length? lst/subface/pane [
            f: either i > length? fonts [last fonts][fonts/:i]
            lst/subface/pane/:i/font: make standard-font f
          ]
        ]
        lst/subface/color: spacing-color
      ]

      if not empty? viewed-columns [lst-lo]
      pane: reduce [hdr lst scr edt hscr]

      ; list initialization

      lst/subface/size/x: lst/size/x
      list-size: does [
      ; have a look at this... it seems that it doesn't adhere to row-spacing
        to-integer lst/size/y / lst/subface/size/y
      ]
      value-size: does [
        length? either empty? filter-string [data][
          either empty? filter-index [[]][sort-index]]
      ]
      scr/action: has [value] [
        scrolling?: true
        value: to-integer scr/data * max 0 value-size - list-size
        if all [cnt <> value][
          cnt: value
          show lst
        ]
      ]
      hscr/action: has [value] [
        scrolling?: true
        value: do replace/all trim/with mold px-widths "[]" " " " + "
        hdr/offset/x: lst/offset/x: negate (value - lst/size/x) * hscr/data
        show self
      ]

      edt/pane: get in layout/tight either row-face [row-face][
        edt-lo: copy [across space 0]
        repeat i length? viewed-columns [
          insert tail edt-lo compose [
            list-field (lst/subface/pane/:i/size - 0x1)
          ]
          insert/only tail edt-lo
            either i = length? viewed-columns [[hide-edit]][[]]
          insert tail edt-lo reduce ['pad spacing/x]
        ] edt-lo
      ] 'pane
      foreach e edt/pane [e/font: make standard-font []]

      edt/size: lst/subface/size

      set-scr
      filter-list
      cell?: []
      row?: []

      lst/color: either fill [
        either even? list-size [second colors][first colors]
      ][last colors]
      
      ; supply list with data

      lst/pane: func [face index /local c-index j k s o-set t col sp][

; in here we need to manage how to make conditions.
; we need to create objects from rows either precalced or on the fly


        col: attempt [index? find viewed-columns selected-column]
        either integer? index [
          c-index: index + cnt
          if all [index <= list-size any [fill sort-index/:c-index]] [
            o-set: k: 0
            repeat i length? lst/subface/pane [
              sp: either i = length? lst/subface/pane [0][spacing/x]
              s: lst/subface
              j: s/pane/:i
              s/offset/y: (index - 1 * s/size/y) - spacing/y
              if not scrolling? [
                if not row-face [j/offset/x: o-set]
                o-set: o-set + j/size/x + sp
                if all [not row-face resize-column = data-columns/:i] [
                  j/size/x: px-widths/:i - sp
                ]
              ]
              j/color: either
                switch select-mode [
                  single [
                    all [
                      sort-index/:c-index
                      sel-cnt = sort-index/:c-index
                      col = i
                    ]
                  ]
                  row [
                    all [sort-index/:c-index sel-cnt = sort-index/:c-index]]
                  column [all [sort-index/:c-index col = i]]
                  multi [
                    all [
                      col
                      sort-index/:c-index
                      any [
                        all [
                          sel-cnt = sort-index/:c-index
                          col = i
                        ]
                        found? find range as-pair i sort-index/:c-index
                      ]
                    ]
                  ]
                  multi-row [
                    all [
                      col
                      sort-index/:c-index
                      any [
                        all [sort-index/:c-index sel-cnt = sort-index/:c-index]
                        found? find range as-pair i sort-index/:c-index
                      ]
                    ]
                  ]
                ] [third colors][pick colors c-index // 2 + 1]
              if all [not row-face h-fill > 0 i = length? lst/subface/pane] [
                j/color: j/color * 0.9
              ]
              if flag-face? j 'text [
                k: k + 1
                j/data: sort-index/:c-index
                j/row: index
                j/text: j/full-text: attempt [either block-data? [
                  pick pick data sort-index/:c-index indices/:k 
                ][
                  pick data sort-index/:c-index
                ]]
                either image? j/text [
                  j/effect: compose/deep [
                    draw [
                      translate ((j/size - j/text/size) / 2)
                      image (j/text)
                    ]
                  ]
                  j/text: none
                ][
                  j/effect: none
                  either all [
                    j/text
                    truncate
                    not empty? j/text
                    (t: index? offset-to-caret j as-pair j/size/x 15) <= 
                      length? to-string j/text
                  ] [
                    j/truncated?: true
                    j/text: join copy/part to-string j/text t - 3 "..."
                  ][j/truncated?: false]
                ]
              ]
            ]
            s/size/y: row-height + spacing/y +
              either index = list-size [spacing/y][0]
            return s
          ]
        ][return to-integer index/y / lst/subface/size/y + 1]
      ]

      ; header initialization

      if not empty? header-columns [
        o-set: o-size: 0
        repeat i min length? header-columns length? viewed-columns [
          insert tail hdr/pane make hdr-btn compose [
            corner: none
            edge: (make edge [size: 1x1 color: 140.140.140 effect: 'bevel])
            offset: (as-pair o-set 0)
            text: (
              to-string to-word pick header-columns
                either all [no-header-columns not empty? indices] [
                  indices/:i][i]
            )
            var: (
              either all [sort-column 1 = length? viewed-columns] [
                to-lit-word sort-column][to-lit-word viewed-columns/:i]
            )
            size: (as-pair
              o-size: either 1 = length? header-columns [
                either any [not fit h-scroll] [px-widths/:i][lst/size/x]
              ][px-widths/:i] hdr/size/y
            )
            related: 'hdr-btns
          ]
          o-set: o-set + o-size
        ]
        if h-fill > 0 [
          insert tail hdr/pane make hdr-fill-btn compose [
            size: (as-pair h-fill hdr/size/y)
            offset: (as-pair o-set 0)
          ] o-set: o-set + h-fill
        ]
        glyph-scale: (min scroller-width hdr/size/y) / 3
        glyph-adjust: as-pair
          scroller-width / 2 - 1
          hdr/size/y / 2 - 1
        insert tail hdr/pane make hdr-corner-btn compose/deep [
;          corner: true
          offset: (as-pair o-set 0)
          color: 140.140.140
          edge: (make edge [size: 1x1 color: 140.140.140 effect: 'bevel])
          size: (as-pair scr/size/x hdr/size/y)
          effect: [
            draw [
              anti-alias off
              pen none fill-pen 200.200.200 polygon
              (0x-1 * glyph-scale + glyph-adjust)
              (1x0 * glyph-scale + glyph-adjust)
              (0x1 * glyph-scale + glyph-adjust)
              (-1x0 * glyph-scale + glyph-adjust)
            ]
          ]
        ]

        hdr/pane: reduce hdr/pane
        hdr/size/x: size/x
        foreach h hdr/pane [all [h/style = 'hdr-btn h/show-sort-hdr h]]
      ]
    ]
    init: [init-code]
    update: func [/force] [
;      dirty?: true
      scrolling?: false
      either force [init-code][
        either size <> old-size [init-code][filter-list]]
      if all [self/parent-face show?] [show self]
    ]
  ]
]