#lang racket/base ;;; Copyright: Laurent Orseau 2010-2011 ;;; Licence: LGPL (require racket/gui/base racket/class racket/function racket/list (only-in framework panel:horizontal-dragable%)) (provide columns-box% ) #| comparators: (list-of comparator) comparator: (or (λ(val1 val2) -> boolean?) (λ(val1 data1 val2 data2) -> boolean?)) |# (define columns-box% (class panel:horizontal-dragable% ;horizontal-panel% (init-field labels [comparators #f] [callback void] [min-widths (build-list (length labels) (λ(n) #f))] ) (init [min-height 0]) (when (and comparators (not (= (length comparators) (length labels)))) (error "labels and comparators must have the same length")) (super-new [min-height min-height]) (define last-sorted #f) (define lboxes (for/list ([l (in-list labels)] [w (in-list min-widths)] [i (in-naturals)]) (let ([vp (new vertical-panel% [parent this] [min-width (or w 0)] [min-height min-height] [stretchable-width #t] )]) (when comparators (new button% [label l] [parent vp] [callback (λ _ (this-sort i))] [font tiny-control-font] [horiz-margin 0] [stretchable-width #t] )) (new list-box% [parent vp] [label (and (not comparators) l)] [choices '()] [style '(vertical-label single)] [font tiny-control-font] [horiz-margin 0] [stretchable-width #t] [callback (λ(lb evt) (set-selection (send lb get-selection)) (let ([first-visible (send lb get-first-visible-item)]) (for-each (λ(lb)(send lb set-first-visible-item first-visible)) lboxes)) (callback this evt) )] )))) (define lb-values '()) (define/public (get-number) (send (first lboxes) get-number)) (define/public (set-selection n) (for-each (λ(lb)(send lb set-selection n)) lboxes)) (define/public (clear) (for-each (λ(lb)(send lb clear)) lboxes) (set! lb-values '()) ) ;; Internal name: this-append (public [this-append append]) (define (this-append lstr [data (first lstr)]) (for-each (λ(lb str)(send lb append str data)) lboxes lstr ) (set! lb-values (cons (list lstr data) lb-values)) ) (define/public (get-data n) (send (first lboxes) get-data n)) (define/public (get-selection) (send (first lboxes) get-selection)) (define/public (get-string-list-selection) (map (λ(lb)(send lb get-string-selection)) lboxes)) (define/public (get-selection-data) (get-data (get-selection))) (public [this-sort sort]) (define (this-sort num-column) (let* ([comparator (list-ref comparators num-column)] [comparator (if (procedure-arity-includes? comparator 2) ; takes only two elements, transform to take 4: (λ(val-data1 val-data2)(comparator (first val-data1) (first val-data2))) ; else, supposed to alraedy take 4 arguments: (λ(val-data1 val-data2)(apply comparator (append val-data1 val-data2))) )] [comparator (if (equal? last-sorted num-column) (negate comparator) comparator)] [lb-vals (sort lb-values comparator #:key (λ(l)(list (list-ref (first l) num-column) (second l))) )]) (clear) (for-each (λ(l)(this-append (first l) (second l))) lb-vals) (set! last-sorted (if last-sorted #f num-column)) )) )) #| TESTS: | # (define frame (new frame% [label ""])) (define (make-comp n) (λ(val1 data1 val2 data2) (<= (list-ref data1 n) (list-ref data2 n)))) (define lbc (new columns-box% [parent frame] [labels '("a" "b" "c")] [comparators ;(list stringstring l) l))) (send frame show #t) ;|#