Advertisement
Guest User

Spreadsheet editor in Racket

a guest
Jul 19th, 2014
400
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lisp 14.57 KB | None | 0 0
  1. (require racket/gui
  2.          racket/draw
  3.          data/gvector)
  4. (require (planet williams/table-panel:1:2/table-panel))
  5.  
  6. ;; Helper function
  7. (define (find-max list get-value)
  8.   (for/fold ((max-value 0)) ((x list))
  9.     (max (get-value x) max-value)))
  10.  
  11. ;; Global constants describing the table
  12. (define n-rows 10000)
  13. (define n-columns 1000)
  14. (define rownum-column-width 70)
  15.  
  16. ;; Accessing table cells (stub)
  17. (define cell-contents-hash (make-hash))
  18.  
  19. (define (get-cell-contents row column)
  20.   (hash-ref cell-contents-hash (list row column) ""))
  21.  
  22. (define (set-cell-contents! row column contents)
  23.   (hash-set! cell-contents-hash (list row column) contents))
  24.  
  25. ;; Column header (stub)
  26. (define (get-column-label i)
  27.   (number->string (+ 1 i)))
  28.  
  29. ;; Number of row and column buttons currently visible
  30. (define n-row-buttons 0)
  31. (define n-column-buttons 0)
  32. (define starting-row 0)
  33. (define starting-column 0)
  34.  
  35. ;; All visible row buttons
  36. (define row-buttons (gvector))
  37.  
  38. ;; All column buttons (visible or not)
  39. (define all-column-buttons (make-vector n-columns))
  40.  
  41. ;; Helper functions for scrolling
  42. (define (calculate-starting-column pivot width)
  43.   (define n-buttons 0)
  44.   (define column pivot)
  45.   (define total-length 0)
  46.   (let loop ((i pivot)
  47.              (length 0))
  48.     (cond
  49.       ((>= i (vector-length all-column-buttons))
  50.        (let loop1 ((i pivot) (length length))
  51.          (cond
  52.            ((zero? i) 0)
  53.            ((<= (+ length (send (vector-ref all-column-buttons (- i 1)) min-width)) width)
  54.             (loop1 (- i 1) (+ length (send (vector-ref all-column-buttons (- i 1)) min-width))))
  55.            (else i))))
  56.       ((> length width) pivot)
  57.       (else
  58.        (loop (+ i 1) (+ length (send (vector-ref all-column-buttons i) min-width)))))))
  59.  
  60. (define (calculate-starting-row pivot height)
  61.   (define button-height (send (gvector-ref row-buttons 0) get-height))
  62.   (define nbuttons-visible (floor (/ height button-height)))
  63.   (cond
  64.     ((< (+ pivot nbuttons-visible) n-rows) pivot)
  65.     ((>= nbuttons-visible n-rows) 0)
  66.     (else (- n-rows nbuttons-visible))))
  67.  
  68. ;; Helper functions for table cell editing
  69. (define (detect-column-by-x x)
  70.   (define-values (col _)
  71.     (for/fold ((result starting-column)
  72.                (length 0))
  73.       ((button (send hpanel-top get-children)))
  74.       #:break (<= x (+ length (send button min-width)))
  75.       (values (+ result 1)
  76.               (+ length (send button min-width)))))
  77.   (if (< col n-columns) col #f))
  78.  
  79. (define (detect-row-by-y y)
  80.   (define rownum
  81.     (+ starting-row (floor (/ y (send (gvector-ref row-buttons 0) get-height)))))
  82.   (if (< rownum n-rows) rownum #f))
  83.  
  84. ;; Panel with column buttons
  85. (define my-horizontal-panel%
  86.   (class horizontal-panel%
  87.     (super-new)
  88.     (define/override (container-size info)
  89.       (values 0 (find-max info second)))
  90.     (define/override (on-size w h)
  91.       (define total-length 0)
  92.       (set! n-column-buttons 0)
  93.       (define flag #f)
  94.       (send this begin-container-sequence)
  95.       ;; Delete buttons that are completely out of sight
  96.       (for ((child (send this get-children))
  97.             (i (in-naturals starting-column)))
  98.         (set! total-length (+ total-length (send child min-width)))
  99.         (cond
  100.           ((> total-length w)
  101.            (cond
  102.              (flag
  103.               (send this delete-child child))
  104.              (else
  105.               (set! n-column-buttons (+ 1 n-column-buttons))
  106.               (set! flag #t))))
  107.           (else
  108.            (set! n-column-buttons (+ 1 n-column-buttons)))))
  109.       ;; Add buttons till there is place on the panel
  110.       (let loop ()
  111.         (when (and (< total-length w)
  112.                    (< (+ starting-column n-column-buttons) (vector-length all-column-buttons)))
  113.           (define button (vector-ref all-column-buttons (+ starting-column n-column-buttons)))
  114.           (send this add-child button)
  115.           (set! n-column-buttons (+ 1 n-column-buttons))
  116.           (set! total-length (+ total-length (send button min-width)))
  117.           (loop)))
  118.       (send this end-container-sequence))))
  119.  
  120. ;; Panel with row buttons
  121. (define my-vertical-panel%
  122.   (class vertical-panel%
  123.     (super-new)
  124.     (define/override (container-size info)
  125.       (values 0 0))
  126.     (define/override (on-size w h)
  127.       (define total-length 0)
  128.       (set! n-row-buttons 0)
  129.       (send this begin-container-sequence)
  130.       ;; Delete buttons that are completely out of sight
  131.       (for ((child (send this get-children))
  132.             (i (in-naturals)))
  133.         (set! total-length (+ total-length (send child get-height)))
  134.         (cond
  135.           ((> total-length h)
  136.            (send this delete-child child))
  137.           (else
  138.            ;; ... and relabel buttons that are visible
  139.            (send child set-label (number->string (+ 1 n-row-buttons starting-row)))
  140.            (set! n-row-buttons (+ 1 n-row-buttons))
  141.            )))
  142.       ;; Add buttons till there is place on the panel
  143.       (let loop ()
  144.         (when (and (< total-length h)
  145.                    (< (+ n-row-buttons starting-row) n-rows))
  146.           (define button (void))
  147.           (cond
  148.             ((< n-row-buttons (gvector-count row-buttons))
  149.              ;; Relabel and reuse a previously added and deleted button
  150.              (set! button (gvector-ref row-buttons n-row-buttons))
  151.              (send button set-label (number->string (+ 1 n-row-buttons starting-row)))
  152.              (send this add-child button))
  153.             (else
  154.              ;; Add a new button
  155.              (set! button (new button%
  156.                                (parent this)
  157.                                (stretchable-width #t)
  158.                                (horiz-margin 0)
  159.                                (vert-margin 0)
  160.                                (label (number->string (+ 1 n-row-buttons starting-row)))))
  161.              (gvector-add! row-buttons button)))
  162.           (set! n-row-buttons (+ 1 n-row-buttons))
  163.           (set! total-length (+ total-length (send button get-height)))
  164.           (loop)))
  165.       (send this end-container-sequence)
  166.       )))
  167.  
  168. ;; Transient text snip to edit cell contents
  169. (define text-snip (void))
  170. (define text-snip-row (void))
  171. (define text-snip-column (void))
  172.  
  173. (define (done-with-text-snip save-contents?)
  174.   (unless (void? text-snip)
  175.     (when save-contents?
  176.       (set-cell-contents! text-snip-row text-snip-column (send (send text-snip get-editor) get-text)))
  177.     (define ts text-snip)
  178.     ;; void the text-snip to prevent another (done-with-text-snip) from on-focus
  179.     (set! text-snip (void))
  180.     (send ts release-from-owner)
  181.     (send editor-canvas refresh)))
  182.  
  183. ;; Specialized text% for editing the cell contents
  184. (define my-text%
  185.   (class text%
  186.     (super-new)
  187.     (define/override (on-focus on?)
  188.       (unless on?
  189.         (done-with-text-snip #t))
  190.       (super on-focus on?))
  191.     (define/override (on-char event)
  192.       (define key-code (send event get-key-code))
  193.       (when (equal? key-code 'escape)
  194.         (done-with-text-snip #f))
  195.       (when (equal? key-code #\return)
  196.         (done-with-text-snip #t))
  197.       (super on-char event))))
  198.  
  199. (define brown-delta-fg
  200.   (send (make-object style-delta%) set-delta-foreground (make-object color% "brown")))
  201. (define yellow-delta-bg
  202.   (send (make-object style-delta%) set-delta-background (make-object color% "yellow")))
  203.  
  204. ;; Specialized pasteboard% to place the text snip on
  205. (define my-pasteboard%
  206.   (class pasteboard%
  207.     (super-new)
  208.     (define/override (on-default-event event)
  209.       (define type (send event get-event-type))
  210.       (cond
  211.         ((equal? type 'left-down)
  212.          (unless (void? text-snip)
  213.            (done-with-text-snip #t))
  214.          (define x (send event get-x))
  215.          (define y (send event get-y))
  216.          (set! text-snip-column (detect-column-by-x x))
  217.          (define column-height (send (gvector-ref row-buttons 0) get-height))
  218.          (define column-width (send (vector-ref all-column-buttons text-snip-column) get-width))
  219.          (define column-x (send (vector-ref all-column-buttons text-snip-column) get-x))
  220.          (set! text-snip-row (detect-row-by-y y))
  221.          (define column-y (send (gvector-ref row-buttons (- text-snip-row starting-row)) get-y))
  222.          (define text-obj (new my-text%))
  223.          (send text-obj insert (get-cell-contents text-snip-row text-snip-column) 0)
  224.          (send text-obj extend-position 0)
  225.          (set! text-snip (new editor-snip%
  226.                          (editor text-obj)
  227.                          (left-margin 0) (right-margin 0) (top-margin 0) (bottom-margin 0)
  228.                          (with-border? #t)
  229.                          (min-width column-width)
  230.                          (max-width column-width)
  231.                          (min-height column-height)
  232.                          (max-height column-height)))
  233.          (send this insert text-snip column-x column-y)
  234.          (send text-obj change-style yellow-delta-bg)
  235.          (send this change-style brown-delta-fg text-snip)
  236.          (send this set-caret-owner text-snip)
  237.          ))
  238.       (void))))
  239.  
  240. ;; Specialized canvas to draw the grid lines and cells' contents
  241. (define my-editor-canvas%
  242.   (class editor-canvas%
  243.     (super-new)
  244.     (define/override (on-paint)
  245.       (super on-paint)
  246.       (define dc (send this get-dc))
  247.       (define-values (width height) (send dc get-size))
  248.       (define border-x 0)
  249.       (define border-y 0)
  250.       (when (positive? n-column-buttons)
  251.         (define border-column-button
  252.           (vector-ref all-column-buttons
  253.                       (+ starting-column n-column-buttons -1)))
  254.         (set! border-x (+ (send border-column-button get-x)
  255.                             (send border-column-button get-width))))
  256.       (when (positive? n-row-buttons)
  257.         (define border-row-button
  258.           (gvector-ref row-buttons
  259.                       (- n-row-buttons 1)))
  260.         (set! border-y (+ (send border-row-button get-y)
  261.                           (send border-row-button get-height))))
  262.       (for ((btn (send hpanel-top get-children)))
  263.         (send dc draw-line
  264.               (- (send btn get-x) 1) 0
  265.               (- (send btn get-x) 1) (- border-y 1)))
  266.       (send dc draw-line
  267.               (- border-x 1) 0
  268.               (- border-x 1) (- border-y 1))
  269.       (for ((btn row-buttons))
  270.         (send dc draw-line
  271.               0 (- (send btn get-y) 1)
  272.               (- border-x 1) (- (send btn get-y) 1)))
  273.       (send dc draw-line
  274.               0 (- border-y 1)
  275.               (- border-x 1) (- border-y 1))
  276.       (for ((col-btn (send hpanel-top get-children))
  277.             (column (in-naturals starting-column)))
  278.         (for ((row-btn row-buttons)
  279.               (row (in-naturals starting-row)))
  280.           (send dc set-clipping-rect (send col-btn get-x) (send row-btn get-y)
  281.                 (send col-btn get-width) (send row-btn get-height))
  282.           (send dc draw-text (get-cell-contents row column) (send col-btn get-x) (send row-btn get-y) )
  283.           (send dc set-clipping-region #f)))
  284.       )))
  285.  
  286. ;; Main window
  287. (define frame (new frame% (label "Table editor")))
  288.  
  289. ;; Main window 3x3 layout
  290. (define table-panel
  291.   (new table-panel%
  292.     (parent frame)
  293.     (alignment '(center center))
  294.     (spacing 0)
  295.     (stretchable-width #t)
  296.     (stretchable-height #t)
  297.     (horiz-margin 0)
  298.     (vert-margin 0)
  299.     (dimensions '(3 3))))
  300.  
  301. ;; Top-left corner
  302. (new pane%
  303.      (parent table-panel)
  304.      (horiz-margin 0)
  305.      (vert-margin 0)
  306.      (stretchable-width #f)
  307.      (stretchable-height #f))
  308.  
  309. (define hpanel-top
  310.     (new my-horizontal-panel%
  311.          (parent table-panel)
  312.          (border 0)
  313.          (stretchable-width #t)
  314.          (stretchable-height #f)))
  315.  
  316. (for ((i (in-range n-columns)))
  317.   (vector-set!
  318.    all-column-buttons i
  319.    (new button%
  320.         (parent hpanel-top)
  321.         (stretchable-width #f)
  322.         (horiz-margin 0)
  323.         (vert-margin 0)
  324.         (min-width 80)
  325.         (style '(deleted))
  326.         (label (get-column-label i)))))
  327.  
  328. ;; Top-right corner
  329. (new pane%
  330.      (parent table-panel)
  331.      (horiz-margin 0)
  332.      (vert-margin 0)
  333.      (stretchable-width #f)
  334.      (stretchable-height #f))
  335.  
  336. (define vpanel-left
  337.   (new my-vertical-panel%
  338.        (parent table-panel)
  339.        (border 0)
  340.        (stretchable-width #f)
  341.        (stretchable-height #t)
  342.        (min-width rownum-column-width)))
  343.  
  344.  
  345. ;; Canvas (middle cell in the 3x3 layout)
  346. (define editor-canvas
  347.   (new my-editor-canvas%
  348.        (editor (new my-pasteboard%))
  349.        (vertical-inset 0)
  350.        (horizontal-inset 0)
  351.        (parent table-panel)
  352.        (style '(no-border no-hscroll no-vscroll))))
  353.  
  354. (define vslider
  355.   (new slider%
  356.        (parent table-panel)
  357.        (stretchable-width #f)
  358.        (stretchable-height #t)
  359.        (label "")
  360.        (style '(vertical plain))
  361.        (min-value 1)
  362.        (max-value n-rows)
  363.        (callback
  364.         (lambda (self event)
  365.           (define new-starting-row
  366.             (calculate-starting-row
  367.                                   (- (send self get-value) 1)
  368.                                   (send vpanel-left get-height)))
  369.           (unless (equal? starting-row new-starting-row)
  370.             (set! starting-row new-starting-row)
  371.             (send vpanel-left on-size
  372.                   (send vpanel-left get-width)
  373.                   (send vpanel-left get-height))
  374.             (send editor-canvas refresh))))))
  375.  
  376. ;; Bottom left corner
  377. (new pane%
  378.      (parent table-panel)
  379.      (stretchable-width #f)
  380.      (stretchable-height #f)
  381.      (horiz-margin 0)
  382.      (vert-margin 0)
  383.      (min-width rownum-column-width))
  384.  
  385. (define hslider
  386.   (new slider%
  387.        (parent table-panel)
  388.        (label "")
  389.        (style '(horizontal plain))
  390.        (min-value 1)
  391.        (max-value n-columns)
  392.        (stretchable-height #f)
  393.        (callback
  394.         (lambda (self event)
  395.           (define new-starting-column
  396.             (calculate-starting-column
  397.                                   (- (send self get-value) 1)
  398.                                   (send hpanel-top get-width)))
  399.           (unless (equal? starting-column new-starting-column)
  400.             (set! starting-column new-starting-column)
  401.             (send hpanel-top begin-container-sequence)
  402.             (send hpanel-top change-children (lambda (x) '()))
  403.             (send hpanel-top on-size
  404.                   (send hpanel-top get-width)
  405.                   (send hpanel-top get-height))
  406.             (send hpanel-top end-container-sequence)
  407.             (send editor-canvas refresh)
  408.             )))))
  409.  
  410. ;; Bottom right corner
  411. (new pane%
  412.      (parent table-panel)
  413.      (horiz-margin 0)
  414.      (vert-margin 0)
  415.      (stretchable-width #f)
  416.      (stretchable-height #f))
  417.  
  418. (send frame resize 600 400)
  419. (send frame show #t)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement