
compiler/garbage collector - fixed div by zero bug in pong.ss demo - removed defunct net-pong demos and ft-spread - added documentation and demo sheet for spreadsheet svn: r3329
636 lines
26 KiB
Scheme
636 lines
26 KiB
Scheme
(module spread (lib "frtime-big.ss" "frtime")
|
|
|
|
(require (lib "class.ss")
|
|
(all-except (lib "mred.ss" "mred") send-event)
|
|
(rename mzscheme mz:define-struct define-struct)
|
|
"preprocessor2.ss"
|
|
(lifted "ss-funcs.ss" inflate-data)
|
|
"quotes.ss"
|
|
(as-is:unchecked (lib "match.ss") match-lambda)
|
|
(as-is:unchecked (lib "frp-core.ss" "frtime") signal-value
|
|
proc->signal)
|
|
(lib "framework.ss" "framework")
|
|
(as-is:unchecked (lib "string.ss") expr->string)
|
|
(as-is:unchecked (lib "etc.ss") build-vector)
|
|
;(lifted mzscheme regexp-match)
|
|
(as-is:unchecked mzscheme make-hash-table hash-table-put! hash-table-get
|
|
hash-table-remove! let*-values vector-set! make-string
|
|
exn?
|
|
open-input-file open-output-file read write hash-table-map
|
|
file-exists? delete-file open-input-string eof
|
|
flush-output close-output-port dynamic-require))
|
|
;;
|
|
;; TO DO:
|
|
;;
|
|
;; rewrite cleanly
|
|
;; case-insensitive and relative identifier expansion with ranges
|
|
;; select multiple cells
|
|
;; fill row or column
|
|
;; copy and paste formula
|
|
;; disable text field when selection empty
|
|
;; allow resizing of columns
|
|
|
|
;; KNOWN BUGS:
|
|
;; when loading file, expression text field does not update
|
|
|
|
(define stock-price
|
|
(opt-lambda (name-string [seconds-between 1200])
|
|
(lift-strict (lambda (name _) (stock-quote name)) name-string (quotient seconds seconds-between))))
|
|
|
|
(define-syntax for
|
|
(syntax-rules (=)
|
|
[(_ (var = init) condn delta proc ...)
|
|
(let loop ([var init])
|
|
(when condn
|
|
proc ...
|
|
(loop (delta var))))]))
|
|
|
|
(set-cell! raise-exceptions #t)
|
|
|
|
(mz:define-struct ss-loc (row col))
|
|
|
|
(define (ss-format val)
|
|
(if (or (and (signal? val)
|
|
(undefined? (signal-value val)))
|
|
(and (not (signal? val))
|
|
(undefined? val)))
|
|
""
|
|
(format "~a" (signal-value val))))
|
|
|
|
(define (@e r c)
|
|
(ss-get-cell-value/force r c))
|
|
|
|
(define (@c r0 r1 c)
|
|
(build-list (add1 (abs (- r1 r0)))
|
|
(lambda (i)
|
|
(@e (+ i (min r1 r0)) c))))
|
|
|
|
(define (@r r c0 c1)
|
|
(build-list (add1 (abs (- c1 c0)))
|
|
(lambda (i)
|
|
(@e r (+ i (min c1 c0))))))
|
|
|
|
(define (@m r0 r1 c0 c1)
|
|
(build-list (add1 (abs (- c1 c0)))
|
|
(lambda (i)
|
|
(@c r0 r1 (+ i (min c1 c0))))))
|
|
|
|
(define frame
|
|
(instantiate frame% ("Spreadsheet") (width 600) (height 400)))
|
|
|
|
(define menu-bar
|
|
(instantiate menu-bar% (frame)))
|
|
|
|
(define file-menu
|
|
(instantiate menu% ("File" menu-bar)))
|
|
|
|
(define open-item
|
|
(instantiate menu-item%
|
|
("Open..."
|
|
file-menu
|
|
(lambda (_ event)
|
|
(cond
|
|
[(finder:get-file)
|
|
=>
|
|
(lambda (filename)
|
|
(let ([p (open-input-file filename)])
|
|
(for (i = 0) (< i cols) add1
|
|
(vector-set! vec i (make-hash-table))
|
|
(for-each (lambda (elt) #;(printf "adding ~a ~a ~a~n" (first elt) i (second elt))
|
|
(ss-set-cell-processed-expr! (first elt) i (process (second elt) '@e '@r '@c '@m (first elt) i))) (read p))))
|
|
(send canvas refresh))])))
|
|
(shortcut #\O)))
|
|
|
|
(define save-item
|
|
(instantiate menu-item%
|
|
("Save as..."
|
|
file-menu
|
|
(lambda (_ event)
|
|
(cond
|
|
[(finder:put-file)
|
|
=>
|
|
(lambda (filename)
|
|
(when (file-exists? filename)
|
|
(delete-file filename))
|
|
(let ([p (open-output-file filename)])
|
|
(for (i = 0) (i . < . cols) add1
|
|
(let ([v (hash-table-map (vector-ref vec i) (lambda (row cell) (list row (ss-cell-expr cell))))])
|
|
#;(printf "~a~n" v)
|
|
(write v p)))
|
|
(flush-output p)
|
|
(close-output-port p)))])))
|
|
(shortcut #\S)))
|
|
|
|
(define edit-menu
|
|
(instantiate menu% ("Edit" menu-bar)))
|
|
|
|
(define text-field
|
|
(instantiate text-field%
|
|
("Formula:"
|
|
frame
|
|
(lambda (this control-event)
|
|
(case (send control-event get-event-type)
|
|
[(text-field-enter) (send canvas new-expression (send this get-value))])))))
|
|
|
|
(define value-field
|
|
(instantiate text-field%
|
|
("Value:" frame void)))
|
|
(send value-field enable #f)
|
|
|
|
(define rows 1000)
|
|
(define cols 100)
|
|
|
|
(define vec
|
|
(build-vector
|
|
cols
|
|
(lambda (_) (make-hash-table))))
|
|
|
|
(mz:define-struct ss-cell (expr value updater))
|
|
|
|
(define (ss-get-cell-text row col)
|
|
(cond
|
|
[(hash-table-get (vector-ref vec col) row (lambda () #f))
|
|
=> (lambda (cell)
|
|
(let ([expr (unprocess (ss-cell-expr cell) '@e '@r '@c '@m row col)])
|
|
(if (eq? expr 'undefined)
|
|
""
|
|
(expr->string expr))))]
|
|
[else ""]))
|
|
|
|
(define (ss-get-cell-value row col)
|
|
(cond
|
|
[(hash-table-get (vector-ref vec col) row (lambda () #f))
|
|
=> ss-cell-value]
|
|
[else undefined]))
|
|
|
|
(define (fresh-ss-cell row col)
|
|
(let* ([value (new-cell)]
|
|
[ret (make-ss-cell
|
|
'undefined value
|
|
(proc->signal
|
|
(lambda () (send canvas draw-cell row col))
|
|
value))])
|
|
(hash-table-put! (vector-ref vec col) row ret)
|
|
ret))
|
|
|
|
(define (ss-get-cell-value/force row col)
|
|
(ss-cell-value (hash-table-get (vector-ref vec col) row (lambda () (fresh-ss-cell row col)))))
|
|
|
|
(define (text->processed-expr txt row col)
|
|
(let* ([expr
|
|
(with-handlers
|
|
([exn? (lambda (exn)
|
|
(message-box
|
|
"Error"
|
|
(format "The expression you entered is invalid:~n~a"
|
|
(exn-message exn))
|
|
frame
|
|
'(ok stop))
|
|
eof)])
|
|
(read (open-input-string txt)))])
|
|
(if (eof-object? expr)
|
|
'undefined
|
|
(process expr '@e '@r '@c '@m row col))))
|
|
|
|
;; should not (and does not) remove when cells are emptied
|
|
;; should not reset when expression is the same
|
|
(define (ss-set-cell-processed-expr! row col processed-expr)
|
|
(let* ([cell
|
|
(hash-table-get
|
|
(vector-ref vec col) row
|
|
(lambda ()
|
|
(fresh-ss-cell row col)))])
|
|
(when (not (equal? (ss-cell-expr cell) processed-expr))
|
|
(set-ss-cell-expr! cell processed-expr)
|
|
(set-cell! (ss-cell-value cell)
|
|
(with-handlers
|
|
([exn? (lambda (exn)
|
|
#;(message-box
|
|
"Error"
|
|
(format "The following error occurred while evaluating a formula:~n~a"
|
|
(exn-message exn))
|
|
frame
|
|
'(ok stop))
|
|
exn)])
|
|
(eval `(let ([row ,row]
|
|
[col ,col])
|
|
,processed-expr))))
|
|
;(synchronize)
|
|
(send canvas draw-cell row col))
|
|
(send canvas focus)))
|
|
|
|
(define chars-per-cell 14)
|
|
|
|
(define (take-upto n lst)
|
|
(if (and (positive? n)
|
|
(cons? lst))
|
|
(cons (first lst) (take-upto (sub1 n) (rest lst)))
|
|
empty))
|
|
|
|
(define (history-e n b)
|
|
(collect-e (changes b) (list (value-now b)) (lambda (ev acc) (take-upto n (cons ev acc)))))
|
|
|
|
(define (clip lo x hi)
|
|
(max lo (min x hi)))
|
|
|
|
(define (between x y z)
|
|
(or (<= x y z)
|
|
(<= z y x)))
|
|
|
|
(define ss-canvas%
|
|
(class canvas%
|
|
(super-instantiate ())
|
|
|
|
(inherit
|
|
refresh
|
|
get-dc
|
|
get-scroll-pos
|
|
get-client-size
|
|
set-scroll-range
|
|
set-scroll-page
|
|
init-manual-scrollbars)
|
|
|
|
(override
|
|
set-scroll-pos
|
|
on-event
|
|
on-paint
|
|
on-scroll
|
|
on-size
|
|
on-char)
|
|
|
|
(field
|
|
[can-refresh? #t]
|
|
[offscreen-dc (new bitmap-dc% (bitmap (make-object bitmap% 1280 1024 #f)))]
|
|
|
|
[char-width (inexact->exact (send offscreen-dc get-char-width))]
|
|
[cell-width (* chars-per-cell char-width)]
|
|
[cell-height (+ 2 (inexact->exact (send offscreen-dc get-char-height)))]
|
|
|
|
[left-margin (* 5 char-width)]
|
|
[top-margin cell-height]
|
|
|
|
[canvas-width-rcvr (event-receiver)]
|
|
[canvas-height-rcvr (event-receiver)]
|
|
[h-scroll-rcvr (event-receiver)]
|
|
[v-scroll-rcvr (event-receiver)]
|
|
[mouse-x-rcvr (event-receiver)]
|
|
[mouse-y-rcvr (event-receiver)]
|
|
[left-clicks (event-receiver)]
|
|
[left-releases (event-receiver)]
|
|
[key-events (event-receiver)]
|
|
|
|
[canvas-width~ (hold canvas-width-rcvr)]
|
|
[canvas-height~ (hold canvas-height-rcvr)]
|
|
|
|
[mouse-x~ (hold mouse-x-rcvr 0)]
|
|
[mouse-y~ (hold mouse-y-rcvr 0)]
|
|
|
|
[left-button-down~ (hold (merge-e (left-clicks . -=> . #t)
|
|
(left-releases . -=> . #f))
|
|
#f)]
|
|
|
|
[h-chars-per-page~ (quotient (- canvas-width~ left-margin) char-width)]
|
|
[v-cells-per-page~ (quotient (- canvas-height~ top-margin) cell-height)]
|
|
[h-scroll-range~ (max 0 (- (* cols chars-per-cell) h-chars-per-page~))]
|
|
[v-scroll-range~ (max 0 (- rows v-cells-per-page~))]
|
|
|
|
[h-scroll-pos~ (hold h-scroll-rcvr 0)]
|
|
[v-scroll-pos~ (hold v-scroll-rcvr 0)]
|
|
[h-scroll-cells~ (quotient h-scroll-pos~ chars-per-cell)]
|
|
[h-scroll-offset~ (* char-width (remainder h-scroll-pos~ chars-per-cell))]
|
|
[v-scroll-cells~ v-scroll-pos~]
|
|
|
|
[mouse-row~ (y->row mouse-y~)]
|
|
[mouse-col~ (x->col mouse-x~)]
|
|
|
|
[first-vis-row~ (y->row (add1 top-margin))]
|
|
[last-vis-row~ (y->row (sub1 canvas-height~))]
|
|
[first-vis-col~ (x->col (add1 left-margin))]
|
|
[last-vis-col~ (x->col (sub1 canvas-width~))]
|
|
|
|
[start-sel-row~
|
|
(accum-b
|
|
(merge-e
|
|
(left-clicks . -=> . (lambda (_) (value-now mouse-row~)))
|
|
(key-events . ==> . (lambda (key)
|
|
(lambda (prev)
|
|
(case (send key get-key-code)
|
|
[(up) (max 0 (sub1 prev))]
|
|
[(down) (min (sub1 rows) (add1 prev))]
|
|
[else prev])))))
|
|
0)]
|
|
[start-sel-col~
|
|
(accum-b
|
|
(merge-e
|
|
(left-clicks . -=> . (lambda (_) (value-now mouse-col~)))
|
|
(key-events . ==> . (lambda (key)
|
|
(lambda (prev)
|
|
(case (send key get-key-code)
|
|
[(left) (max 0 (sub1 prev))]
|
|
[(right) (min (sub1 cols) (add1 prev))]
|
|
[else prev])))))
|
|
0)]
|
|
|
|
[cur-sel-row~
|
|
(hold (merge-e
|
|
(changes start-sel-row~)
|
|
((changes start-sel-col~) . -=> . (value-now start-sel-row~))
|
|
((changes mouse-row~) . =#> . (lambda (_)
|
|
left-button-down~))) 0)]
|
|
[cur-sel-col~
|
|
(hold (merge-e
|
|
(changes start-sel-col~)
|
|
((changes start-sel-row~) . -=> . (value-now start-sel-col~))
|
|
((changes mouse-col~) . =#> . (lambda (_)
|
|
left-button-down~))) 0)]
|
|
|
|
[scrollbar-updater
|
|
(list
|
|
(lift-strict (lambda (pg) (set-scroll-page 'horizontal (clip 1 (- pg chars-per-cell -1) 10000))) h-chars-per-page~)
|
|
(lift-strict (lambda (pg) (set-scroll-page 'vertical (clip 1 (sub1 pg) 10000))) v-cells-per-page~)
|
|
(lift-strict (lambda (rng) (set-scroll-range 'horizontal (clip 1 rng 10000))) h-scroll-range~)
|
|
(lift-strict (lambda (rng) (set-scroll-range 'vertical (clip 1 rng 10000))) v-scroll-range~))]
|
|
|
|
[scroller ((merge-e (changes h-scroll-pos~)
|
|
(changes v-scroll-pos~)) . -=> . (refresh))]
|
|
|
|
[v-auto-scroller (merge-e
|
|
((while-e (and left-button-down~
|
|
(>= cur-sel-row~ last-vis-row~)
|
|
(< cur-sel-row~ (sub1 rows))
|
|
(not (= cur-sel-row~ start-sel-row~))) 50)
|
|
. -=> . (set-scroll-pos 'vertical (add1 (value-now v-scroll-pos~))))
|
|
((while-e (and left-button-down~
|
|
(<= cur-sel-row~ first-vis-row~)
|
|
(> cur-sel-row~ 0)
|
|
(not (= cur-sel-row~ start-sel-row~))) 50)
|
|
. -=> . (set-scroll-pos 'vertical (sub1 (value-now v-scroll-pos~))))
|
|
(key-events
|
|
. ==> .
|
|
(lambda (ev)
|
|
(case (send ev get-key-code)
|
|
[(prior) (set-scroll-pos 'vertical (max 0 (- (value-now v-scroll-pos~) (value-now v-cells-per-page~))))]
|
|
[(next) (set-scroll-pos 'vertical (min (value-now v-scroll-range~)
|
|
(+ (value-now v-scroll-pos~) (value-now v-cells-per-page~))))]))))]
|
|
|
|
[h-auto-scroller (merge-e
|
|
((while-e (and left-button-down~
|
|
(>= cur-sel-col~ last-vis-col~)
|
|
(< h-scroll-pos~ h-scroll-range~)) 50)
|
|
. -=> . (set-scroll-pos 'horizontal (+ 3 (value-now h-scroll-pos~))))
|
|
((while-e (and left-button-down~
|
|
(<= cur-sel-col~ first-vis-col~)
|
|
(> h-scroll-pos~ 0)) 50)
|
|
. -=> . (set-scroll-pos 'horizontal (+ -3 (value-now h-scroll-pos~)))))]
|
|
|
|
[highlighter (merge-e
|
|
((history-e 2 (list mouse-row~ mouse-col~))
|
|
. ==> .
|
|
(lambda (lst)
|
|
(for-each
|
|
(lambda (p)
|
|
(draw-cell (first p) (second p)))
|
|
lst)))
|
|
((history-e 2 (list start-sel-row~ start-sel-col~ cur-sel-row~ cur-sel-col~))
|
|
. ==> .
|
|
(match-lambda
|
|
[((r01 c01 rf1 cf1) (r00 c00 rf0 cf0))
|
|
(cond
|
|
[(and (= r01 rf1) (= c01 cf1))
|
|
; fresh selection: clear old selection, redraw new cell
|
|
(draw-cell-block r00 rf0 c00 cf0)
|
|
(draw-cell r01 c01)]
|
|
[else
|
|
; extended selection, so r00 = r01 and c00 = c01
|
|
(draw-cell-block rf0 rf1 (min c00 cf0 cf1) (max c00 cf0 cf1))
|
|
(draw-cell-block (min r00 rf0 rf1) (max r00 rf0 rf1) cf0 cf1)
|
|
(draw-cell-block rf0 rf1 cf0 cf1)])])))]
|
|
|
|
[focuser ((key-events . =#> . (lambda (ev) (eq? #\return (send ev get-key-code))))
|
|
. -=> . (send text-field focus))]
|
|
|
|
[text-field-switcher (lift-strict (lambda (row col)
|
|
(unless (or (negative? row)
|
|
(negative? col))
|
|
(send text-field set-value (ss-get-cell-text row col))))
|
|
start-sel-row~ start-sel-col~)]
|
|
|
|
[light-steel-blue (make-object color% "LightSteelBlue")]
|
|
[lavender (make-object color% "Lavender")]
|
|
[white (make-object color% "White")]
|
|
[line-pen (make-object pen% (make-object color% "DimGray") 1 'solid)]
|
|
[light-gray (make-object color% "LightGray")]
|
|
[trans-pen (make-object pen%)]
|
|
[default-font (send offscreen-dc get-font)]
|
|
[label-font (make-object font% 11 'roman 'normal 'bold)]
|
|
[gray-brush (make-object brush% light-gray 'solid)]
|
|
[highlight-brush (make-object brush% lavender 'solid)]
|
|
[selected-brush (make-object brush% light-steel-blue 'solid)]
|
|
[clear-brush (make-object brush% white 'solid)])
|
|
|
|
(send trans-pen set-style 'transparent)
|
|
|
|
(define (set-scroll-pos which pos)
|
|
(super set-scroll-pos which pos)
|
|
(send-event
|
|
(case which
|
|
[(horizontal) h-scroll-rcvr]
|
|
[(vertical) v-scroll-rcvr]) pos))
|
|
|
|
(define/private (x->col x)
|
|
(if (> x left-margin)
|
|
(+ h-scroll-cells~ (quotient (+ (- x left-margin) h-scroll-offset~) cell-width))
|
|
-1))
|
|
|
|
(define/private (y->row y)
|
|
(if (> y top-margin)
|
|
(+ v-scroll-cells~ (quotient (- y top-margin) cell-height))
|
|
-1))
|
|
|
|
(define/private (row->y-top row)
|
|
(snapshot/sync (v-scroll-cells~)
|
|
(+ (* cell-height (- row v-scroll-cells~))
|
|
top-margin)))
|
|
|
|
(define/private (col->x-left col)
|
|
(snapshot/sync (h-scroll-cells~ h-scroll-offset~)
|
|
(+ (* (- col h-scroll-cells~) cell-width)
|
|
(- h-scroll-offset~)
|
|
left-margin)))
|
|
|
|
#;(define foo (lift #t printf "~a ~a ~a ~a~n" cur-sel-row~ cur-sel-col~ start-sel-row~ start-sel-col~))
|
|
|
|
(define/public (draw-cell-block r0 rf c0 cf)
|
|
(let ([r0 (min r0 rf)]
|
|
[rf (max r0 rf)]
|
|
[c0 (min c0 cf)]
|
|
[cf (max c0 cf)])
|
|
(for (i = r0) (i . <= . rf) add1
|
|
(for (j = c0) (j . <= . cf) add1
|
|
(draw-cell-offscreen i j)))
|
|
(let ([x0 (col->x-left c0)]
|
|
[y0 (row->y-top r0)]
|
|
[xf (col->x-left (add1 cf))]
|
|
[yf (row->y-top (add1 rf))])
|
|
(send (get-dc)
|
|
draw-bitmap-section (send offscreen-dc get-bitmap)
|
|
x0 y0 x0 y0 (- xf x0) (- yf y0)))))
|
|
|
|
(define/public (draw-cell-block-offscreen r0 rf c0 cf)
|
|
(let ([r0 (min r0 rf)]
|
|
[rf (max r0 rf)]
|
|
[c0 (min c0 cf)]
|
|
[cf (max c0 cf)])
|
|
(for (i = r0) (i . <= . rf) add1
|
|
(for (j = c0) (j . <= . cf) add1
|
|
(draw-cell-offscreen i j)))))
|
|
|
|
(define/public (new-expression text)
|
|
(snapshot/sync (cur-sel-row~ cur-sel-col~ start-sel-row~ start-sel-col~)
|
|
(let ([r0 (min cur-sel-row~ start-sel-row~)]
|
|
[r1 (max cur-sel-row~ start-sel-row~)]
|
|
[c0 (min cur-sel-col~ start-sel-col~)]
|
|
[c1 (max cur-sel-col~ start-sel-col~)]
|
|
[processed-expr (text->processed-expr text start-sel-row~ start-sel-col~)])
|
|
(for (row = r0) (row . <= . r1) add1
|
|
(for (col = c0) (col . <= . c1) add1
|
|
(ss-set-cell-processed-expr! row col processed-expr))))
|
|
(send canvas focus)))
|
|
|
|
(define (draw-cell-offscreen row col)
|
|
(snapshot/sync (first-vis-row~
|
|
last-vis-row~
|
|
first-vis-col~ last-vis-col~
|
|
mouse-row~ mouse-col~
|
|
start-sel-row~ start-sel-col~
|
|
cur-sel-row~ cur-sel-col~)
|
|
(let ([x (col->x-left col)]
|
|
[y (row->y-top row)])
|
|
(when (and (< -1 row rows)
|
|
(< -1 col cols))
|
|
(let ([text (ss-format (ss-get-cell-value row col))])
|
|
(when (and (= row start-sel-row~)
|
|
(= col start-sel-col~))
|
|
(send value-field set-value text))
|
|
(when (and (<= first-vis-row~ row last-vis-row~)
|
|
(<= first-vis-col~ col last-vis-col~))
|
|
(send offscreen-dc set-clipping-rect
|
|
(max x (+ left-margin 1)) y cell-width cell-height)
|
|
(send offscreen-dc set-brush
|
|
(cond
|
|
[(and (between start-sel-row~ row cur-sel-row~)
|
|
(between start-sel-col~ col cur-sel-col~)) selected-brush]
|
|
[(and (= row mouse-row~)
|
|
(= col mouse-col~)) highlight-brush]
|
|
[else clear-brush]))
|
|
(send offscreen-dc draw-rectangle x y (+ cell-width 1) (+ cell-height 1))
|
|
(send offscreen-dc draw-text text
|
|
(- (+ x cell-width) 2
|
|
(let-values ([(width height descent space)
|
|
(send offscreen-dc get-text-extent text #f #f 0)])
|
|
width))
|
|
(+ y 1) #f 0 0)
|
|
(send offscreen-dc set-clipping-region #f)))))))
|
|
|
|
(define/public (draw-cell row col)
|
|
(draw-cell-offscreen row col)
|
|
(let ([x (col->x-left col)]
|
|
[y (row->y-top row)])
|
|
(send (get-dc)
|
|
draw-bitmap-section (send offscreen-dc get-bitmap)
|
|
x y x y cell-width cell-height)))
|
|
|
|
(define (get-text-width dc text)
|
|
(let-values ([(width height descent space)
|
|
(send dc get-text-extent text #f #f 0)])
|
|
width))
|
|
|
|
(define (num->char n)
|
|
(integer->char (+ n (char->integer #\A))))
|
|
|
|
(define (column->string col)
|
|
(list->string
|
|
(if (< col 26)
|
|
(list (num->char col))
|
|
(list (num->char (sub1 (quotient col 26)))
|
|
(num->char (remainder col 26))))))
|
|
|
|
(define (on-char event)
|
|
(send-event key-events event)
|
|
(synchronize))
|
|
|
|
(define (on-scroll scroll-event)
|
|
(case (send scroll-event get-direction)
|
|
[(vertical) (send-event v-scroll-rcvr (send scroll-event get-position))]
|
|
[(horizontal) (send-event h-scroll-rcvr (send scroll-event get-position))])
|
|
(synchronize))
|
|
|
|
(define (on-event event)
|
|
(case (send event get-event-type)
|
|
[(enter motion)
|
|
(send-event mouse-x-rcvr (send event get-x))
|
|
(send-event mouse-y-rcvr (send event get-y))]
|
|
[(leave)
|
|
(send-event mouse-x-rcvr -1)
|
|
(send-event mouse-y-rcvr -1)]
|
|
[(left-down) (send-event left-clicks #t)]
|
|
[(left-up) (send-event left-releases #t)])
|
|
(synchronize))
|
|
|
|
(define (on-size width height)
|
|
(let-values ([(width height) (get-client-size)])
|
|
(send-event canvas-width-rcvr width)
|
|
(send-event canvas-height-rcvr height)
|
|
(synchronize)))
|
|
|
|
(define (on-paint)
|
|
(snapshot/sync (canvas-width~
|
|
canvas-height~
|
|
first-vis-row~ last-vis-row~
|
|
first-vis-col~ last-vis-col~
|
|
h-scroll-cells~ h-scroll-offset~ v-scroll-cells~)
|
|
(let ([dc offscreen-dc])
|
|
(send dc set-clipping-region #f)
|
|
(send dc clear)
|
|
;(send dc set-pen line-pen)
|
|
;(send dc set-brush highlight-brush)
|
|
(send dc set-pen trans-pen)
|
|
(send dc set-brush gray-brush)
|
|
(send dc draw-rectangle 0 0 left-margin canvas-height~)
|
|
(send dc draw-rectangle 0 0 canvas-width~ top-margin)
|
|
(send dc set-pen line-pen)
|
|
(send dc draw-line 0 0 0 canvas-height~)
|
|
(send dc draw-line 0 0 canvas-width~ 0)
|
|
(send dc set-brush clear-brush)
|
|
(send dc set-font label-font)
|
|
;; draw horizontal rules and row labels
|
|
(for (row = first-vis-row~) (row . <= . (min last-vis-row~ (sub1 rows))) add1
|
|
(let ([y (row->y-top row)]
|
|
[text (number->string row)])
|
|
(send dc draw-line 0 y canvas-width~ y)
|
|
(send dc draw-text text (- left-margin (get-text-width dc text) 2) (add1 y) #f 0 0)))
|
|
;; draw vertical rules and column labels
|
|
(send dc draw-line left-margin 0 left-margin canvas-height~)
|
|
(send dc set-clipping-rect (+ left-margin 1) 0 (- canvas-width~ left-margin 1) canvas-height~)
|
|
(for (col = first-vis-col~) (col . <= . (min last-vis-col~ (sub1 cols))) add1
|
|
(let ([x (col->x-left col)]
|
|
[text (column->string col)])
|
|
(send dc draw-text text (+ x (quotient (- cell-width (get-text-width dc text)) 2)) 0 #f 0 0)
|
|
(send dc draw-line x 0 x canvas-height~)))
|
|
(send dc set-font default-font)
|
|
(draw-cell-block-offscreen first-vis-row~ last-vis-row~ first-vis-col~ last-vis-col~)
|
|
(send (get-dc) draw-bitmap-section (send dc get-bitmap) 0 0 0 0 canvas-width~ canvas-height~))))
|
|
|
|
(let-values ([(width height) (get-client-size)])
|
|
(send-event canvas-width-rcvr width)
|
|
(send-event canvas-height-rcvr height))
|
|
(synchronize)
|
|
(init-manual-scrollbars 1 1 1 1 0 0)
|
|
(send offscreen-dc set-pen line-pen)
|
|
(send offscreen-dc set-brush highlight-brush)))
|
|
|
|
(define canvas
|
|
(instantiate ss-canvas% (frame) (style (list 'hscroll 'vscroll))))
|
|
|
|
(send frame show #t)
|
|
(send canvas focus)) |