gui/collects/mred/mred.ss
Matthew Flatt c1dade6f7e .
original commit: a180d01faba0e7b7b1bf260f9a8e9901b2806c9c
2003-10-06 16:31:27 +00:00

7832 lines
264 KiB
Scheme

(module mred mzscheme
(require (prefix wx: (lib "kernel.ss" "mred" "private")))
(require (lib "class.ss")
(lib "class100.ss")
(lib "file.ss")
(lib "etc.ss")
(lib "list.ss")
(lib "process.ss")
(lib "moddep.ss" "syntax")
"private/seqcontract.ss")
;;;;;;;;;;;;;;; Constants ;;;;;;;;;;;;;;;;;;;;
; default spacing between items.
(define const-default-spacing 0)
; default margins:
(define const-default-x-margin 2)
(define const-default-y-margin 2)
; default spacing around edge of panel
(define const-default-border 0)
; the maximum hard-min-width of a gauge
(define const-max-gauge-length 150)
; maximum reasonable minimum width/height
(define max-min 10000)
(define o (current-output-port))
(define no-val (gensym)) ; indicates init arg not supplied
;;;;;;;;;;;;;;; Thread Safety ;;;;;;;;;;;;;;;;;;;;
;; When the user creates an object or calls a method, or when the
;; system invokes a callback, many steps may be required to initialize
;; or reset fields to maintain invariants. To ensure that other
;; threads do not call methods during a time when invariants do not
;; hold, we force all of the following code to be executed in a single
;; threaded manner, and we temporarily disable breaks. This accompiled
;; with a single monitor: all entry points into the code use
;; `entry-point' or `as-entry', and all points with this code that
;; call back out to user code uses `as-exit'.
;; If an exception is raised within an `enter'ed area, control is
;; moved back outside by the exception handler, and then the exception
;; is re-raised. The user can't tell that the exception was caught an
;; re-raised. But without the catch-and-reraise, the user's exception
;; handler might try to use GUI elements from a different thread,
;; leading to deadlock.
(define monitor-sema (make-semaphore 1))
(define monitor-owner #f)
;; An exception may be constructed while we're entered:
(define entered-err-string-handler
(lambda (s n)
(as-exit
(lambda ()
((error-value->string-handler) s n)))))
(define old-handler #f)
(define old-err-string-handler #f)
(define old-break #f)
(define (enter-paramz)
(set! old-handler (current-exception-handler))
(set! old-err-string-handler (error-value->string-handler))
(set! old-break (break-enabled))
(break-enabled #f)
(error-value->string-handler entered-err-string-handler))
(define (exit-paramz)
(current-exception-handler old-handler)
(error-value->string-handler old-err-string-handler)
(break-enabled old-break))
(define (as-entry f)
(cond
[(eq? monitor-owner (current-thread))
(f)]
[else
((let/ec k
(dynamic-wind
(lambda ()
(wx:in-atomic-region monitor-sema)
(set! monitor-owner (current-thread))
(enter-paramz)
(current-exception-handler
(lambda (exn)
(k (lambda () (raise exn))))))
(lambda ()
(call-with-values
f
(lambda args (lambda () (apply values args)))))
(lambda ()
(set! monitor-owner #f)
(exit-paramz)
(semaphore-post monitor-sema)
(wx:in-atomic-region #f)))))]))
; entry-point macros in macros.ss
(define (as-exit f)
; (unless (eq? monitor-owner (current-thread)) (error 'monitor-exit "not in monitored area"))
(let ([eh #f])
(dynamic-wind
(lambda ()
(set! eh (current-exception-handler))
(set! monitor-owner #f)
(exit-paramz)
(semaphore-post monitor-sema)
(wx:in-atomic-region #f))
f
(lambda ()
(wx:in-atomic-region monitor-sema)
(set! monitor-owner (current-thread))
(enter-paramz)
(current-exception-handler eh)))))
(define-syntax entry-point
(lambda (stx)
(syntax-case stx (lambda case-lambda)
[(_ (lambda args body1 body ...))
(syntax (lambda args (as-entry (lambda () body1 body ...))))]
[(_ (case-lambda [vars body1 body ...] ...))
(syntax (case-lambda
[vars (as-entry (lambda () body1 body ...))]
...))])))
(define-syntax mk-param
(lambda (stx)
(syntax-case stx ()
[(_ val filter check force-redraw)
(syntax
(case-lambda
[() val]
[(v) (check v)
(let ([v2 (filter v)])
(unless (eq? v2 val)
(set! val v2)
(force-redraw)))]))])))
;;;;;;;;;;;;;;; Helpers ;;;;;;;;;;;;;;;;;;;;
; this structure holds the information that a child will need to send
; to its parent when the parent must resize itself.
(define-struct child-info (x-min y-min ; includes margins!
x-margin y-margin ; requested margin space
x-stretch y-stretch)) ; booleans indicating strechability
; get-two-int-values: a wrapper around functions that need to return
; two results.
; input: function: a function which takes two boxes and returns results
; in them.
; returns: the contents of the two boxes (as multiple values)
(define get-two-int-values
(lambda (function)
(let ([a (box 0)]
[b (box 0)])
(function a b)
(values (unbox a) (unbox b)))))
(define non-negative-number?
(lambda (n)
(and (real? n) (not (negative? n)))))
(define same-dimension?
(lambda (new-dim current-dim)
(or (= new-dim current-dim)
(= new-dim -1))))
;; list-diff: computes the difference between two lists
;; input: l1, l2: two lists
;; returns: a list of all elements in l1 which are not in l2.
(define list-diff
(lambda (l1 l2)
(let ([table (make-hash-table)])
(for-each
(lambda (item)
(hash-table-put! table item #t))
l2)
(let loop ([l l1])
(cond
[(null? l) null]
[(hash-table-get table (car l) (lambda () #f))
(loop (cdr l))]
[else (cons (car l) (loop (cdr l)))])))))
(define ibeam (make-object wx:cursor% 'ibeam))
(define arrow-cursor (make-object wx:cursor% 'arrow))
(define top-x 1)
(define top-y 1)
(define top-level-windows (make-hash-table 'weak))
(define (key-regexp c)
(regexp (format "(^|[^&])&[~a~a]" (char-downcase c) (char-upcase c))))
(define (do-command c e)
(as-exit (lambda () (send c command e))))
;;;;;;;;;;;;;;; Focus-tabbing helpers ;;;;;;;;;;;;;;;;;;;;
(define (traverse x y w h dir dests)
;; x, y : real = starting positions
;; dir : one of 'left, 'right, 'up, 'next, 'prev = desried move
;; dests : list of (cons key x y w h) = destinations
;; returns key or #f
(case dir
[(next prev)
(letrec ([get-x cadr]
[get-w cadddr]
[get-y caddr]
[get-h (lambda (x) (caddr (cddr x)))]
[backward? (eq? dir 'prev)]
[fail-start (if backward?
1000000000
0)]
[find-stripe (lambda (t stripes)
(let loop ([s stripes])
(cond
[(null? s) #f]
[(and (<= (caar s) t) (< t (cdar s)))
(car s)]
[else (loop (cdr s))])))]
[mk-stripes
(lambda (get-y get-h stripes dests)
(let loop ([l (append (map (lambda (x) (cons (car x) (- (cdr x) (car x))))
stripes)
(map (lambda (x)
(cons (get-y x) (get-h x)))
dests))])
(if (null? l)
null
;; Find longest top-most
(let* ([top (let loop ([l (cdr l)][best (car l)])
(cond
[(null? l) best]
[(or (< (caar l) (car best)) ; topmost
(and (= (caar l) (car best)) ; at least as top
(> (cdar l) (cdr best)))) ; longer
(loop (cdr l) (car l))]
[else (loop (cdr l) best)]))]
[t (car top)]
[b (+ t (cdr top))])
;; Stripe is anything that starts before the end of `top'
(let ([remaining (let loop ([l l])
(cond
[(null? l) null]
[(find-stripe (caar l) (list (cons t b)))
(loop (cdr l))]
[else (cons (car l) (loop (cdr l)))]))])
(cons (cons t b) (loop remaining)))))))]
[in-stripe (lambda (stripe dests get-y get-h)
(let loop ([l dests])
(cond
[(null? l) null]
[(find-stripe (get-y (car l)) (list stripe))
(cons (car l) (loop (cdr l)))]
[else (loop (cdr l))])))]
[next-stripe (lambda (stripe stripes)
(let loop ([s stripes][best #f])
(cond
[(null? s) best]
[(and (or (not stripe)
(if backward?
(<= (cdar s) (car stripe))
(>= (caar s) (cdr stripe))))
(or (not best)
(if backward?
(> (cdar s) (cdr best))
(< (caar s) (cdr best)))))
(loop (cdr s) (car s))]
[else (loop (cdr s) best)])))]
[find (lambda (get-x get-w get-y get-h use-x? x w use-y? y h dests fail)
;; find's variable names correspond to an h-stripe view, but everything is
;; flipped to v-stripes if the args are flipped
(let ([h-stripes (mk-stripes get-y get-h
(if use-y? (list (cons y (+ y h))) null)
dests)])
;; find the initial h-stripe
(let sel-h-stripe-loop ([init-h-stripe (if use-y?
(find-stripe y h-stripes)
(next-stripe #f h-stripes))]
[x x][w w][use-x? use-x?])
;; find items in the initial stripe
(let ([in-init-h-stripe (in-stripe init-h-stripe dests get-y get-h)]
[next (lambda ()
(let ([s (next-stripe init-h-stripe h-stripes)])
(if s
(sel-h-stripe-loop s fail-start fail-start #f)
(fail))))])
(if (null? in-init-h-stripe)
;; no items in this stripe; try the next one
(next)
;; Non-empty h-stripe; now look for items in the same or later v-stripe
(if (null? (cdr in-init-h-stripe))
;; one item in the stripe; take it unless we're using x and it's
;; before x:
(if (or (not use-x?)
((if backward? < >) (get-x (car in-init-h-stripe)) x))
(car in-init-h-stripe)
;; Only item is no good; try the next stripe
(next))
;; Recur to work with v-stripes
(find get-y get-h get-x get-w use-y? y h use-x? x w in-init-h-stripe next)))))))])
(if (null? dests)
#f
(car (find get-x get-w get-y get-h #t x w #t y h dests
(lambda ()
(find get-x get-w get-y get-h
#f fail-start fail-start
#f fail-start fail-start
dests void))))))]
[else
(let ([v (let loop ([d dests])
(if (null? d)
#f
(let* ([best (loop (cdr d))]
[this (car d)]
[diff (lambda (v l x w)
(cond
[(< (+ v l) x) (- x (+ v l))]
[(< (+ x w) v) (- (+ x w) v)]
[else 0]))])
(let* ([get-x cadr]
[get-w cadddr]
[get-y caddr]
[get-h (lambda (x) (caddr (cddr x)))]
[tdx (diff x w (get-x this) (get-w this))]
[tdy (diff y h (get-y this) (get-h this))]
[bdx (and best (diff x w (get-x best) (get-w best)))]
[bdy (and best (diff y h (get-y best) (get-h best)))]
[better (lambda (tdx tdy bdy negative?)
(if (and (zero? tdx) (negative? tdy)
(or (not best)
(< (abs tdy) (abs bdy))))
this
best))])
(case dir
[(up) (better tdx tdy bdy negative?)]
[(down) (better tdx tdy bdy positive?)]
[(left) (better tdy tdx bdx negative?)]
[(right) (better tdy tdx bdx positive?)])))))])
(and v (car v)))]))
(define (object->position o)
(let-values ([(x y) (double-boxed 0 0 (lambda (x y) (send o client-to-screen x y)))]
[(w h) (double-boxed 0 0 (lambda (x y) (send o get-client-size x y)))])
(list o x y w h)))
(define (container->children f except must-focus?)
(apply
append
(map
(lambda (i)
(cond
[(is-a? i wx-basic-panel<%>)
(if (or (is-a? i wx:windowless-panel%)
(send i is-shown?))
(container->children i except must-focus?)
null)]
[(or (eq? i except)
(and must-focus? (not (send i gets-focus?)))
(not (send i is-enabled?))
(not (send i is-shown?)))
null]
[else (list i)]))
(send f get-children))))
(define (filter-overlapping l)
(if (null? l)
null
(let* ([rest (filter-overlapping (cdr l))]
[first (car l)]
[f (cdr first)]
[x (car f)]
[y (cadr f)]
[x2 (+ x (caddr f))]
[y2 (+ y (cadddr f))])
(if (ormap (lambda (other)
(let* ([p (cdr other)]
[px (car p)]
[py (cadr p)]
[px2 (+ px (caddr p))]
[py2 (+ py (cadddr p))])
(and (or (<= x px x2) (<= x px2 x2)
(<= px x px2) (<= px x2 px2))
(or (<= y py y2) (<= y py2 y2)
(<= py y py2) (<= py y2 py2)))))
rest)
rest
(cons first rest)))))
;;;;;;;;;;;;;;; wx- Class Construction ;;;;;;;;;;;;;;;;;;;;
;; ------------- Mixins for common functionality --------------
(define wx-make-window%
(lambda (% top?)
(class100 % args
(rename [super-on-set-focus on-set-focus]
[super-on-kill-focus on-kill-focus]
[super-drag-accept-files drag-accept-files]
[super-show show]
[super-enable enable])
(private-field
[top-level #f]
[focus? #f]
[container this]
[visible? #f]
[active? #f])
(private
[currently?
(lambda (m)
(let loop ([p this])
(and (or (is-a? p wx:windowless-panel%)
(m p))
(or (is-a? p wx:frame%)
(is-a? p wx:dialog%)
(loop (send p get-parent))))))])
(public
[on-visible
(lambda ()
(let ([vis? (currently? (lambda (o) (send o is-shown?)))])
(unless (eq? vis? visible?)
(set! visible? vis?)
(as-exit
(lambda ()
(send (wx->proxy this) on-superwindow-show vis?))))))]
[queue-visible
(lambda ()
(parameterize ([wx:current-eventspace (send (get-top-level) get-eventspace)])
(wx:queue-callback (entry-point (lambda () (on-visible))) wx:middle-queue-key)))])
(public
[on-active
(lambda ()
(let ([act? (currently? (lambda (o) (send o is-enabled?)))])
(unless (eq? act? active?)
(set! active? act?)
(as-exit
(lambda ()
(send (wx->proxy this) on-superwindow-enable act?))))))]
[queue-active
(lambda ()
(parameterize ([wx:current-eventspace (send (get-top-level) get-eventspace)])
(wx:queue-callback (entry-point (lambda () (on-active))) wx:middle-queue-key)))]
;; Needed for radio boxes:
[orig-enable
(lambda args (super-enable . args))])
(private-field
[can-accept-drag? #f])
(public
[accept-drag? (lambda () can-accept-drag?)]
[get-container (lambda () container)]
[set-container (lambda (c) (set! container c))]
[get-window (lambda () this)]
[dx (lambda () 0)]
[dy (lambda () 0)]
[ext-dx (lambda () (dx))]
[ext-dy (lambda () (dy))]
[handles-key-code (lambda (x alpha? meta?) #f)]
[char-to (lambda () (void))]
[get-top-level
(lambda ()
(unless top-level
(let loop ([window this])
(cond
[(or (is-a? window wx:frame%)
(is-a? window wx:dialog%))
(set! top-level window)]
[else (loop (send window get-parent))])))
top-level)])
(override
[show
(lambda (on?)
(queue-visible)
(super-show on?))]
[enable
(lambda (on?)
(queue-active)
(super-enable on?))]
[drag-accept-files
(lambda (on?)
(set! can-accept-drag? (and on? #t))
(super-drag-accept-files on?))]
[on-set-focus
(entry-point
(lambda ()
(send (get-top-level) set-focus-window this)
(set! focus? #t)
(as-exit (lambda () (super-on-set-focus)))))]
[on-kill-focus
(entry-point
(lambda ()
(send (get-top-level) set-focus-window #f)
(set! focus? #f)
(as-exit (lambda () (super-on-kill-focus)))))])
(public
[has-focus? (lambda () focus?)])
(sequence
(apply super-init args)
(unless top?
(set! visible? (currently? (lambda (o) (send o is-shown?))))
(set! active? (currently? (lambda (o) (send o is-enabled?)))))))))
; make-container% - for panels and top-level windows
(define (wx-make-container% %) %)
; make-top-container%: adds the necessary functionality to wx:frame% and
; wx:dialog%.
; input: base%: the base class from which to descend the new class.
; Intended to be either wx:frame% or wx:dialog%, but can
; be anything which contains all methods in the inherit section
; below.
; returns: a new class, descended from base%, which possesses the added
; capabilities necessary to serve as the frame/dialog which
; contains container classes.
(define (make-top-container% base% dlg?)
(class100 (wx-make-container% (wx-make-window% base% #t)) (parent . args)
(inherit get-x get-y get-width get-height set-size
get-client-size is-shown? on-close)
(rename [super-show show] [super-move move] [super-center center]
[super-on-size on-size]
[super-enable enable]
[super-on-visible on-visible]
[super-on-active on-active])
(private-field
; have we had any redraw requests while the window has been
; hidden?
[pending-redraws? #t]
[perform-updates? #t]
[seq-count 0]
[ignore-redraw-request? #f]
[already-trying? #f]
[was-bad? #f] ; hack around min-frame-size limitations
[last-width -1]
[last-height -1]
; pointer to panel in the frame for use in on-size
[panel #f]
[use-default-position? (and (= -11111 (list-ref args 2))
(= -11111 (list-ref args (if dlg? 3 1))))]
[enabled? #t]
[focus #f]
[target #f])
(override
[enable
(lambda (b)
(set! enabled? (and b #t))
(super-enable b))])
(private-field
[eventspace (if parent
(send parent get-eventspace)
(wx:current-eventspace))])
(public
[get-eventspace (lambda () eventspace)]
[is-enabled?
(lambda () enabled?)]
[set-focus-window
(lambda (w)
(set! focus w)
(when w
(set! target w)))]
[get-focus-window
(lambda () focus)]
[get-edit-target-window
(lambda () (and target (send (wx->proxy target) is-shown?) target))]
[get-focus-object
(lambda ()
(window->focus-object focus))]
[get-edit-target-object
(lambda ()
(window->focus-object target))]
[window->focus-object
(lambda (w)
(and w
(if (is-a? w wx:editor-canvas%)
(let loop ([m (send w get-editor)]
[prev w])
(if m
(let ([snip (send m get-focus-snip)])
(if (and snip (is-a? snip wx:editor-snip%))
(loop (send snip get-editor) m)
m))
w))
focus)))]
; add-child: update panel pointer.
; input: new-panel: panel in frame (descendant of
; panel%)
; returns: nothing
; effects: sets panel to new-panel
; if new-panel is not a descendant of
; panel%, calls error; panel not updated.
[add-child
(lambda (new-panel)
(set! panel new-panel)
(set! pending-redraws? #t)
(let-values ([(client-w client-h)
(get-two-int-values (lambda (a b) (get-client-size a b)))])
(send panel set-size 0 0 client-w client-h))
(self-redraw-request))]
[area-parent (lambda () #f)]
[get-top-panel
(lambda ()
panel)]
[delay-updates
(case-lambda
[() (not perform-updates?)]
[(f) (set! perform-updates? (not f))
(when pending-redraws?
(force-redraw))])]
[begin-container-sequence
(lambda ()
(when (zero? seq-count)
(delay-updates #t))
(set! seq-count (add1 seq-count)))]
[end-container-sequence
(lambda ()
(set! seq-count (sub1 seq-count))
(when (zero? seq-count)
(delay-updates #f)))]
; force-redraw: receives a message from to redraw the
; entire frame.
; input: none
; returns: nothing
; effects: redraws the frame at its current size (changing size
; as necessary).
[child-redraw-request
; since there's only one panel, we assume that `from' is the
; panel and the request should be granted
(lambda (from)
(unless ignore-redraw-request?
(self-redraw-request)))]
[self-redraw-request
(lambda ()
(if (and (is-shown?) perform-updates?)
(force-redraw)
(set! pending-redraws? #t)))]
[force-redraw
(lambda ()
(if panel
(dynamic-wind
(lambda () (set! ignore-redraw-request? #t))
(lambda () (resized))
(lambda () (set! ignore-redraw-request? #f)))
(set! pending-redraws? #f)))]
[correct-size
(lambda (frame-w frame-h)
(if (not panel)
(values frame-w frame-h)
(let-values ([(f-client-w f-client-h) (get-two-int-values
(lambda (a b) (get-client-size a b)))])
(let* ([panel-info (send panel get-info)]
; difference between panel's full size &
; frame's full size
[delta-w (max 0 (- (get-width) f-client-w))]
[delta-h (max 0 (- (get-height) f-client-h))]
; minimum frame size:
[min-w (+ delta-w (child-info-x-min panel-info))]
[min-h (+ delta-h (child-info-y-min panel-info))]
; correct size for frame
[new-w
(cond
[(< frame-w min-w) min-w]
[(and (> frame-w min-w) (not (child-info-x-stretch panel-info))) min-w]
[else frame-w])]
[new-h
(cond
[(< frame-h min-h) min-h]
[(and (> frame-h min-h) (not (child-info-y-stretch panel-info))) min-h]
[else frame-h])])
(values new-w new-h)))))]
[set-panel-size
(lambda ()
(when panel
(let-values ([(f-client-w f-client-h) (get-two-int-values
(lambda (a b) (get-client-size a b)))]
[(panel-info) (send panel get-info)]
[(sel) (lambda (nsize psize stretch?)
(if stretch?
(max nsize psize)
psize))])
(send panel set-size 0 0
(sel f-client-w (child-info-x-min panel-info)
(child-info-x-stretch panel-info))
(sel f-client-h (child-info-y-min panel-info)
(child-info-y-stretch panel-info)))
(set! pending-redraws? #f)
(send panel on-container-resize))))]
[resized
(entry-point
(lambda ()
(unless already-trying?
(let ([new-width (get-width)]
[new-height (get-height)])
(let-values ([(correct-w correct-h) (correct-size new-width new-height)])
(cond
[(and (= new-width correct-w) (= new-height correct-h))
;; Good size; do panel
(set! was-bad? #f)
(set-panel-size)]
[(and (= last-width correct-w) (= last-height correct-h)
was-bad?)
;; We give up; do panel
(set-panel-size)]
[else
;; Too large/small; try to fix it, but give up after a while
(set! was-bad? #t)
(set! last-width correct-w)
(set! last-height correct-h)
(set! already-trying? #t)
(set-size -1 -1 correct-w correct-h)
(set! already-trying? #f)
(resized)]))))))])
(override
; show: add capability to set perform-updates
; input: now : boolean
; returns: nothing
; effects: if we're showing for the first time, unblock updates
; and force an update. If we're hiding, block updates.
; pass now to superclass's show.
[show
(lambda (on?)
(when (and on? pending-redraws?)
(force-redraw))
(when (and on? use-default-position?)
(set! use-default-position? #f)
(let*-values ([(w) (get-width)]
[(h) (get-height)]
[(sw sh) (get-display-size)]
[(x x-reset?) (if (< (+ top-x w) sw)
(values top-x #f)
(values (max 0 (- sw w 10)) #t))]
[(y y-reset?) (if (< (+ top-y h) sh)
(values top-y #f)
(values (max 0 (- sh h 20)) #t))])
(move x y)
(set! top-x (if x-reset? 0 (+ top-x 10)))
(set! top-y (if y-reset? 0 (+ top-y 20)))))
(if on?
(hash-table-put! top-level-windows this #t)
(hash-table-remove! top-level-windows this))
(as-exit ; as-exit because there's an implicit wx:yield for dialogs
(lambda () (super-show on?))))]
[on-visible
(lambda ()
(send panel queue-visible)
(super-on-visible))]
[on-active
(lambda ()
(send panel queue-active)
(super-on-active))]
[move (lambda (x y) (set! use-default-position? #f) (super-move x y))]
[center (lambda (dir)
(when pending-redraws? (force-redraw))
(set! use-default-position? #f)
(super-center dir))]
; on-size: ensures that size of frame matches size of content
; input: new-width/new-height: new size of frame
; returns: nothing
; effects: if new size is smaller than allowed size of
; contents, frame resized to smallest possible size.
; If frame is larger than contents and contents
; aren't stretchable, frame resized to size of
; contents. Each direction is handled
; independently.
[on-size
(lambda (bad-width bad-height)
(unless (and already-trying? (not (eq? 'unix (system-type))))
(parameterize ([wx:current-eventspace eventspace])
(wx:queue-callback (lambda () (resized)) #t))))])
(public
[handle-traverse-key
(lambda (e)
(and panel
(let ([code (send e get-key-code)])
(case code
[(#\return)
(let ([o (get-focus-window)])
(if (and o (send o handles-key-code code #f #f))
#f
(let ([objs (container->children panel #f #f)])
(or (ormap
(lambda (x)
(and (is-a? x wx:button%)
(send x has-border?)
(let ([v (make-object wx:control-event% 'button)])
(do-command x v)
#t)))
objs)
(not (is-a? o wx-editor-canvas%))))))]
[(escape #\.)
(and (is-a? this wx:dialog%)
(or (eq? code 'escape)
(and (memq (system-type) '(macos macosx))
(send e get-meta-down)))
(let ([o (get-focus-window)])
(if (and o (send o handles-key-code code #f (send e get-meta-down)))
#f
(begin
(when (on-close)
(show #f))
#t))))]
[(#\space)
(let ([o (get-focus-window)])
(cond
[(is-a? o wx:button%)
(do-command o (make-object wx:control-event% 'button))
#t]
[(is-a? o wx:check-box%)
(send o set-value (not (send o get-value)))
(do-command o (make-object wx:control-event% 'check-box))
#t]
[(is-a? o wx:radio-box%)
(let ([s (send o button-focus -1)])
(unless (negative? s)
(send o set-selection s)
(do-command o (make-object wx:control-event% 'radio-box))))
#t]
[else #f]))]
[(#\tab left up down right)
(let ([o (get-focus-window)])
(if (and o (send o handles-key-code code #f #f))
#f
(let* ([shift? (send e get-shift-down)]
[forward? (or (and (eq? code #\tab) (not shift?))
(memq code '(right down)))]
[normal-move
(lambda ()
(let* ([o (if (or (is-a? o wx:canvas%) (is-a? o wx:item%))
(if (or (is-a? o wx-tab-group%)
(is-a? o wx-group-box%))
#f
o)
#f)]
[candidates
(map object->position (container->children panel o #t))]
[dests (filter-overlapping candidates)]
[pos (if o (object->position o) (list 'x 0 0 1 1))]
[o (traverse (cadr pos) (caddr pos) (cadddr pos) (list-ref pos 4)
(case code
[(#\tab) (if shift? 'prev 'next)]
[else code])
dests)])
(when o
(if (is-a? o wx:radio-box%)
(send o button-focus (if forward? 0 (sub1 (send o number))))
(begin
(send o set-focus)
(if (and (is-a? o wx-text-editor-canvas%)
(send o is-single-line?))
(let ([e (send o get-editor)])
(as-exit
(lambda ()
(send e set-position 0 (send e last-position) #f #t 'local))))
;; Not a text field; a canvas?
(when (or (is-a? o wx-canvas%)
(is-a? o wx-editor-canvas%))
(as-exit (lambda () (send o on-tab-in))))))))))])
(if (is-a? o wx:radio-box%)
(let ([n (send o number)]
[s (send o button-focus -1)]
[v-move? (memq code '(up down))]
[h-move? (memq code '(left right))]
[v? (send o vertical?)])
(cond
[(or (negative? s)
(and v? h-move?)
(and (not v?) v-move?))
(normal-move)]
[(and forward? (< s (sub1 n)))
(send o button-focus (add1 s))]
[(and (not forward?) (positive? s))
(send o button-focus (sub1 s))]
[else (normal-move)]))
(normal-move))
#t)))]
[else (if (and (wx:shortcut-visible-in-label?)
(char? code)
(or (char-alphabetic? code)
(char-numeric? code))
(not (send e get-shift-down))
(not (send e get-control-down))
(not (send e get-alt-down)))
(let ([o (get-focus-window)]
[meta? (send e get-meta-down)])
(if (and o (send o handles-key-code code #t meta?))
#f
;; Move selection/hit control based on & shortcuts
(let* ([objs (container->children panel #f #t)]
[re (key-regexp code)])
(ormap
(lambda (o)
(let* ([win (wx->proxy o)]
[l (send win get-label)])
(cond
[(and (string? l)
(regexp-match re l))
(send o set-focus)
(send o char-to)
#t]
[(is-a? o wx:radio-box%)
(let ([n (send o number)])
(let loop ([i 0])
(if (= i n)
#f
(let ([l (send o get-string i)])
(if (and (string? l)
(regexp-match re l))
(begin
(send o button-focus i)
(send o char-to-button i)
#t)
(loop (add1 i)))))))]
[else #f])))
objs))))
#f)]))))])
(sequence
(apply super-init parent args))))
; make-item%: creates items which are suitable for placing into
; containers.
; input: item%: a wx:item% descendant (but see below) from which the
; new class will be derived.
; stretch-x/stretch-y: booleans which specify the default
; stretchability behavior for the new class.
; returns: a class, descended from wx:item%, which is suitable for
; placing in a container.
; Note: the item% parameter does not necessarily HAVE to be a
; descendant of wx:item%, so long as it contains the identifiers in the
; inherit section below. You will note below that I ran wx:panel%
; through this function to create panel%.
(define make-item%
(lambda (item% x-margin-w y-margin-h stretch-x stretch-y)
(class100 (wx-make-window% item% #f) (window-style . args)
(rename [super-on-set-focus on-set-focus]
[super-on-kill-focus on-kill-focus])
(inherit get-width get-height get-x get-y
get-parent get-client-size)
(rename [super-enable enable]
[super-set-size set-size])
(private-field [enabled? #t])
(override
[enable
(lambda (b)
(set! enabled? (and b #t))
(super-enable b))]
; set-size: caches calls to set-size to avoid unnecessary work,
; and works with windowsless panels
; input: x/y: new position for object
; width/height: new size for object
; returns: nothing
; effect: if arguments mark a different geometry than the object's
; current geometry, passes args to super-class's set-size.
; Otherwise, does nothing.
[set-size
(lambda (x y width height)
(set! x (+ x (send (area-parent) dx)))
(set! y (+ y (send (area-parent) dy)))
(unless (and (same-dimension? x (get-x))
(same-dimension? y (get-y))
(same-dimension? width (get-width))
(same-dimension? height (get-height)))
(super-set-size x y width height)))])
(public
[is-enabled?
(lambda () enabled?)])
(private-field
; Store minimum size of item.
; This will never change after the item is created.
hard-min-width
hard-min-height)
(public
[set-min-height (lambda (v) (set! hard-min-height v) (min-height v))]
[set-min-width (lambda (v) (set! hard-min-width v) (min-width v))]
[get-hard-minimum-size (lambda () (values hard-min-width hard-min-height))]
[client-inset
(lambda (h?)
(let ([h #f][w #f])
(unless h
(let ([w-box (box 0)]
[h-box (box 0)])
(get-client-size w-box h-box)
(set! h (- (get-height) (unbox h-box)))
(set! w (- (get-width) (unbox w-box)))))
(if h? h w)))]
; gets/sets user's requirement for minimum width. Errors out
; if new value is not a non-negative real number. Forces a
; redraw upon a set.
[min-client-width
(case-lambda
[() (- (min-width) (client-inset #f))]
[(new-width)
(check-range-integer '(method canvas<%> min-client-width) new-width)
(min-width (+ new-width (client-inset #f)))])]
[min-client-height
(case-lambda
[() (- (min-height) (client-inset #t))]
[(new-height)
(check-range-integer '(method canvas<%> min-client-height) new-height)
(min-height (+ new-height (client-inset #t)))])])
(private-field [-mw 0]
[-mh 0]
[-xm x-margin-w]
[-ym y-margin-h]
[-sx stretch-x]
[-sy stretch-y]
[first-arg (car args)])
(public
[min-width
(mk-param
-mw identity
(lambda (v)
(check-range-integer '(method area<%> min-width) v))
force-redraw)]
[min-height
(mk-param
-mh identity
(lambda (v)
(check-range-integer '(method area<%> min-height) v))
force-redraw)]
[x-margin
(mk-param
-xm identity
(lambda (v)
(check-margin-integer '(method subarea<%> horiz-margin) v)
v)
force-redraw)]
[y-margin
(mk-param
-ym identity
(lambda (v)
(check-margin-integer '(method subarea<%> vert-margin) v)
v)
force-redraw)]
[stretchable-in-x
(mk-param -sx (lambda (x) (and x #t)) void force-redraw)]
[stretchable-in-y
(mk-param -sy (lambda (x) (and x #t)) void force-redraw)]
; get-info: passes necessary info up to parent.
; input: none
; returns: child-info struct containing the info about this
; item.
; intended to be called by item's parent upon resize.
[get-info
(lambda ()
(let* ([min-size (get-min-size)]
[result (make-child-info (car min-size) (cadr min-size)
(x-margin) (y-margin)
(stretchable-in-x)
(stretchable-in-y))])
result))]
[area-parent (lambda () first-arg)]
; force-redraw: unconditionally trigger redraw.
; input: none
; returns: nothing
; effects: forces the item's parent (if it exists) to redraw
; itself. This will recompute the min-size cache if it is
; invalid.
[force-redraw
(lambda ()
(let ([parent (area-parent)])
(when parent
(send parent child-redraw-request this))))]
[on-container-resize (lambda () (void))] ; This object doesn't contain anything
[init-min (lambda (x) x)]
; get-min-size: computes the minimum size the item can
; reasonably assume.
; input: none
; returns: a list containing the minimum width & height.
[get-min-size
(lambda ()
(let ([w (+ (* 2 (x-margin)) (max hard-min-width (min-width)))]
[h (+ (* 2 (y-margin)) (max hard-min-height (min-height)))])
(list w h)))])
(sequence
(apply super-init (send (car args) get-window) (cdr args))
(set-min-width (init-min (get-width)))
(set-min-height (init-min (get-height)))
(unless (memq 'deleted window-style)
;; For a pane[l], the creator must call the equivalent of the following,
;; delaying to let the panel's wx field get initialized before
;; panel-sizing methods are called
(unless (is-a? this wx-basic-panel<%>)
(send (area-parent) add-child this)))))))
; make-control% - for non-panel items
(define (make-control% item% x-margin y-margin
stretch-x stretch-y)
(class100 (make-item% item% x-margin y-margin stretch-x stretch-y) args
(inherit get-parent)
(sequence
(apply super-init args)
(send (get-parent) set-item-cursor 0 0))))
(define (make-simple-control% item%)
(make-control% item%
const-default-x-margin const-default-y-margin
#f #f))
;------------- Mixins for glue to mred classes -----------------
(define (queue-window-callback w cb)
(parameterize ([wx:current-eventspace (send (send w get-top-level) get-eventspace)])
(wx:queue-callback cb wx:middle-queue-key)))
(define wx<%> (interface () get-mred))
(define wx/proxy<%> (interface (wx<%>) get-proxy))
(define (make-glue% %)
(class100* % (wx/proxy<%>) (mr prxy . args)
(private-field [mred mr]
[proxy prxy])
(public
[get-mred (lambda () mred)]
[get-proxy (lambda () proxy)])
(sequence (apply super-init args))))
(define (make-window-glue% %) ; implies make-glue%
(class100 (make-glue% %) (mred proxy . args)
(inherit get-x get-y get-width get-height area-parent get-mred get-proxy)
(rename [super-on-size on-size]
[super-on-set-focus on-set-focus]
[super-on-kill-focus on-kill-focus]
[super-pre-on-char pre-on-char])
(private-field
[pre-wx->proxy (lambda (orig-w e k)
;; MacOS: w may not be something the user knows
;; Look for a parent, and shift coordinates
(let loop ([w orig-w])
(if w
(if (is-a? w wx/proxy<%>)
(if (eq? w orig-w)
(k (wx->proxy w) e)
(let ([bx (box (send e get-x))]
[by (box (send e get-y))])
(send orig-w client-to-screen bx by)
(send w screen-to-client bx by)
(let ([new-e (if (e . is-a? . wx:key-event%)
(instantiate wx:key-event% ()
[key-code (send e get-key-code)])
(instantiate wx:mouse-event% ()
[event-type (send e get-event-type)]
[left-down (send e get-left-down)]
[right-down (send e get-right-down)]
[middle-down (send e get-middle-down)]))])
(when (e . is-a? . wx:key-event%)
(send new-e set-key-release-code (send e get-key-release-code)))
(send new-e set-time-stamp (send e get-time-stamp))
(send new-e set-alt-down (send e get-alt-down))
(send new-e set-control-down (send e get-control-down))
(send new-e set-meta-down (send e get-meta-down))
(send new-e set-shift-down (send e get-shift-down))
(send new-e set-x (unbox bx))
(send new-e set-y (unbox by))
(k (wx->proxy w) new-e))))
(loop (send w get-parent)))
#f)))]
[old-w -1]
[old-h -1]
[old-x -1]
[old-y -1])
(override
[on-drop-file (entry-point
(lambda (f)
(as-exit
(lambda ()
(send (get-proxy) on-drop-file f)))))]
[on-size (lambda (bad-w bad-h)
(super-on-size bad-w bad-h)
;; Delay callback to make sure X structures (position) are updated, first.
;; Also, Windows needs a trampoline.
(queue-window-callback
this
(entry-point
(lambda ()
(let ([mred (get-mred)])
(when mred
(let* ([w (get-width)]
[h (get-height)])
(when (not (and (= w old-w) (= h old-h)))
(set! old-w w)
(set! old-h h)
(as-exit (lambda () (send mred on-size w h)))))
(let* ([p (area-parent)]
[x (- (get-x) (or (and p (send p dx)) 0))]
[y (- (get-y) (or (and p (send p dy)) 0))])
(when (not (and (= x old-x) (= y old-y)))
(set! old-x x)
(set! old-y y)
(as-exit (lambda () (send mred on-move x y)))))))))))]
[on-set-focus (entry-point
(lambda ()
; Windows circumvents the event queue to call on-focus
; when you click on the window's icon in the task bar.
(queue-window-callback
this
(lambda () (send (get-proxy) on-focus #t)))
(as-exit (lambda () (super-on-set-focus)))))]
[on-kill-focus (entry-point
(lambda ()
; see on-set-focus:
(queue-window-callback
this
(lambda () (send (get-proxy) on-focus #f)))
(as-exit (lambda () (super-on-kill-focus)))))]
[pre-on-char (lambda (w e)
(or (super-pre-on-char w e)
(as-entry
(lambda ()
(pre-wx->proxy w e
(lambda (m e)
(as-exit (lambda ()
(send (get-proxy) on-subwindow-char m e)))))))))]
[pre-on-event (entry-point
(lambda (w e)
(pre-wx->proxy w e
(lambda (m e)
(as-exit (lambda ()
(send (get-proxy) on-subwindow-event m e)))))))])
(sequence (apply super-init mred proxy args))))
(define (make-container-glue% %)
(class100 % (mr prxy . args)
(inherit do-place-children do-get-graphical-min-size get-children-info get-hidden-child)
(private-field [mred mr][proxy prxy])
(override
[get-graphical-min-size (lambda ()
(cond
[mred (let ([info
(map (lambda (i)
(list (child-info-x-min i) (child-info-y-min i)
(child-info-x-stretch i) (child-info-y-stretch i)))
(get-children-info))])
(let-values ([(w h) (as-exit (lambda () (send mred container-size
(if (get-hidden-child)
(cdr info) ; hidden child is first
info))))])
(list w h)))]
[else (do-get-graphical-min-size)]))]
[place-children (lambda (l w h)
(cond
[(null? l) null]
[mred (as-exit (lambda () (send mred place-children l w h)))]
[else (do-place-children l w h)]))])
(sequence
(apply super-init mred proxy args))))
;; Weak boxed:
(define active-main-frame (make-weak-box #f))
;; An app-handler record keeps a wrapped procedure with
;; its original wrappee.
(define-values (struct:app-handler
make-app-handler
app-handler?
app-handler-ref
app-handler-set!)
(make-struct-type 'app-handler
#f 2 0
#f null (current-inspector)
0))
(define (app-handler-orig ah)
(app-handler-ref ah 1))
(let* ([running-quit? #f]
[f (entry-point
(lambda ()
(unless running-quit?
(let ([af (weak-box-value active-main-frame)])
(when af
(set! running-quit? #t)
(queue-window-callback
af
(entry-point
(lambda ()
(dynamic-wind
void
(lambda () (send af on-exit))
(lambda () (set! running-quit? #f)))))))))))])
(wx:application-quit-handler (make-app-handler f f)))
(define (set-handler! who proc param arity)
(when proc
(unless (and (procedure? proc)
(procedure-arity-includes? proc arity))
(raise-type-error who
(format "procedure (arity ~a) or #f" arity)
proc)))
(let ([e (wx:current-eventspace)])
(when (wx:main-eventspace? e)
(param (make-app-handler
(lambda args
(parameterize ([wx:current-eventspace e])
(wx:queue-callback
(lambda () (apply proc args))
wx:middle-queue-key)))
proc)))))
(define application-preferences-handler
(case-lambda
[() (and (wx:main-eventspace? (wx:current-eventspace))
(app-handler-orig (wx:application-pref-handler)))]
[(proc)
(set-handler! 'application-preferences-handler proc
wx:application-pref-handler
0)]))
(define application-about-handler
(case-lambda
[() (or (and (wx:main-eventspace? (wx:current-eventspace))
(app-handler-orig (wx:application-about-handler)))
void)]
[(proc)
(set-handler! 'application-about-handler proc
wx:application-about-handler
0)]))
(define application-quit-handler
(case-lambda
[() (or (and (wx:main-eventspace? (wx:current-eventspace))
(app-handler-orig (wx:application-quit-handler)))
void)]
[(proc)
(set-handler! 'application-quit-handler proc
wx:application-quit-handler
0)]))
(define default-application-file-handler
(entry-point
(lambda (f)
(let ([af (weak-box-value active-main-frame)])
(when af
(queue-window-callback
af
(entry-point
(lambda () (when (send af accept-drag?)
(send af on-drop-file f))))))))))
(define (install-defh)
(wx:application-file-handler (make-app-handler
default-application-file-handler
default-application-file-handler)))
(install-defh)
(define application-file-handler
(case-lambda
[() (or (and (wx:main-eventspace? (wx:current-eventspace))
(app-handler-orig (wx:application-file-handler)))
void)]
[(proc)
;; Special case for default-application-file-handler,
;; because it need not be constrained to the main eventspace:
(if (eq? proc default-application-file-handler)
(install-defh)
(set-handler! 'application-file-handler proc
wx:application-file-handler
1))]))
(define (current-eventspace-has-standard-menus?)
(and (eq? 'macosx (system-type))
(wx:main-eventspace? (wx:current-eventspace))))
(define (current-eventspace-has-menu-root?)
(and (memq (system-type) '(macos macosx))
(wx:main-eventspace? (wx:current-eventspace))))
(define root-menu-frame #f)
(define (eventspace-handler-thread e)
(let ([t (wx:eventspace-handler-thread e)])
(or t
;; eventspace dead, or just no thread, yet?
(with-handlers ([not-break-exn?
(lambda (x)
(if (wx:eventspace-shutdown? e)
(raise-mismatch-error
'eventspace-handler-thread
"eventspace is shutdown: "
e)
(raise x)))])
(let ([done (make-semaphore)]
[t #f])
(parameterize ([wx:current-eventspace e])
(wx:queue-callback
(lambda ()
(set! t (current-thread))
(semaphore-post done))
#t)
(if (object-wait-multiple 1.0 done)
t
;; Weird - no response after 1 second. Maybe
;; someone killed the handler thread before it could
;; do our work? Or shutdown the eventspace? Or the
;; thread is busy? In any of those cases, we'll
;; succeed on the next iteration.
(eventspace-handler-thread e))))))))
(define (make-top-level-window-glue% %) ; implies make-window-glue%
(class100 (make-window-glue% %) (mred proxy . args)
(inherit is-shown? get-mred queue-visible get-eventspace)
(rename [super-on-activate on-activate])
(private-field
[act-date/seconds 0] [act-date/milliseconds 0] [act-on? #f])
(public
[on-exit (entry-point
(lambda ()
(and (is-shown?)
(let ([mred (get-mred)])
(and (and mred (as-exit (lambda () (send mred can-exit?))))
(as-exit (lambda () (send mred on-exit))))))))])
(override
[on-close (entry-point
(lambda ()
(let ([mred (get-mred)])
(if mred
(if (as-exit (lambda () (send mred can-close?)))
(begin
(as-exit (lambda () (send mred on-close)))
(queue-visible)
#t)
#f)
#t))))]
[on-activate (entry-point
(lambda (on?)
(set! act-on? on?)
(when on?
(set! act-date/seconds (current-seconds))
(set! act-date/milliseconds (current-milliseconds))
(when (wx:main-eventspace? (get-eventspace))
(set! active-main-frame (make-weak-box this))))
;; Windows needs trampoline:
(queue-window-callback
this
(lambda () (send (get-mred) on-activate on?)))
(as-exit
(lambda ()
(super-on-activate on?)))))])
(public
[is-act-on? (lambda () act-on?)]
[get-act-date/seconds (lambda () act-date/seconds)]
[get-act-date/milliseconds (lambda () act-date/milliseconds)])
(sequence (apply super-init mred proxy args))))
(define (make-canvas-glue% %) ; implies make-window-glue%
(class100 (make-window-glue% %) (mred proxy . args)
(inherit get-mred get-top-level)
(rename [super-on-char on-char]
[super-on-event on-event]
[super-on-paint on-paint]
[super-on-scroll on-scroll])
(public
[do-on-char (lambda (e) (super-on-char e))]
[do-on-event (lambda (e) (super-on-event e))]
[do-on-scroll (lambda (e) (super-on-scroll e))]
[do-on-paint (lambda () (super-on-paint))])
(override
[on-char (entry-point
(lambda (e)
(let ([mred (get-mred)])
(if mred
(as-exit (lambda () (send mred on-char e)))
(super-on-char e)))))]
[on-event (entry-point
(lambda (e)
(let ([mred (get-mred)])
(if mred
(as-exit (lambda () (send mred on-event e)))
(as-exit (lambda () (super-on-event e)))))))]
[on-scroll (entry-point
(lambda (e)
(let ([mred (get-mred)])
(if mred
;; Delay callback for Windows scrollbar
;; and Windows/Mac trampoiline
(queue-window-callback
this
(lambda () (send mred on-scroll e)))
(as-exit (lambda () (super-on-scroll e)))))))]
[on-paint (entry-point
(lambda ()
(let ([mred (get-mred)])
(if mred
(if (and (eq? 'windows (system-type))
(not (eq? (wx:current-eventspace)
(send (get-top-level) get-eventspace))))
;; Windows circumvented the event queue; delay
(queue-window-callback
this
(lambda () (send mred on-paint)))
(as-exit (lambda () (send mred on-paint))))
(as-exit (lambda () (super-on-paint)))))))])
(sequence (apply super-init mred proxy args))))
;------------- Create the actual wx classes -----------------
(define wx-frame%
(make-top-level-window-glue%
(class100 (make-top-container% wx:frame% #f) args
(rename [super-set-menu-bar set-menu-bar])
(private-field
[menu-bar #f]
[is-mdi-parent? #f])
(public
[get-the-menu-bar (lambda () menu-bar)]
[get-mdi-parent (lambda (x) x)]
[set-mdi-parent (lambda (x) (and (set! is-mdi-parent? x) #t))])
(override
[set-menu-bar
(lambda (mb)
(when mb (set! menu-bar mb))
(super-set-menu-bar mb))]
[on-menu-command
(entry-point
(lambda (id)
(let ([wx (wx:id-to-menu-item id)])
(let ([go (lambda ()
(do-command (wx->mred wx) (make-object wx:control-event% 'menu)))])
(if (eq? 'windows (system-type))
;; Windows: need trampoline
(wx:queue-callback
(entry-point (lambda () (go)))
wx:middle-queue-key)
(go))))))]
[on-menu-click
(entry-point
(lambda ()
;; Windows: no trampoline needed
(and menu-bar (send menu-bar on-demand))))]
[on-toolbar-click
(entry-point
(lambda ()
(as-exit (lambda () (send (wx->mred this) on-toolbar-button-click)))))])
(public
[handle-menu-key
(lambda (event)
(and menu-bar
;; It can't be a menu event without a
;; control, meta, or alt key...
(or (send event get-control-down)
(send event get-meta-down)
(send event get-alt-down))
(begin
(send menu-bar on-demand)
(send menu-bar handle-key event))))])
(sequence
(apply super-init args)))))
(define wx-dialog%
(make-top-level-window-glue%
(class100 (make-top-container% wx:dialog% #t) args
(sequence
(apply super-init args)))))
(define wx-button% (make-window-glue%
(class100 (make-simple-control% wx:button%) (parent cb label x y w h style)
(inherit command)
(private-field [border? (memq 'border style)])
(public [has-border? (lambda () border?)])
(override
[char-to (lambda ()
(as-exit
(lambda ()
(command (make-object wx:control-event% 'button)))))])
(sequence (super-init style parent cb label x y w h style)))))
(define wx-check-box% (class100 (make-window-glue% (make-simple-control% wx:check-box%)) (mred proxy parent cb label x y w h style)
(inherit set-value get-value command)
(override
[char-to (lambda ()
(as-exit
(lambda ()
(set-value (not (get-value)))
(command (make-object wx:control-event% 'check-box)))))])
(sequence (super-init mred proxy style parent cb label x y w h style))))
(define wx-choice% (class100 (make-window-glue% (make-simple-control% wx:choice%)) (mred proxy parent cb label x y w h choices style)
(override
[handles-key-code
(lambda (x alpha? meta?)
(or (memq x '(up down))
(and alpha? (not meta?))))])
(sequence (super-init mred proxy style parent cb label x y w h choices style))))
(define wx-message% (class100 (make-window-glue% (make-simple-control% wx:message%)) (mred proxy parent label x y style)
(override [gets-focus? (lambda () #f)])
(sequence (super-init mred proxy style parent label x y style))))
(define wx-gauge%
(make-window-glue%
(class100 (make-control% wx:gauge%
const-default-x-margin const-default-y-margin
#f #f)
(parent label range style)
(inherit get-client-size get-width get-height set-size
stretchable-in-x stretchable-in-y set-min-height set-min-width
get-parent)
(override [gets-focus? (lambda () #f)])
(private-field
; # pixels per unit of value.
[pixels-per-value 1])
(sequence
(super-init style parent label range -1 -1 -1 -1 style)
(let-values ([(client-width client-height) (get-two-int-values
(lambda (a b) (get-client-size a b)))])
(let ([delta-w (- (get-width) client-width)]
[delta-h (- (get-height) client-height)]
[vertical-labels? (eq? (send (send (get-parent) get-window) get-label-position) 'vertical)]
[horizontal? (memq 'horizontal style)])
(set-min-width (if horizontal?
(let ([cw (min const-max-gauge-length
(* range pixels-per-value))])
(max (if vertical-labels?
cw
(+ cw delta-w))
(get-width)))
; client-height is the default
; dimension in the minor direction.
(+ client-width delta-w)))
(set-min-height (if horizontal?
(+ client-height delta-h)
(let ([ch (min const-max-gauge-length
(* range pixels-per-value))])
(max (if vertical-labels?
(+ ch delta-h)
ch)
(get-height)))))))
(if (memq 'horizontal style)
(begin
(stretchable-in-x #t)
(stretchable-in-y #f))
(begin
(stretchable-in-x #f)
(stretchable-in-y #t)))))))
(define list-box-wheel-step #f)
(define wx-list-box%
(make-window-glue%
(class100 (make-control% wx:list-box%
const-default-x-margin const-default-y-margin
#t #t) (parent cb label kind x y w h choices style)
(rename
[super-pre-on-char pre-on-char])
(inherit get-first-item
set-first-visible-item)
(private
[scroll (lambda (dir)
(unless list-box-wheel-step
(set! list-box-wheel-step (get-preference '|MrEd:wheelStep| (lambda () 3)))
(unless (and (number? list-box-wheel-step)
(exact? list-box-wheel-step)
(integer? list-box-wheel-step)
(<= 1 list-box-wheel-step 100))
(set! list-box-wheel-step 3)))
(let ([top (get-first-item)])
(set-first-visible-item (+ top (* list-box-wheel-step dir)))))])
(override
[handles-key-code (lambda (x alpha? meta?)
(case x
[(up down) #t]
[else (and alpha? (not meta?))]))]
[pre-on-char (lambda (w e)
(or (super-pre-on-char w e)
(case (send e get-key-code)
[(wheel-up) (scroll -1) #t]
[(wheel-down) (scroll 1) #t]
[else #f])))])
(sequence (super-init style parent cb label kind x y w h choices style)))))
(define wx-radio-box%
(make-window-glue%
(class100 (make-simple-control% wx:radio-box%) (parent cb label x y w h choices major style)
(inherit number orig-enable set-selection command)
(rename [super-enable enable]
[super-is-enabled? is-enabled?])
(override
[enable
(case-lambda
[(on?) (super-enable on?)]
[(which on?) (when (< -1 which (number))
(vector-set! enable-vector which (and on? #t))
(orig-enable which on?))])]
[is-enabled?
(case-lambda
[() (super-is-enabled?)]
[(which) (and (< -1 which (number))
(vector-ref enable-vector which))])])
(private-field [is-vertical? (memq 'vertical style)])
(public
[vertical? (lambda () is-vertical?)]
[char-to-button (lambda (i)
(as-exit
(lambda ()
(set-selection i)
(command (make-object wx:control-event% 'radio-box)))))])
(sequence (super-init style parent cb label x y w h choices major style))
(private-field [enable-vector (make-vector (number) #t)]))))
(define wx-slider%
(make-window-glue%
(class100 (make-control% wx:slider%
const-default-x-margin const-default-y-margin
#f #f)
(parent func label value min-val max-val style)
(inherit set-min-width set-min-height stretchable-in-x stretchable-in-y
get-client-size get-width get-height get-parent)
(private-field
; # pixels per possible setting.
[pixels-per-value 3])
; 3 is good because with horizontal sliders under Xt, with 1 or 2
; pixels per value, the thumb is too small to display the number,
; which looks bad.
(sequence
(super-init style parent func label value min-val max-val -1 -1 -1 style)
(let-values ([(client-w client-h) (get-two-int-values (lambda (a b)
(get-client-size a b)))])
(let* ([horizontal? (memq 'horizontal style)]
[vertical-labels? (eq? (send (send (get-parent) get-window) get-label-position) 'vertical)]
[range (+ (* pixels-per-value (add1 (- max-val min-val)))
(cond
[(and horizontal? (not vertical-labels?)) (- (get-width) client-w)]
[(and (not horizontal?) vertical-labels?) (- (get-height) client-h)]
[else 0]))])
((if horizontal? (lambda (v) (set-min-width v)) (lambda (v) (set-min-height v)))
(max ((if horizontal? (lambda () (get-width)) (lambda () (get-height))))
(min const-max-gauge-length range)))
(stretchable-in-x horizontal?)
(stretchable-in-y (not horizontal?))))))))
(define wx-canvas% (make-canvas-glue%
(class100 (make-control% wx:canvas% 0 0 #t #t) (parent x y w h style)
(private-field
[tabable? #f])
(public
[on-tab-in (lambda () (send (wx->mred this) on-tab-in))]
[get-tab-focus (lambda () tabable?)]
[set-tab-focus (lambda (v) (set! tabable? v))])
(override
[gets-focus? (lambda () tabable?)]
[handles-key-code
(lambda (code alpha? meta?)
(or meta? (not tabable?)))])
(sequence
(super-init style parent x y w h style)))))
;--------------------- tab group -------------------------
(define bg-color (wx:get-panel-background))
(define tab-v-space 2)
(define raise-h 2)
(define (scale-color c f)
(make-object wx:color%
(min 255 (floor (* f (send c red))))
(min 255 (floor (* f (send c green))))
(min 255 (floor (* f (send c blue))))))
(define trans-pen (send (wx:get-the-pen-list) find-or-create-pen "white" 0 'transparent))
(define light-pen (send (wx:get-the-pen-list) find-or-create-pen (scale-color bg-color #e1.35) 0 'solid))
(define dark-pen (send (wx:get-the-pen-list) find-or-create-pen (scale-color bg-color #e0.6) 0 'solid))
(define dark-brush (send (wx:get-the-brush-list) find-or-create-brush (scale-color bg-color #e0.8) 'solid))
(define canvas-based-tab-group%
(class wx-canvas%
(init mred proxy style parent call-back label tab-labels style-again)
(define callback call-back)
(define tabs (map wx:label->plain-label tab-labels))
(define tab-widths #f)
(define tab-height #f)
(define font (let loop ([p parent])
(if (not (p . is-a? . wx:window%))
(loop (send p get-parent))
(send p get-control-font))))
(inherit get-dc get-client-size get-mred
set-min-width set-min-height
set-tab-focus
set-background-to-gray)
(rename [super-on-size on-size])
(define selected 0)
(define tracking-pos #f)
(define tracking-hit? #f)
(define regions #f)
(define redo-regions? #f)
(define border? (memq 'border style))
(define/private (compute-sizes)
(let ([dc (get-dc)])
(let ([w+hs (map (lambda (lbl)
(let-values ([(w h d a) (send dc get-text-extent lbl font)])
(cons w h)))
tabs)])
(set! tab-widths (map car w+hs))
(let-values ([(sw sh sd sa) (send dc get-text-extent " " font)])
(let ([th (ceiling (+ (* 2 tab-v-space) (apply max 0 sh (map cdr w+hs))))])
(set! tab-height (if (even? th) th (add1 th))))))))
(define/private (get-total-width)
(apply + tab-height (* (length tabs) (+ raise-h raise-h tab-height)) tab-widths))
(define/private (get-init-x)
(let-values ([(w h) (my-get-client-size)]
[(tw) (get-total-width)])
(/ (- w tw) 2)))
(define/override (on-char e) (void))
(define/override on-event
(entry-point
(lambda (e)
(cond
[(and (send e button-down?) tab-widths)
(set! tracking-pos (find-click (send e get-x) (send e get-y)))
(when tracking-pos
(set! tracking-hit? #t)
(update-tracking))]
[(and (send e dragging?) tracking-pos)
(let ([hit? (equal? tracking-pos (find-click (send e get-x) (send e get-y)))])
(unless (eq? tracking-hit? hit?)
(set! tracking-hit? hit?)
(update-tracking)))]
[(and (send e button-up?) tracking-pos
(equal? tracking-pos (find-click (send e get-x) (send e get-y)))
(not (= tracking-pos selected)))
;; Button released for final selection
(let ([new tracking-pos])
(set! tracking-pos #f)
(set! tracking-hit? #f)
(set-selection new)
(as-exit
(lambda ()
(callback this (make-object wx:control-event% 'tab-panel)))))]
;; otherwise, turn off tracking...
[else
(when tracking-hit?
(set! tracking-hit? #f)
(update-tracking))
(set! tracking-pos #f)]))))
(define/private (update-tracking)
(let ([dc (get-dc)])
(send dc set-clipping-region (list-ref regions tracking-pos))
(on-paint)
(send dc set-clipping-region #f)))
(define tmp-rgn #f)
(define/private (find-click x y)
(ready-regions)
(unless tmp-rgn
(set! tmp-rgn (make-object wx:region% (get-dc))))
(let loop ([rl regions][pos 0])
(if (null? rl)
#f
(begin
(send tmp-rgn set-rectangle x y 1 1)
(send tmp-rgn intersect (car rl))
(if (send tmp-rgn is-empty?)
(loop (cdr rl) (add1 pos))
pos)))))
(define/private (setup-regions)
(let ([dc (get-dc)])
(set! regions
(map (lambda (tpl r)
(let ([points (map (lambda (p) (make-object wx:point% (car p) (+ 2 raise-h (cadr p))))
tpl)])
(send r set-polygon points))
r)
(draw-once #f 0 #f #f 0)
(if regions
regions
(map (lambda (x)
(make-object wx:region% dc))
tabs))))
(set! redo-regions? #f)))
(define/private (ready-regions)
(unless (and regions (not redo-regions?))
(setup-regions)))
(define (draw-once dc w light? dark? inset)
(let ([init-x (get-init-x)])
(let loop ([x init-x][l tabs][wl tab-widths][pos 0])
(if (null? l)
null
(let ([next-x (+ x tab-height (car wl))]
[-sel-d (if (= pos selected) (- raise-h) 0)])
(cons
(append
;; start point
(list (list (+ x tab-height -sel-d inset) (+ 2 tab-height (- inset))))
;; left line
(begin
(when (= pos selected)
(when light?
(send dc draw-line 0 tab-height x tab-height)
(send dc draw-line 0 (add1 tab-height) x (add1 tab-height))))
(let ([short (if (or (= pos 0) (= pos selected))
0
(+ (/ tab-height 2)
(if (= selected (sub1 pos))
raise-h
0)))])
(when light?
(send dc draw-line (+ x short -sel-d) (- tab-height short) (+ x tab-height) -sel-d)
(send dc draw-line (+ x short -sel-d 1) (- tab-height short) (+ x tab-height 1) -sel-d))
(list (list (+ x short -sel-d -2 inset) (- tab-height short -2 inset))
(list (+ x tab-height inset) (+ -sel-d inset)))))
;; top line
(begin
(when light?
(send dc draw-line (+ x tab-height) -sel-d next-x -sel-d)
(send dc draw-line (+ x tab-height) (+ 1 -sel-d) next-x (+ 1 -sel-d)))
(list (list (+ 1 next-x (- inset)) (+ inset -sel-d))))
;; right line
(let* ([short (if (= (add1 pos) selected)
(+ (/ tab-height 2) (sub1 raise-h))
0)]
[short-d (if (zero? short) 0 -1)])
(when dark?
(send dc draw-line (+ 1 next-x) (+ -sel-d 1) (- (+ next-x tab-height) short 1 -sel-d) (- tab-height short 1))
(send dc draw-line next-x (+ -sel-d 1)
(- (+ next-x tab-height) short 2 -sel-d short-d) (- tab-height short 1 short-d)))
(list (list (- (+ next-x tab-height) -sel-d short (- short-d) -2 inset) (- tab-height short -2 inset))))
;; end point
(begin
(when light?
(when (= pos selected)
(send dc draw-line (+ next-x tab-height) tab-height w tab-height)
(send dc draw-line (+ next-x tab-height) (add1 tab-height) w (add1 tab-height)))
(send dc draw-text (car l) (+ x tab-height) (- tab-v-space (if (= pos selected) raise-h 0))))
(list (list (+ next-x inset (if (= selected (add1 pos)) -2 0)) (+ 2 tab-height (- inset))))))
(loop next-x (cdr l) (cdr wl) (add1 pos))))))))
(define/override on-paint
(entry-point
(lambda ()
(compute-sizes)
(let ([dc (get-dc)])
(send dc set-background bg-color)
(send dc set-font font)
(send dc clear)
(send dc set-origin 0 (+ 2 raise-h))
(when (and tracking-pos tracking-hit?)
(let ([b (send dc get-brush)])
(send dc set-brush dark-brush)
(send dc set-pen trans-pen)
(send dc draw-polygon (map (lambda (x) (make-object wx:point% (car x) (cadr x)))
(list-ref (draw-once #f 0 #f #f 1) tracking-pos)))
(send dc set-brush b)))
(let-values ([(w h) (my-get-client-size)])
(send dc set-pen light-pen)
(draw-once dc w #t #f 0)
(when border?
(when (> h tab-height)
(send dc draw-line 0 tab-height 0 h)
(send dc draw-line 1 tab-height 1 h)))
(send dc set-pen dark-pen)
(draw-once dc w #f #t 0)
(when border?
(when (> h tab-height)
(send dc draw-line (- w 1) tab-height (- w 1) (- h raise-h))
(send dc draw-line (- w 2) (+ 1 tab-height) (- w 2) (- h raise-h))
(send dc draw-line 0 (- h 3 raise-h) w (- h 3 raise-h))
(send dc draw-line 1 (- h 4 raise-h) w (- h 4 raise-h)))))
(send dc set-origin 0 0)))))
(define/override (on-size w h)
(set! redo-regions? #t)
(super-on-size w h))
(define/private (my-get-client-size)
(get-two-int-values (lambda (a b) (get-client-size a b))))
(define/public (get-selection)
selected)
(define/public (set-selection i)
(as-entry
(lambda ()
(ready-regions)
(when (< -1 i (length regions))
(let* ([dc (get-dc)]
[r (make-object wx:region% dc)]
[old-rgn (list-ref regions selected)])
(set! selected i)
(send r union old-rgn)
(setup-regions)
(let ([new-rgn (list-ref regions selected)])
;; Union the new and old regions and repaint:
(send r union new-rgn)
(send dc set-clipping-region r)
(on-paint)
(send dc set-clipping-region #f)))))))
(define -append
(entry-point
(lambda (s)
(set! tabs (append tabs (list (wx:label->plain-label s))))
(set! tab-widths #f)
(set! regions #f)
(on-paint))))
(public (-append append))
(define/public (delete i)
(as-entry
(lambda ()
(set! tabs (let loop ([pos 0][tabs tabs])
(if (= i pos)
(cdr tabs)
(cons (car tabs) (loop (add1 pos) (cdr tabs))))))
(set! selected (min selected (max 0 (sub1 (length tabs)))))
(set! regions #f)
(set! tab-widths #f)
(on-paint))))
(define/override (handles-key-code code alpha? meta?)
#f)
(super-instantiate (mred proxy parent -1 -1 -1 -1 null))
(set-background-to-gray)
(compute-sizes)
(set-min-width (inexact->exact (ceiling (get-total-width))))
(set-min-height (inexact->exact (ceiling (+ tab-height 9 raise-h))))
(set-tab-focus #f)))
(define wx-tab-group%
(if (eq? 'unix (system-type))
canvas-based-tab-group%
(class (make-window-glue%
(make-control% wx:tab-group% 0 0 #t #t))
(define/override (gets-focus?) #f)
(super-instantiate ()))))
(define group-right-inset 4)
(define canvas-based-group-box%
(class wx-canvas%
(init mred proxy style parent label style-again)
(define font (let loop ([p parent])
(if (not (p . is-a? . wx:window%))
(loop (send p get-parent))
(send p get-control-font))))
(inherit get-dc get-client-size get-mred
set-min-width set-min-height
set-tab-focus
set-background-to-gray)
(rename [super-on-size on-size])
(define lbl label)
(define lbl-w 0)
(define lbl-h 0)
(define/private (compute-sizes)
(let ([dc (get-dc)])
(let-values ([(w h d a) (send dc get-text-extent lbl font)])
(set! lbl-w w)
(set! lbl-h h))))
(define/override (on-char e) (void))
(define/override (on-event e) (void))
(define/override on-paint
(entry-point
(lambda ()
(let ([dc (get-dc)])
(send dc set-background bg-color)
(send dc set-font font)
(send dc clear)
(send dc draw-text lbl group-right-inset 0)
(send dc set-pen light-pen)
(let-values ([(w h) (my-get-client-size)])
(send dc draw-line
1 (/ lbl-h 2)
(- group-right-inset 2) (/ lbl-h 2))
(send dc draw-line
1 (/ lbl-h 2)
1 (- h 2))
(send dc draw-line
1 (- h 2)
(- w 2) (- h 2))
(send dc draw-line
(- w 2) (- h 2)
(- w 2) (/ lbl-h 2))
(send dc draw-line
(- w 2) (/ lbl-h 2)
(min (- w 2)
(+ group-right-inset 4 lbl-w))
(/ lbl-h 2)))))))
(define/private (my-get-client-size)
(get-two-int-values (lambda (a b) (get-client-size a b))))
(define/override (handles-key-code code alpha? meta?)
#f)
(define/public (set-label l)
(set! lbl l)
(on-paint))
(super-instantiate (mred proxy parent -1 -1 -1 -1 null))
(set-background-to-gray)
(compute-sizes)
(set-min-width (inexact->exact (ceiling (+ lbl-w group-right-inset 4))))
(set-min-height (inexact->exact (ceiling (+ lbl-h 6))))
(set-tab-focus #f)))
(define wx-group-box%
(if (eq? 'unix (system-type))
canvas-based-group-box%
(class (make-window-glue%
(make-control% wx:group-box% 0 0 #t #t))
(define/override (gets-focus?) #f)
(super-instantiate ()))))
;--------------------- wx media Classes -------------------------
(define (make-editor-canvas% %)
(class100 % (parent x y w h name style spp init-buffer)
(inherit get-editor force-redraw
call-as-primary-owner min-height get-size
get-hard-minimum-size set-min-height)
(rename [super-set-editor set-editor]
[super-on-set-focus on-set-focus])
(private-field
[fixed-height? #f]
[fixed-height-lines 0]
[orig-hard #f]
[single-line-canvas? #f]
[tabable? #f])
(override
[on-container-resize (lambda ()
(let ([edit (get-editor)])
(when edit
(as-exit (lambda () (send edit on-display-size-when-ready))))))]
[on-set-focus
(entry-point
(lambda ()
(as-exit (lambda () (super-on-set-focus)))
(let ([m (get-editor)])
(when m
(let ([mred (wx->mred this)])
(when mred
(as-exit (lambda () (send m set-active-canvas mred)))))))))]
[set-editor
(letrec ([l (case-lambda
[(edit) (l edit #t)]
[(edit redraw?)
(let ([old-edit (get-editor)])
(super-set-editor edit redraw?)
(let ([mred (wx->mred this)])
(when mred
(when old-edit
(as-exit
(lambda () (send old-edit remove-canvas mred))))
(when edit
(as-exit
(lambda () (send edit add-canvas mred))))))
(update-size)
;; force-redraw causes on-container-resize to be called,
;; but only when the size of the canvas really matters
;; (i.e., when it is shown)
(force-redraw))])])
l)]
[handles-key-code
(lambda (x alpha? meta?)
(case x
[(#\tab #\return escape) (and (not tabable?)
(not single-line-canvas?))]
[else (not meta?)]))]
[popup-for-editor (entry-point
(lambda (e m)
(let ([mwx (mred->wx m)])
(and (send mwx popup-grab e)
(as-exit (lambda () (send m on-demand) #t))
mwx))))])
(public
[set-tabable (lambda (on?) (set! tabable? on?))]
[is-tabable? (lambda () tabable?)]
[on-tab-in (lambda ()
(let ([mred (wx->mred this)])
(when mred
(send mred on-tab-in))))]
[set-single-line (lambda () (set! single-line-canvas? #t))]
[is-single-line? (lambda () single-line-canvas?)]
[set-line-count (lambda (n)
(if n
(begin
(unless orig-hard
(let-values ([(hmw hmh) (get-hard-minimum-size)])
(set! orig-hard hmh)))
(set! fixed-height? #t)
(set! fixed-height-lines n))
(when orig-hard
(set! fixed-height? #f)
(set-min-height orig-hard)))
(update-size))]
[get-line-count (lambda () (and fixed-height? fixed-height-lines))]
[update-size
(lambda ()
(let ([edit (get-editor)])
(when (and edit fixed-height?)
(let* ([top (if (is-a? edit text%)
(send edit line-location 0 #t)
0)]
[bottom (if (is-a? edit text%)
(send edit line-location 0 #f)
14)]
[height (- bottom top)])
(let* ([ch (box 0)]
[h (box 0)])
(call-as-primary-owner
(lambda ()
(send (send edit get-admin)
get-view #f #f #f ch)))
(get-size (box 0) h)
(let ([new-min-height (+ (* fixed-height-lines height)
(- (unbox h) (unbox ch)))])
(set-min-height (inexact->exact (round new-min-height)))
(force-redraw)))))))])
(rename [super-set-y-margin set-y-margin])
(override
[set-y-margin (lambda (m)
(super-set-y-margin m)
(when fixed-height? (update-size)))])
(sequence
(super-init style parent x y w h (or name "") style spp init-buffer)
(when init-buffer
(let ([mred (wx->mred this)])
(when mred
(as-exit (lambda () (send init-buffer add-canvas mred)))))))))
(define wx-editor-canvas% (make-canvas-glue%
(make-editor-canvas% (make-control% wx:editor-canvas%
0 0 #t #t))))
(define internal-editor<%> (interface ()))
(define editor<%> (interface (wx:editor<%>)
get-canvases
get-active-canvas set-active-canvas
get-canvas
add-canvas remove-canvas
auto-wrap get-max-view-size))
(define-local-member-name
-format-filter
-get-current-format
-get-file-format
-set-file-format
-set-format)
(define (make-editor-buffer% % can-wrap? get-editor%)
; >>> This class is instantiated directly by the end-user <<<
(class100* % (editor<%> internal-editor<%>) args
(inherit get-max-width set-max-width get-admin get-view-size
get-keymap get-style-list
can-load-file? on-load-file after-load-file
set-modified set-filename)
(rename [super-on-display-size on-display-size]
[super-get-view-size get-view-size]
[super-copy-self-to copy-self-to]
[super-print print]
[super-get-filename get-filename]
[super-begin-edit-sequence begin-edit-sequence]
[super-end-edit-sequence end-edit-sequence]
[super-erase erase]
[super-insert-port insert-port]
[super-clear-undos clear-undos]
[super-get-load-overwrites-styles get-load-overwrites-styles])
(private-field
[canvases null]
[active-canvas #f]
[auto-set-wrap? #f])
(private
[max-view-size
(lambda ()
(let ([wb (box 0)]
[hb (box 0)])
(super-get-view-size wb hb)
(unless (or (null? canvases) (null? (cdr canvases)))
(for-each
(lambda (canvas)
(send canvas call-as-primary-owner
(lambda ()
(let ([wb2 (box 0)]
[hb2 (box 0)])
(super-get-view-size wb2 hb2)
(set-box! wb (max (unbox wb) (unbox wb2)))
(set-box! hb (max (unbox hb) (unbox hb2)))))))
canvases))
(values (unbox wb) (unbox hb))))])
(public
[-format-filter (lambda (f) f)]
[-set-file-format (lambda (f) (void))]
[-get-file-format (lambda () 'standard)])
(override
[insert-file
(opt-lambda ([file #f] [format 'guess] [show-errors? #t])
(dynamic-wind
(lambda () (super-begin-edit-sequence))
(lambda () (super-insert-port file format #f))
(lambda () (super-end-edit-sequence))))]
[load-file
(opt-lambda ([file #f] [format 'guess] [show-errors? #t])
(let* ([temp-filename?-box (box #f)]
[old-filename (super-get-filename temp-filename?-box)])
(let ([file (if (or (not file) (string=? file ""))
(if (or (equal? file "") (not old-filename) (unbox temp-filename?-box))
(let ([path (if old-filename
(path-only old-filename)
#f)])
(get-file path))
old-filename)
file)])
(and
file
(can-load-file? file (-format-filter format))
(begin
(on-load-file file (-format-filter format))
(let ([port (open-input-file file)]
[finished? #f])
(dynamic-wind
void
(lambda ()
(wx:begin-busy-cursor)
(super-begin-edit-sequence)
(dynamic-wind
void
(lambda ()
(super-erase)
(unless (and (not (unbox temp-filename?-box))
(equal? file old-filename))
(set-filename file #f))
(let ([format (if (eq? format 'same)
(-get-file-format)
format)])
(let ([new-format (super-insert-port port
(-format-filter format)
(super-get-load-overwrites-styles))])
(close-input-port port) ; close as soon as possible
(-set-file-format new-format)))) ; text% only
(lambda ()
(super-end-edit-sequence)
(wx:end-busy-cursor)))
(super-clear-undos)
(set-modified #f)
(set! finished? #t)
#t)
(lambda ()
(after-load-file finished?)
;; In case it wasn't closed before:
(close-input-port port)))))))))])
(public
[get-canvases (entry-point (lambda () (map wx->mred canvases)))]
[get-active-canvas (entry-point (lambda () (and active-canvas (wx->mred active-canvas))))]
[get-canvas
(entry-point
(lambda ()
(let ([c (or active-canvas
(and (not (null? canvases))
(car canvases)))])
(and c (wx->mred c)))))]
[set-active-canvas
(entry-point
(lambda (new-canvas)
(check-instance '(method editor<%> set-active-canvas) editor-canvas% 'editor-canvas% #t new-canvas)
(set! active-canvas (mred->wx new-canvas))))]
[add-canvas
(entry-point
(lambda (new-canvas)
(check-instance '(method editor<%> add-canvas) editor-canvas% 'editor-canvas% #f new-canvas)
(let ([new-canvas (mred->wx new-canvas)])
(unless (memq new-canvas canvases)
(set! canvases (cons new-canvas canvases))))))]
[remove-canvas
(entry-point
(lambda (old-canvas)
(check-instance '(method editor<%> remove-canvas) editor-canvas% 'editor-canvas% #f old-canvas)
(let ([old-canvas (mred->wx old-canvas)])
(when (eq? old-canvas active-canvas)
(set! active-canvas #f))
(set! canvases (remq old-canvas canvases)))))]
[auto-wrap (case-lambda
[() auto-set-wrap?]
[(on?) (as-entry
(lambda ()
(set! auto-set-wrap? (and on? #t))
(as-exit
(lambda ()
(if on?
(on-display-size)
(set-max-width 'none))))))])]
[get-max-view-size (entry-point (lambda () (max-view-size)))])
(override
[copy-self
(lambda () (let ([e (make-object (get-editor%))])
(copy-self-to e)
e))]
[copy-self-to
(lambda (e)
(super-copy-self-to e)
(send e auto-wrap auto-set-wrap?))]
[on-display-size
(entry-point
(lambda ()
(as-exit (lambda () (super-on-display-size)))
(when (as-exit (lambda () (get-admin)))
(when (and can-wrap? auto-set-wrap?)
(let-values ([(current-width) (as-exit (lambda () (get-max-width)))]
[(new-width new-height) (max-view-size)])
(when (and (not (= current-width new-width))
(< 0 new-width))
(as-exit (lambda () (set-max-width new-width)))))))))])
(private
[sp (lambda (x y z f b?)
;; let super method report z errors:
(let ([zok? (memq z '(standard postscript))])
(when zok?
(check-top-level-parent/false '(method editor<%> print) f))
(let ([p (and zok? f (mred->wx f))])
(as-exit (lambda () (super-print x y z p b?))))))])
(override
[print
(entry-point
(case-lambda
[() (sp #t #t 'standard #f #t)]
[(x) (sp x #t 'standard #f #t)]
[(x y) (sp x y 'standard #f #t)]
[(x y z) (sp x y z #f #t)]
[(x y z f) (sp x y z f #t)]
[(x y z f b?) (sp x y z f b?)]))]
[on-new-box
(entry-point
(lambda (type)
(unless (memq type '(text pasteboard))
(raise-type-error (who->name '(method editor<%> on-new-box)) "symbol: text or pasteboard" type))
(make-object editor-snip%
(let ([e (make-object (cond
[(eq? type 'pasteboard) pasteboard%]
[else text%]))])
(send e set-keymap (get-keymap))
(send e set-style-list (get-style-list))
e))))])
(sequence (apply super-init args))))
(define text%
(lock-contract-mixin
(es-contract-mixin
(class100 (make-editor-buffer% wx:text% #t (lambda () text%)) ([line-spacing 1.0]
[tab-stops null]
[auto-wrap #f])
(rename (super-auto-wrap auto-wrap)
(super-set-file-format set-file-format)
(super-get-file-format get-file-format)
(super-set-position set-position))
(override
[-get-file-format (lambda ()
(super-get-file-format))]
[-set-file-format (lambda (format)
(super-set-file-format format)
(super-set-position 0 0))])
(sequence (super-init line-spacing tab-stops)
(when auto-wrap
(super-auto-wrap #t)))))))
(define pasteboard%
(es-contract-mixin
(class100 (make-editor-buffer% wx:pasteboard% #f (lambda () pasteboard%)) ()
(override
[-format-filter (lambda (f) 'standard)])
(sequence (super-init)))))
(define editor-snip% (class100 wx:editor-snip% ([editor #f]
[with-border? #t]
[left-margin 5]
[top-margin 5]
[right-margin 5]
[bottom-margin 5]
[left-inset 1]
[top-inset 1]
[right-inset 1]
[bottom-inset 1]
[min-width 'none]
[max-width 'none]
[min-height 'none]
[max-height 'none])
(sequence
(super-init (or editor (make-object text%))
with-border?
left-margin
top-margin
right-margin
bottom-margin
left-inset
top-inset
right-inset
bottom-inset
min-width
max-width
min-height
max-height))))
(wx:set-editor-snip-maker (lambda args (apply make-object editor-snip% args)))
(wx:set-text-editor-maker (lambda () (make-object text%)))
(wx:set-pasteboard-editor-maker (lambda () (make-object pasteboard%)))
;--------------------- wx Panel Classes -------------------------
(define wx:windowless-panel%
(class100 object% (prnt x y w h style)
(private-field
[pos-x 0] [pos-y 0] [width 1] [height 1]
[parent prnt])
(public
[drag-accept-files (lambda () (void))]
[on-drop-file (lambda () (void))]
[on-set-focus (lambda () (void))]
[on-kill-focus (lambda () (void))]
[set-focus (lambda () (void))]
[on-size (lambda () (void))]
[enable (lambda () (void))]
[show (lambda (on?) (void))]
[get-parent (lambda () parent)]
[get-client-size (lambda (wb hb)
(when wb (set-box! wb width))
(when hb (set-box! hb height)))]
[set-size (lambda (x y w h)
(unless (negative? x) (set! pos-x x))
(unless (negative? y) (set! pos-y y))
(unless (negative? w) (set! width w))
(unless (negative? h) (set! height h)))]
[get-x (lambda () pos-x)]
[get-y (lambda () pos-y)]
[get-width (lambda () width)]
[get-height (lambda () height)])
(sequence (super-init))))
(define wx-basic-panel<%> (interface ()))
(define tab-h-border (if (eq? (system-type) 'unix)
2
3))
(define tab-v-bottom-border (if (memq (system-type) '(macosx macos))
2
2))
(define (wx-make-basic-panel% wx:panel% stretch?)
(class100* (wx-make-container% (make-item% wx:panel% 0 0 stretch? stretch?)) (wx-basic-panel<%>) (parent style)
(inherit get-x get-y get-width get-height
min-width min-height set-min-width set-min-height
x-margin y-margin
get-client-size area-parent
get-hard-minimum-size)
(rename [super-set-focus set-focus])
(private-field
; cache to prevent on-size from recomputing its result every
; time. when curr-width is #f, cache invalid.
curr-width
curr-height
; list of child-info structs corresponding to the children. (#f
; if no longer valid.)
[children-info null]
; Not used by linear panels
[h-align 'center] [v-align 'center]
; Needed for windowless panes
[move-children? #f]
[ignore-redraw-request? #f])
(override
[set-focus ; dispatch focus to a child panel
(lambda ()
(if (null? children)
(super-set-focus)
(send (car children) set-focus)))]
[ext-dx (lambda () (if hidden-child
tab-h-border
0))]
[ext-dy (lambda () (if hidden-child
(let-values ([(mw mh) (get-hard-minimum-size)])
(- mh tab-v-bottom-border 1))
0))])
(private-field
;; list of panel's contents.
[children null]
[hidden-child #f]
[curr-border const-default-border]
[border? (memq 'border style)])
(public
[need-move-children (lambda () (set! move-children? #t))]
[get-children (lambda () children)]
[get-hidden-child (lambda () hidden-child)]
[set-first-child-is-hidden (lambda ()
(set! hidden-child (car children))
(let ([i (send hidden-child get-info)])
(set-min-width (child-info-x-min i))
(set-min-height (child-info-y-min i))))]
[border
(case-lambda
[() curr-border]
[(new-val)
(check-margin-integer '(method area-container<%> border) new-val)
(set! curr-border new-val)
(force-redraw)])]
; add-child: adds an existing child to the panel.
; input: new-child: item% descendant to add
; returns: nothing
; effects: adds new-child to end of list of children.
[add-child
(lambda (new-child)
(unless (eq? this (send new-child area-parent))
(raise-mismatch-error 'add-child
"not a child of this container: "
(wx->proxy new-child)))
(when (memq new-child children)
(raise-mismatch-error 'add-child "child already active: "
(wx->proxy new-child)))
(change-children
(lambda (l)
(append l (list new-child)))))]
; change-children: changes the list of children.
; input: f is a function which takes the current list of children
; and returns a new list of children.
; returns: nothing
; effects: sets the list of children to the value of applying f.
[change-children
(lambda (f)
(let ([new-children (f children)]) ;; hidden child, if any , must be first!
(unless (andmap (lambda (child)
(eq? this (send child area-parent)))
new-children)
(raise-mismatch-error 'change-children
(format
(string-append
"not all members of the returned list are "
"children of the container ~e; list: ")
(wx->proxy this))
(map wx->proxy (remq hidden-child new-children))))
(let loop ([l new-children])
(unless (null? l)
(if (memq (car l) (cdr l))
(raise-mismatch-error 'change-children
"child in the returned list twice: "
(wx->proxy (car l)))
(loop (cdr l)))))
; show all new children, hide all deleted children.
(let ([added-children (list-diff new-children children)]
[removed-children (list-diff children new-children)])
(let ([non-window (ormap (lambda (child)
(and (not (is-a? child wx:window%))
child))
removed-children)])
(when non-window
(raise-mismatch-error 'change-children
(format "cannot delete non-window area in ~e: "
(wx->proxy this))
non-window)))
;; Newly-added children may have been removed when
;; disabled, or now added into a disabled panel:
(for-each (lambda (child) (send child queue-active))
added-children)
(for-each (lambda (child) (send child show #f))
removed-children)
(set! children new-children)
(force-redraw)
(for-each (lambda (child) (send child show #t))
added-children))))]
; delete-child: removes a child from the panel.
; input: child: child to delete.
; returns: nothing
; effects: removes child from list; forces redraw.
[delete-child
(lambda (child)
(unless (memq child children)
(raise-mismatch-error 'delete-child
"not a child of this container or child is not active: "
(wx->proxy child)))
(change-children (lambda (child-list)
(remq child child-list))))]
; get-children-info: returns children info list, recomputing it
; if needed.
; input: none
; returns: list of child-info structs.
; effects: upon exit, children-info is eq? to result.
[get-children-info
(lambda ()
(unless children-info
(let* ([childs children]
[info (map (lambda (child)
(send child get-info))
childs)])
(if (and (= (length childs) (length children))
(andmap eq? childs children))
;; Got the info for the right set of children
(set! children-info info)
;; During the call to some get-info, the set of children changed;
;; try again
(get-children-info))))
children-info)]
[child-redraw-request
(lambda (from)
(unless (or ignore-redraw-request?
(not (memq from children)))
(force-redraw)))]
; do-graphical-size: creates a function which returns the minimum
; possible size for a horizontal-panel% or vertical-panel% object.
; input: compute-x/compute-y: functions which take the current x/y
; location, the amount of spacing which will come after the
; current object, and the list of child-info structs beginning
; with the current object, and return the new x/y locations.
; returns: a thunk which returns the minimum possible size of the
; entire panel (not just client) as a list of two elements:
; (min-x min-y).
[do-graphical-size
(lambda (compute-x compute-y)
(letrec ([gms-help
(lambda (kid-info x-accum y-accum first?)
(if (null? kid-info)
(list x-accum y-accum)
(gms-help
(cdr kid-info)
(compute-x x-accum kid-info (and hidden-child first?))
(compute-y y-accum kid-info (and hidden-child first?))
#f)))])
(let-values ([(client-w client-h)
(get-two-int-values (lambda (a b) (get-client-size a b)))])
(let* ([border (border)]
[min-client-size
(gms-help (get-children-info)
(* 2 border) (* 2 border)
#t)]
[delta-w (- (get-width) client-w)]
[delta-h (- (get-height) client-h)])
(list (+ delta-w (car min-client-size) (if hidden-child (* 2 tab-h-border) 0))
(+ delta-h (cadr min-client-size)))))))]
; do-get-min-graphical-size: poll children and return minimum possible
; size, as required by the graphical representation of the tree,
; of the panel.
; input: none
; returns: minimum full size (as a list, width & height) of the
; container.
; effects: none
[get-graphical-min-size (lambda () (void))]
[do-get-graphical-min-size
(lambda ()
(do-graphical-size
(lambda (x-accum kid-info first?)
(max x-accum (+ (* 2 (border))
(child-info-x-min (car kid-info)))))
(lambda (y-accum kid-info first?)
(max y-accum (+ (* 2 (border))
(child-info-y-min (car kid-info)))))))])
(override
[force-redraw
(lambda ()
(set! children-info #f)
(set! curr-width #f)
(let ([parent (area-parent)])
(send parent child-redraw-request this)))]
; get-min-size: poll children and return minimum possible size
; for the container which considers the user min sizes.
; input: none
; returns: minimum full size (as a list, width & height) of
; container.
; effects: none.
[get-min-size
(lambda ()
(let ([graphical-min-size (get-graphical-min-size)])
(list (+ (* 2 (x-margin))
(max (car graphical-min-size) (min-width)))
(+ (* 2 (y-margin))
(max (cadr graphical-min-size) (min-height))))))]
[on-container-resize
(lambda ()
(let-values ([(client-width client-height)
(get-two-int-values (lambda (a b) (get-client-size a b)))])
(unless (and (number? curr-width)
(number? curr-height)
(= curr-width client-width)
(= curr-height client-height)
(not move-children?))
(set! curr-width client-width)
(set! curr-height client-height)
(set! move-children? #f)
(redraw client-width client-height))))]
[init-min (lambda (x) (if border? 8 0))])
(public
; place-children: determines where each child of panel should be
; placed.
; input: children-info: list of (int int bool bool)
; width/height: size of panel's client area.
; returns: list of placement info for children; each item in list
; is a list of 4 elements, consisting of child's x-posn,
; y-posn, x-size, y-size (including margins). Items are in same
; order as children-info list.
[place-children (lambda (l w h) (void))]
[check-place-children
(lambda (children-info width height)
(unless (and (list? children-info)
(andmap (lambda (x) (and (list? x)
(= 4 (length x))
(integer? (car x)) (not (negative? (car x))) (exact? (car x))
(integer? (cadr x)) (not (negative? (cadr x))) (exact? (cadr x))))
children-info))
(raise-type-error (who->name '(method area-container-window<%> place-children))
"list of (list of non-negative-integer non-negative-integer boolean boolean)"
children-info))
(check-non-negative-integer '(method area-container-window<%> place-children) width)
(check-non-negative-integer '(method area-container-window<%> place-children) height))]
[do-place-children
(lambda (children-info width height)
(check-place-children children-info width height)
(let loop ([children-info children-info])
(if (null? children-info)
null
(let ([curr-info (car children-info)])
(cons
(list
0 0
(car curr-info) ; child-info-x-min
(cadr curr-info)) ; child-info-y-min
(loop (cdr children-info)))))))])
(private-field
[curr-spacing const-default-spacing])
(public
[spacing ; does nothing!
(case-lambda
[() curr-spacing]
[(new-val)
(check-margin-integer '(method area-container<%> spacing) new-val)
(set! curr-spacing new-val)])]
[do-align (lambda (h v set-h set-v)
(unless (memq h '(left center right))
(raise-type-error 'set-alignment "horizontal alignment symbol: left, center, or right" h))
(unless (memq v '(top center bottom))
(raise-type-error 'set-alignment "vertical alignment symbol: top, center, or bottom" v))
(set-h h)
(set-v (case v [(top) 'left] [(center) 'center] [(bottom) 'right])))]
[alignment (lambda (h v)
(do-align h v (lambda (h) (set! h-align h)) (lambda (h) (set! v-align v)))
(force-redraw))]
[get-alignment (lambda () (values h-align v-align))]
; redraw: redraws panel and all children
; input: width, height: size of area area in panel.
; returns: nothing
; effects: places children at default positions in panel.
[redraw
(lambda (width height)
(let ([children-info (get-children-info)]
[children children]) ; keep list of children matching children-info
(let ([l (place-children (map (lambda (i)
(list (child-info-x-min i) (child-info-y-min i)
(child-info-x-stretch i) (child-info-y-stretch i)))
(if hidden-child
(cdr children-info)
children-info))
(if hidden-child
(- width (* 2 tab-h-border))
width)
(if hidden-child
(- height (child-info-y-min (car children-info))) ;; 2-pixel border here, too
height))])
(unless (and (list? l)
(= (length l) (- (length children-info) (if hidden-child 1 0)))
(andmap (lambda (x) (and (list? x)
(= 4 (length x))
(andmap (lambda (x) (and (integer? x) (exact? x))) x)))
l))
(raise-mismatch-error 'container-redraw
"result from place-children is not a list of 4-integer lists with the correct length: "
l))
(panel-redraw children children-info (if hidden-child
(cons (list 0 0 width
(if (memq (system-type) '(macos macosx)) ;; Yucky hack
(child-info-y-min (car children-info))
height))
(let ([dy (child-info-y-min (car children-info))])
(map (lambda (i)
(list (+ (car i) tab-h-border)
(+ dy (cadr i) (- tab-v-bottom-border) -1)
(caddr i)
(cadddr i)))
l)))
l)))))]
[panel-redraw
(lambda (childs child-infos placements)
(for-each
(lambda (child info placement)
(let-values ([(x y w h) (apply values placement)])
(let ([minw (child-info-x-min info)]
[minh (child-info-y-min info)]
[xm (child-info-x-margin info)]
[ym (child-info-y-margin info)])
(dynamic-wind
(lambda () (set! ignore-redraw-request? #t))
(lambda ()
(send child set-size
(max 0 (+ x xm)) (max 0 (+ y ym))
(- (max minw w) (* 2 xm))
(- (max minh h) (* 2 ym))))
(lambda () (set! ignore-redraw-request? #f)))
(send child on-container-resize))))
childs
child-infos
placements))])
(sequence
(super-init style parent -1 -1 0 0 style))))
(define (wx-make-pane% wx:panel% stretch?)
(class100 (make-container-glue% (make-glue% (wx-make-basic-panel% wx:panel% stretch?))) args
(inherit get-parent get-x get-y need-move-children get-children)
(rename [super-set-size set-size])
(override
[on-visible
(lambda ()
(for-each (lambda (c) (send c queue-visible)) (get-children)))]
[on-active
(lambda ()
(for-each (lambda (c) (send c queue-active)) (get-children)))]
[get-window (lambda () (send (get-parent) get-window))]
[set-size (lambda (x y w h)
(super-set-size x y w h)
(need-move-children))]
[dx (lambda () (get-x))]
[dy (lambda () (get-y))])
(sequence
(apply super-init args))))
(define (wx-make-panel% wx:panel%)
(class100 (make-container-glue% (make-window-glue% (wx-make-basic-panel% wx:panel% #t))) args
(rename [super-on-visible on-visible]
[super-on-active on-active])
(inherit get-children)
(override
[on-visible
(lambda ()
(for-each (lambda (c) (send c queue-visible)) (get-children))
(super-on-visible))]
[on-active
(lambda ()
(for-each (lambda (c) (send c queue-active)) (get-children))
(super-on-active))])
(sequence (apply super-init args))))
(define (wx-make-linear-panel% wx-panel%)
(class100 wx-panel% args
(private-field
[major-align-pos 'left]
[minor-align-pos 'center])
(inherit force-redraw border get-width get-height
get-graphical-min-size)
(private-field [curr-spacing const-default-spacing])
(override
[spacing
(case-lambda
[() curr-spacing]
[(new-val)
(check-margin-integer '(method area-container<%> spacing) new-val)
(set! curr-spacing new-val)
(force-redraw)])])
(public
[minor-align (lambda (a) (set! minor-align-pos a) (force-redraw))]
[major-align (lambda (a) (set! major-align-pos a) (force-redraw))]
[major-offset (lambda (space)
(case major-align-pos
[(center) (quotient space 2)]
[(left) 0]
[(right) space]))]
[minor-offset (lambda (width size)
(case minor-align-pos
[(center) (quotient (- width size) 2)]
[(left) 0]
[(right) (- width size)]))]
[do-get-alignment (lambda (pick) (values (pick major-align-pos minor-align-pos)
(case (pick minor-align-pos major-align-pos)
[(left) 'top] [(center) 'center] [(right) 'bottom])))]
; place-linear-children: implements place-children functions for
; horizontal-panel% or vertical-panel% classes.
; input: child-major-size: function which takes a child-info struct
; and returns the child's minimum size in the major direction
; of the panel.
; child-major-stretch: function which takes a child-info
; struct and returns the child's stretchability in the major
; direction of the panel.
; child-minor-size/child-minor-stretch: see above.
; major-dim/minor-dim: functions which take the width and the
; height of the panel and return the panel's major and minor
; dimensions, respectively.
; get-h-info/get-v-info: functions which take info lists
; describing the major and minor directions and select the
; appropriate one.
; returns: a function which takes the children info, the width and the
; height of the panel's client and returns a list which contains
; posn&size info for each child.
[place-linear-children
(lambda (kid-info width height
child-major-size
child-major-stretch
child-major-offset
child-minor-size
child-minor-stretch
child-minor-position
major-dim minor-dim
get-x-info get-y-info)
(letrec ([count-stretchable
(lambda (kid-info)
(if (null? kid-info)
0
(let ([curr-info (car kid-info)])
(if (child-major-stretch curr-info)
(add1 (count-stretchable (cdr kid-info)))
(count-stretchable (cdr kid-info))))))])
(let* ([spacing (spacing)]
[border (border)]
[full-w (get-width)]
[full-h (get-height)]
[delta-list (list
(- full-w width)
(- full-h height))]
[num-stretchable (count-stretchable kid-info)]
[extra-space (- (major-dim width height)
(- (apply
major-dim
(get-graphical-min-size))
(apply major-dim delta-list)))]
[extra-per-stretchable (if (zero? num-stretchable)
0
(inexact->exact
(floor
(/ extra-space
num-stretchable))))]
[leftover (- extra-space (* extra-per-stretchable num-stretchable))]
[num-children (length kid-info)]
[major-offset (if (= num-stretchable 0)
(child-major-offset extra-space)
0)])
(letrec
([pc-help
(lambda (kid-info left-edge leftover)
(if (null? kid-info)
null
(let* ([curr-info (car kid-info)]
[rest (cdr kid-info)]
[major-posn left-edge]
[next-leftover (if (zero? leftover)
0
(- leftover 1))]
[extra-this-stretchable (if (zero? leftover)
extra-per-stretchable
(+ extra-per-stretchable 1))]
[major-size
(if (child-major-stretch curr-info)
(+ extra-this-stretchable
(child-major-size curr-info))
(child-major-size curr-info))]
[minor-posn (if (child-minor-stretch
curr-info)
border
(inexact->exact
(round
(child-minor-position
(minor-dim width height)
(child-minor-size curr-info)))))]
[minor-size (if (child-minor-stretch
curr-info)
(- (minor-dim width height)
(* 2 border))
(child-minor-size
curr-info))])
(cons
(list
(get-x-info major-posn minor-posn)
(get-y-info major-posn minor-posn)
(get-x-info major-size minor-size)
(get-y-info major-size minor-size))
(pc-help rest
(+ major-size major-posn spacing)
next-leftover)))))])
(pc-help kid-info (+ border major-offset) leftover)))))])
(sequence (apply super-init args))))
; horizontal-panel%: a panel which arranges its children in an evenly
; spaced horizontal row. Items are vertically centered (or stretched
; to fit the dialog box if they are stretchable). The items are evenly
; spaced horizontally, with any extra space divided evenly among the
; stretchable items.
(define (wx-make-horizontal-panel% wx-linear-panel%)
(class100 wx-linear-panel% args
(inherit major-align minor-align do-align do-get-alignment major-offset minor-offset
spacing border do-graphical-size place-linear-children check-place-children)
(override
[alignment (lambda (h v) (do-align h v
(lambda (x) (major-align x))
(lambda (x) (minor-align x))))]
[get-alignment (lambda () (do-get-alignment (lambda (x y) x)))]
[do-get-graphical-min-size
(lambda ()
(do-graphical-size
(lambda (x-accum kid-info hidden?)
(+ x-accum (child-info-x-min (car kid-info))
(if (or hidden? (null? (cdr kid-info)))
0
(spacing))))
(lambda (y-accum kid-info hidden?)
(max y-accum
(+ (child-info-y-min (car kid-info))
(* 2 (border)))))))]
[do-place-children
(lambda (l w h)
(check-place-children l w h)
(place-linear-children l w h
car ; child-info-x-min
caddr ; child-info-x-stretch
(lambda (s) (major-offset s))
cadr ; child-info-y-min
cadddr ; child-info-y-stretch
(lambda (s t) (minor-offset s t))
(lambda (width height) width)
(lambda (width height) height)
(lambda (major minor) major)
(lambda (major minor) minor)))])
(sequence (apply super-init args))))
; vertical-panel%. See horizontal-panel%, but reverse
; "horizontal" and "vertical."
(define (wx-make-vertical-panel% wx-linear-panel%)
(class100 wx-linear-panel% args
(inherit major-align minor-align do-align do-get-alignment major-offset minor-offset
spacing border do-graphical-size place-linear-children check-place-children)
(override
[alignment (lambda (h v) (do-align h v
(lambda (x) (minor-align x))
(lambda (x) (major-align x))))]
[get-alignment (lambda () (do-get-alignment (lambda (x y) y)))]
[do-get-graphical-min-size
(lambda ()
(do-graphical-size
(lambda (x-accum kid-info hidden?)
(max x-accum
(+ (child-info-x-min (car kid-info))
(* 2 (border)))))
(lambda (y-accum kid-info hidden?)
(+ y-accum (child-info-y-min (car kid-info))
(if (or (null? (cdr kid-info)) hidden?)
0
(spacing))))))]
[do-place-children
(lambda (l w h)
(check-place-children l w h)
(place-linear-children l w h
cadr ; child-info-y-min
cadddr ; child-info-y-stretch
(lambda (s) (major-offset s))
car ; child-info-x-min
caddr ; child-info-x-stretch
(lambda (s t) (minor-offset s t))
(lambda (width height) height)
(lambda (width height) width)
(lambda (major minor) minor)
(lambda (major minor) major)))])
(sequence (apply super-init args))))
(define wx-panel% (wx-make-panel% wx:panel%))
(define wx-linear-panel% (wx-make-linear-panel% wx-panel%))
(define wx-horizontal-panel% (wx-make-horizontal-panel% wx-linear-panel%))
(define wx-vertical-panel% (wx-make-vertical-panel% wx-linear-panel%))
(define wx-pane% (wx-make-pane% wx:windowless-panel% #t))
(define wx-grow-box-pane%
(class100 (wx-make-pane% wx:windowless-panel% #f) (mred proxy parent style)
(override
[init-min (lambda (x) (if (or (eq? (system-type) 'macos)
(eq? (system-type) 'macosx))
15
0))])
(sequence
(super-init mred proxy parent style))))
(define wx-linear-pane% (wx-make-linear-panel% wx-pane%))
(define wx-horizontal-pane% (wx-make-horizontal-panel% wx-linear-pane%))
(define wx-vertical-pane% (wx-make-vertical-panel% wx-linear-pane%))
;-------------------- Text control simulation -------------------------
(define text-field-text%
(class100 text% (cb ret-cb control set-cb-mgrs!)
(rename [super-after-insert after-insert]
[super-after-delete after-delete]
[super-on-char on-char])
(inherit get-text last-position)
(private-field
[return-cb ret-cb])
(private-field
[block-callback 1]
[callback
(lambda (type)
(when (zero? block-callback)
(let ([e (make-object wx:control-event% type)])
(as-exit (lambda ()
(cb control e))))))])
(override
[on-char
(entry-point
(lambda (e)
(let ([c (send e get-key-code)])
(unless (and (or (eq? c #\return) (eq? c #\newline))
return-cb
(return-cb (lambda () (callback 'text-field-enter) #t)))
(as-exit (lambda () (super-on-char e)))))))]
[after-insert
(lambda args
(as-entry
(lambda ()
(as-exit (lambda () (super-after-insert . args)))
(callback 'text-field))))]
[after-delete
(lambda args
(as-entry
(lambda ()
(as-exit (lambda () (super-after-delete . args)))
(callback 'text-field))))])
(sequence
(set-cb-mgrs!
(lambda (thunk)
(dynamic-wind
(lambda () (set! block-callback (add1 block-callback)))
thunk
(lambda () (set! block-callback (sub1 block-callback)))))
(lambda ()
(set! block-callback 0)))
(super-init))))
(define wx-text-editor-canvas%
(class100 wx-editor-canvas% (mred proxy control parent style)
(sequence
(super-init mred proxy parent -1 -1 100 30 #f style 100 #f))))
(define (font->delta f)
(define d (make-object wx:style-delta%))
(let ([v (send f get-face)]
[m (send f get-family)])
(if v
(send d set-delta-face v m)
(send d set-delta 'change-family m)))
(send d set-delta 'change-size (send f get-point-size))
(send d set-delta 'change-style (send f get-style))
(send d set-delta 'change-weight (send f get-weight))
(send d set-delta 'change-underline (send f get-underlined))
(send d set-delta 'change-smoothing (send f get-smoothing))
(send d set-delta 'change-size-in-pixels (send f get-size-in-pixels))
d)
(define wx-text-field%
(class100 wx-horizontal-panel% (mred proxy parent fun label value style)
;; Make text field first because we'll have to exit
;; for keymap initializer
(private-field
[func fun]
[without-callback #f]
[callback-ready #f]
[e (make-object text-field-text%
func
(lambda (do-cb)
(if multi?
#f
(do-cb)))
this
(lambda (wc cr)
(set! without-callback wc)
(set! callback-ready cr)))])
(sequence
(as-exit
(lambda ()
((current-text-keymap-initializer) (send e get-keymap)))))
(inherit alignment stretchable-in-y get-control-font area-parent
get-min-size set-min-width set-min-height)
(rename [super-place-children place-children])
(public
[command (lambda (e) ; No entry/exit needed
(check-instance '(method text-field% command) wx:control-event% 'control-event% #f e)
(func this e)
(void))]
[get-editor (lambda () e)]
[get-value (lambda () (send e get-text))] ; note: not as-entry when called
[set-value (lambda (v) (without-callback
(lambda () (send e insert v 0 (send e last-position)))))]
[set-label (lambda (str) (when l (send l set-label str)))])
(override
;; These might be called before we are fully initialized
[set-cursor (lambda (c) (send e set-cursor c #t))]
[set-focus (lambda () (when (object? c) (send c set-focus)))]
[place-children
(lambda (children-info width height)
(if (null? children-info)
null
(let ([r (super-place-children children-info width height)])
(if horiz?
;; Line up label right with text:
(cons (list* (caar r) (+ (cadar r) dy) (cddar r))
(cdr r))
r))))])
(sequence
(super-init #f proxy parent (if (memq 'deleted style) '(deleted) null))
(unless (memq 'deleted style)
(send (area-parent) add-child this)))
(private-field
[multi? (memq 'multiple style)]
[horiz? (cond
[(memq 'vertical-label style) #f]
[(memq 'horizontal-label style) #t]
[else (eq? (send (send parent get-window) get-label-position) 'horizontal)])]
[dy 0]
[p (if horiz?
this
(let ([p (make-object wx-vertical-pane% #f proxy this null)])
(send (send p area-parent) add-child p)
p))])
(sequence
(alignment 'left 'top)
(unless horiz? (send p alignment 'left 'top))
(unless multi? (stretchable-in-y #f)))
(private-field
[l (and label
(make-object wx-message% #f proxy p label -1 -1 null))]
[c (make-object wx-text-editor-canvas% #f proxy this p
(append
'(control-border)
(if multi?
(if (memq 'hscroll style)
null
'(hide-hscroll))
'(hide-vscroll hide-hscroll))))])
(sequence
(send c set-x-margin 2)
(send c set-y-margin 2)
(send e set-line-spacing 0)
(send e set-paste-text-only #t)
(send e auto-wrap (and multi? (not (memq 'hscroll style))))
(let ([f (get-control-font)]
[s (send (send e get-style-list) find-named-style "Standard")])
(send s set-delta (let ([d (font->delta f)])
(if (memq 'password style)
(begin
(send d set-face #f)
(send d set-family 'modern)
(send d set-delta-foreground "darkgray")
(send d set-delta-background "darkgray"))
d))))
(send c set-editor e)
(send c set-line-count (if multi? 3 1))
(unless multi? (send c set-single-line))
(when (and l horiz?)
;; Find amount to drop label down to line up the baselines:
(let ([wbox (box 0)]
[hbox (box 0)]
[ybox (box 0)]
[abox (box 0)])
; To bottom of first line
(send (send e get-admin) get-dc #f ybox)
(set! dy (+ -2 (abs (unbox ybox)) (send e line-location 0 #f))) ; -2 is fudge factor
; Add diff for client size
(send c get-client-size wbox hbox)
(let ([d (- (send c get-height) (unbox hbox))])
(set! dy (+ dy (quotient d 2))))
; Subtract descent of canvas-drawn text
(let ([font (send (send (send e get-style-list) find-named-style "Standard") get-font)])
(send c get-text-extent "hi" wbox hbox ybox #f font)
(set! dy (- dy (unbox ybox))))
; Subtract ascent of label
(send l get-text-extent "hi" wbox hbox ybox abox)
(set! dy (- dy (- (unbox hbox) (unbox ybox))))
; Subtract space above label
(set! dy (- dy (quotient (- (send l get-height) (unbox hbox)) 2)))
; Exact
(set! dy (inexact->exact dy))))
(when value
(set-value value)
(unless (string=? value "")
(let* ([ew (box 0)]
[cw (box 0)]
[tw (box 0)])
(send e get-extent ew #f)
(send (send e get-admin) get-view #f #f cw #f)
(send c get-size tw (box 0))
(let ([new-min-width (+ (unbox ew) (- (unbox tw) (unbox cw)))])
(send c set-min-width (inexact->exact new-min-width))))))
(let ([min-size (get-min-size)])
(set-min-width (car min-size))
(set-min-height (cadr min-size)))
(callback-ready))))
;;;;;;;;;;;;;;;;;;;;;;;;; mred Class Construction ;;;;;;;;;;;;;;;;;;;;;;;;;
;------------ More helpers ---------------
(define wx-get-mred/gen (make-generic wx<%> 'get-mred))
(define wx-get-proxy/gen (make-generic wx/proxy<%> 'get-proxy))
(define (wx->mred w) (send-generic w wx-get-mred/gen))
(define (wx->proxy w) (send-generic w wx-get-proxy/gen))
(define-syntax (param stx)
(syntax-case stx ()
[(_ get-obj method)
(syntax/loc stx
(entry-point
(case-lambda
[() (send (get-obj) method)]
[(v) (send (get-obj) method v)])))]))
(define (constructor-name who)
(string->symbol (format "initialization for ~a%" who)))
(define (iconstructor-name who)
(string->symbol (format "initialization for a class that implements ~a<%>" who)))
(define (check-container-parent who p)
(unless (is-a? p internal-container<%>)
(raise-type-error (who->name who) "built-in container<%> object" p)))
(define (check-top-level-parent/false who p)
(unless (or (not p) (is-a? p frame%) (is-a? p dialog%))
(raise-type-error (who->name who) "frame% or dialog% object or #f" p)))
(define (check-frame-parent/false who p)
(unless (or (not p) (is-a? p frame%))
(raise-type-error (who->name who) "frame% object or #f" p)))
(define (check-orientation cwho l)
(check-style cwho '(vertical horizontal) '(vertical-label horizontal-label deleted) l))
(define (check-container-ready cwho p)
(when p
(let ([wx (mred->wx p)])
(unless wx
(raise-mismatch-error (who->name cwho)
"container is not yet fully initialized: "
p)))))
(define double-boxed
(lambda (x y f)
(let ([x (box x)][y (box y)])
(f x y)
(values (unbox x) (unbox y)))))
(define-local-member-name private-wx)
(define mred%
(class object%
(init-field private-wx)
(super-make-object)))
(define mred->wx (class-field-accessor mred% private-wx))
(define (mred->wx-container w) (send (mred->wx w) get-container))
(define (wrap-callback cb)
(if (and (procedure? cb)
(procedure-arity-includes? cb 2))
(lambda (w e) (if (or (eq? 'windows (system-type))
(and (memq (system-type) '(macos macosx))
(eq? (send e get-event-type) 'slider)))
;; Mac OS slider and Windows (all): need trampoline
(wx:queue-callback
(lambda ()
(cb (wx->proxy w) e))
wx:middle-queue-key)
(cb (wx->proxy w) e)))
cb))
;---------------- Keyword propagation macros -------------------
;; Since we use class100 to construct the classes that users see,
;; keywords are not propagated by position automatically. So we use
;; the class100*/kw macro for every class exported to the user; it
;; explicitly includes all keywords supported through superclasses.
;; To avoid writing the same keyword sets over and over, we have
;; a define-keywords form.
;; Arguably, this is making a problem (using `class100' instead of
;; `class') worse as much as it solves the problem. Or maybe the
;; problem is trying to hard to make by-position and by-name
;; initialization work.
(define-syntax (define-keywords stx)
(syntax-case stx ()
[(_ name kw ...)
(with-syntax ([(kw2 ...)
(apply
append
(map (lambda (kw)
(if (identifier? kw)
(syntax-local-value kw)
(list kw)))
(syntax->list #'(kw ...))))])
#'(define-syntax name '(kw2 ...)))]))
(define-syntax (class100*/kw stx)
(syntax-case stx ()
[(_ base (intf ...) ((base-init ...) keywords) . rest)
(let ([kws (syntax-local-value #'keywords)])
(with-syntax ([super-init (datum->syntax-object
stx
'super-init
stx)]
[super-instantiate (datum->syntax-object
stx
'super-instantiate
stx)]
[this (datum->syntax-object
stx
'this)]
[(new-keyword ...) (map car kws)]
[(new-init ...) (datum->syntax-object
stx
kws)])
#'(let-syntax ([super-init
(lambda (sstx)
(syntax-case sstx ()
[(_ arg (... ...))
(with-syntax ([super-instantiate
(datum->syntax-object
sstx
'super-instantiate
sstx)]
[(new-kw (... ...))
(map (lambda (x)
(datum->syntax-object
sstx
x))
'(new-keyword ...))])
#'(super-instantiate (arg (... ...))
[new-kw new-kw] (... ...)))]))])
(class100*/names (this -hide-super-init super-instantiate)
base (intf ...) (base-init ... new-init ...)
. rest))))]))
;---------------- Window interfaces and base classes ------------
(define area<%>
(interface ()
get-parent get-top-level-window
min-width min-height
get-graphical-min-size
stretchable-width stretchable-height))
(define-keywords area%-keywords
[min-width no-val]
[min-height no-val]
[stretchable-width no-val]
[stretchable-height no-val])
(define area%
(class100* mred% (area<%>) (mk-wx get-wx-pan mismatches prnt
;; for keyword use:
[min-width no-val]
[min-height no-val]
[stretchable-width no-val]
[stretchable-height no-val])
(sequence
(let ([cwho '(iconstructor area)])
(unless (eq? min-width no-val) (check-non#f-dimension cwho min-width))
(unless (eq? min-height no-val) (check-non#f-dimension cwho min-height)))
(mismatches))
(private-field
[get-wx-panel get-wx-pan]
[parent prnt])
(public
[get-parent (lambda () parent)]
[get-top-level-window (entry-point (lambda () (wx->mred (send wx get-top-level))))]
[(minw min-width) (param get-wx-panel min-width)]
[(minh min-height) (param get-wx-panel min-height)]
[(sw stretchable-width) (param get-wx-panel stretchable-in-x)]
[(sh stretchable-height) (param get-wx-panel stretchable-in-y)]
[get-graphical-min-size (entry-point (lambda ()
(if (wx . is-a? . wx-basic-panel<%>)
(apply values (send wx get-graphical-min-size))
(send wx get-hard-minimum-size))))])
(private-field
[wx (mk-wx)])
(sequence
(super-init wx)
(unless (eq? min-width no-val) (minw min-width))
(unless (eq? min-height no-val) (minh min-height))
(unless (eq? stretchable-width no-val) (sw stretchable-width))
(unless (eq? stretchable-height no-val) (sh stretchable-height)))))
(define internal-subarea<%> (interface ()))
(define subarea<%>
(interface (area<%> internal-subarea<%>)
horiz-margin vert-margin))
(define-keywords subarea%-keywords
[horiz-margin no-val]
[vert-margin no-val])
(define (make-subarea% %) ; % implements area<%>
(class100* % (subarea<%>) (mk-wx get-wx-pan mismatches parent
;; for keyword use
[horiz-margin no-val]
[vert-margin no-val])
(sequence
(let ([cwho '(iconstructor subarea)])
(unless (eq? horiz-margin no-val) (check-margin-integer cwho horiz-margin))
(unless (eq? vert-margin no-val) (check-margin-integer cwho vert-margin))))
(private-field [get-wx-panel get-wx-pan])
(public
[(hm horiz-margin) (param get-wx-panel x-margin)]
[(vm vert-margin) (param get-wx-panel y-margin)])
(sequence
(super-init mk-wx get-wx-panel mismatches parent)
(unless (eq? horiz-margin no-val) (hm horiz-margin))
(unless (eq? vert-margin no-val) (vm vert-margin)))))
(define area-container<%>
(interface (area<%>)
reflow-container container-flow-modified begin-container-sequence end-container-sequence
container-size
get-children change-children place-children
after-new-child
add-child delete-child
border spacing
set-alignment get-alignment))
(define internal-container<%> (interface ()))
(define-keywords container%-keywords
[border no-val]
[spacing no-val]
[alignment no-val])
(define (make-container% %) ; % implements area<%>
(class100* % (area-container<%> internal-container<%>) (mk-wx get-wx-pan mismatches parent
;; for keyword use
[border no-val]
[spacing no-val]
[alignment no-val])
(sequence
(let ([cwho '(iconstructor area-container)])
(unless (eq? border no-val) (check-margin-integer cwho border))
(unless (eq? spacing no-val) (check-margin-integer cwho spacing))
(unless (eq? alignment no-val)
(unless (and (list? alignment)
(= 2 (length alignment))
(memq (car alignment) '(left center right))
(memq (cadr alignment) '(top center bottom)))
(raise-type-error (who->name cwho) "alignment list" alignment)))))
(private-field [get-wx-panel get-wx-pan])
(public
[after-new-child (lambda (c)
(check-instance '(method area-container<%> after-new-child) subarea<%> 'subarea<%> #f c)
(void))]
[reflow-container (entry-point (lambda () (send (send (get-wx-panel) get-top-level) force-redraw)))]
[container-flow-modified (entry-point (lambda ()
(let ([p (get-wx-panel)])
(send p need-move-children)
(send p force-redraw))))]
[begin-container-sequence (entry-point (lambda () (send (send (get-wx-panel) get-top-level) begin-container-sequence)))]
[end-container-sequence (entry-point (lambda () (send (send (get-wx-panel) get-top-level) end-container-sequence)))]
[get-children (entry-point (lambda () (map wx->proxy
(let ([l (send (get-wx-panel) get-children)]
[h (send (get-wx-panel) get-hidden-child)])
(if h (remq h l) l)))))]
[(bdr border) (param get-wx-panel border)]
[(spc spacing) (param get-wx-panel spacing)]
[set-alignment (entry-point (lambda (h v) (send (get-wx-panel) alignment h v)))]
[get-alignment (entry-point (lambda () (send (get-wx-panel) get-alignment)))]
[change-children (entry-point
(lambda (f)
(unless (and (procedure? f)
(procedure-arity-includes? f 1))
(raise-type-error (who->name '(method container<%> change-children))
"procedure or arity 1"
f))
(send (get-wx-panel) change-children
(lambda (kids)
(let* ([hidden (send (get-wx-panel) get-hidden-child)]
[mred-kids (map wx->proxy (remq hidden kids))]
[l (as-exit (lambda () (f mred-kids)))])
(unless (and (list? l)
(andmap (lambda (x) (is-a? x internal-subarea<%>)) l))
(raise-mismatch-error 'change-children
"result of given procedure was not a list of subareas: "
l))
(append
(if hidden (list hidden) null)
(map mred->wx l)))))))]
[container-size (entry-point
(lambda (l)
; Check l, even though we don't use it
(unless (and (list? l)
(andmap
(lambda (l)
(and (list? l) (= (length l) 4)
(integer? (car l)) (exact? (car l)) (<= 0 (car l) 10000)
(integer? (cadr l)) (exact? (cadr l)) (<= 0 (cadr l) 10000)))
l))
(raise-type-error (who->name '(method area-container<%> container-size))
"list of lists containing two exact integers in [0, 10000] and two booleans"
l))
(let ([l (send (get-wx-panel) do-get-graphical-min-size)])
(apply values l))))]
[place-children (entry-point (lambda (l w h) (send (get-wx-panel) do-place-children l w h)))]
[add-child (entry-point
(lambda (c)
(check-instance '(method area-container<%> add-child) subwindow<%> 'subwindow<%> #f c)
(send (get-wx-panel) add-child (mred->wx c))))]
[delete-child (entry-point
(lambda (c)
(check-instance '(method area-container<%> delete-child) subwindow<%> 'subwindow<%> #f c)
(send (get-wx-panel) delete-child (mred->wx c))))])
(sequence
(super-init mk-wx get-wx-panel mismatches parent)
(unless (eq? border no-val) (bdr border))
(unless (eq? spacing no-val) (spc spacing))
(unless (eq? alignment no-val) (set-alignment . alignment)))))
(define window<%>
(interface (area<%>)
on-focus focus has-focus?
on-size on-move
accept-drop-files on-drop-file
on-subwindow-char on-subwindow-event
client->screen screen->client
enable is-enabled? on-superwindow-enable
get-label set-label get-plain-label
get-client-size get-size get-width get-height get-x get-y
get-cursor set-cursor popup-menu
show is-shown? on-superwindow-show refresh))
(define-keywords window%-keywords [enabled #t])
(define (make-window% top? %) ; % implements area<%>
(class100* % (window<%>) (mk-wx get-wx-panel mismatches lbl parent crsr
;; for keyword use
[enabled #t])
(private-field [label lbl][cursor crsr])
(public
[popup-menu (entry-point
(lambda (m x y)
(check-instance '(method window<%> popup-menu) popup-menu% 'popup-menu% #f m)
(check-range-integer '(method window<%> popup-menu) x)
(check-range-integer '(method window<%> popup-menu) y)
(let ([mwx (mred->wx m)])
(and (send mwx popup-grab this)
(as-exit
(lambda ()
(send m on-demand)
(send wx popup-menu mwx x y)))))))]
[on-focus (lambda (x) (void))]
[on-size (lambda (w h)
(check-range-integer '(method window<%> on-size) w)
(check-range-integer '(method window<%> on-size) h))]
[on-move (lambda (x y)
(check-slider-integer '(method window<%> on-move) x)
(check-slider-integer '(method window<%> on-move) y))]
[on-subwindow-char (lambda (w e)
(check-instance '(method window<%> on-subwindow-char) window<%> 'window<%> #f w)
(check-instance '(method window<%> on-subwindow-char) wx:key-event% 'key-event% #f e)
#f)]
[on-subwindow-event (lambda (w e)
(check-instance '(method window<%> on-subwindow-event) window<%> 'window<%> #f w)
(check-instance '(method window<%> on-subwindow-event) wx:mouse-event% 'mouse-event% #f e)
#f)]
[on-drop-file (lambda (s)
(unless (string? s)
(raise-type-error (who->name '(method window<%> on-drop-file)) "pathname string" s)))]
[focus (entry-point (lambda () (send wx set-focus)))]
[has-focus? (entry-point (lambda () (send wx has-focus?)))]
[enable (entry-point (lambda (on?) (send wx enable on?)))]
[is-enabled? (entry-point (lambda () (send wx is-enabled?)))]
[get-label (lambda () label)]
[set-label (lambda (l)
(check-label-string/false '(method window<%> set-label) l)
(set! label (if (string? l)
(string->immutable-string l)
l)))]
[get-plain-label (lambda () (and (string? label) (wx:label->plain-label label)))]
[accept-drop-files
(entry-point
(case-lambda
[() (send wx accept-drag?)]
[(on?) (send wx drag-accept-files on?)]))]
[client->screen (entry-point
(lambda (x y)
(check-slider-integer '(method window<%> client->screen) x)
(check-slider-integer '(method window<%> client->screen) y)
(double-boxed
x y
(lambda (x y) (send wx client-to-screen x y)))))]
[screen->client (entry-point
(lambda (x y)
(check-slider-integer '(method window<%> screen->client) x)
(check-slider-integer '(method window<%> screen->client) y)
(double-boxed
x y
(lambda (x y) (send wx screen-to-client x y)))))]
[get-client-size (entry-point
(lambda ()
(double-boxed
0 0
(lambda (x y) (send wx get-client-size x y)))))]
[get-size (entry-point
(lambda ()
(double-boxed
0 0
(lambda (x y) (send wx get-size x y)))))]
[get-width (entry-point (lambda () (send wx get-width)))]
[get-height (entry-point (lambda () (send wx get-height)))]
[get-x (entry-point (lambda () (- (send wx get-x) (if top? 0 (send (send wx get-parent) ext-dx)))))]
[get-y (entry-point (lambda () (- (send wx get-y) (if top? 0 (send (send wx get-parent) ext-dy)))))]
[get-cursor (lambda () cursor)]
[set-cursor (entry-point
(lambda (x)
(send wx set-cursor x)
(set! cursor x)))]
[show (entry-point (lambda (on?)
(when on?
(unless top?
(unless (memq wx (send (send wx area-parent) get-children))
(raise-mismatch-error
(who->name '(method window<%> show))
"cannot show a subwindow that is not active in its parent: "
this))))
(send wx show on?)))]
[is-shown? (entry-point (lambda () (send wx is-shown?)))]
[on-superwindow-show (lambda (visible?) (void))]
[on-superwindow-enable (lambda (active?) (void))]
[refresh (entry-point (lambda () (send wx refresh)))])
(private-field
[wx #f])
(sequence
(super-init (lambda () (set! wx (mk-wx)) wx) get-wx-panel mismatches parent)
(unless enabled (enable #f)))))
(define area-container-window<%>
(interface (window<%> area-container<%>)
set-control-font get-control-font
set-label-font get-label-font
set-label-position get-label-position))
(define (make-area-container-window% %) ; % implements window<%> (and area-container<%>)
(class100* % (area-container-window<%>) (mk-wx get-wx-pan mismatches label parent cursor)
(private-field [get-wx-panel get-wx-pan])
(public
[get-control-font (entry-point (lambda () (send (get-wx-panel) get-control-font)))]
[set-control-font (entry-point (lambda (x) (send (get-wx-panel) set-control-font x)))]
[get-label-font (entry-point (lambda () (send (get-wx-panel) get-label-font)))]
[set-label-font (entry-point (lambda (x) (send (get-wx-panel) set-label-font x)))]
[get-label-position (entry-point (lambda () (send (get-wx-panel) get-label-position)))]
[set-label-position (entry-point (lambda (x) (send (get-wx-panel) set-label-position x)))])
(sequence
(super-init mk-wx get-wx-panel mismatches label parent cursor))))
(define top-level-window<%>
(interface (area-container-window<%>)
get-eventspace
on-activate on-traverse-char on-system-menu-char
can-close? on-close
can-exit? on-exit
get-focus-window get-edit-target-window
get-focus-object get-edit-target-object
center move resize
on-message))
(define-keywords top-level-window%-keywords
window%-keywords container%-keywords area%-keywords)
(define basic-top-level-window%
(class100* (make-area-container-window% (make-window% #t (make-container% area%))) (top-level-window<%>)
(mk-wx mismatches label parent)
(inherit show)
(rename [super-set-label set-label])
(private
[wx-object->proxy
(lambda (o)
(if (is-a? o wx:window%)
(wx->proxy o)
o))])
(override
[set-label (entry-point
(lambda (l)
(check-label-string/false '(method top-level-window<%> set-label) l)
(send wx set-title (or l ""))
(super-set-label l)))])
(public
[on-traverse-char (entry-point
(lambda (e)
(check-instance '(method top-level-window<%> on-traverse-char)
wx:key-event% 'key-event% #f e)
(send wx handle-traverse-key e)))]
[on-system-menu-char (entry-point
(lambda (e)
(check-instance '(method top-level-window<%> on-system-menu-char)
wx:key-event% 'key-event% #f e)
(and (eq? #\space (send e get-key-code))
(send e get-meta-down)
(eq? 'windows (system-type))
(send wx system-menu) #t)))]
[get-eventspace (entry-point (lambda () (send wx get-eventspace)))]
[can-close? (lambda () #t)]
[can-exit? (lambda () (can-close?))]
[on-close (lambda () (void))]
[on-exit (lambda () (on-close) (show #f))]
[on-activate (lambda (x) (void))]
[center (entry-point
(case-lambda
[() (send wx center 'both)]
[(dir) (send wx center dir)]))]
[move (entry-point
(lambda (x y)
(check-slider-integer '(method top-level-window<%> move) x)
(check-slider-integer '(method top-level-window<%> move) y)
(send wx move x y)))]
[resize (entry-point
(lambda (w h)
(check-range-integer '(method top-level-window<%> resize) w)
(check-range-integer '(method top-level-window<%> resize) h)
(send wx set-size -1 -1 w h)))]
[get-focus-window (entry-point
(lambda () (let ([w (send wx get-focus-window)])
(and w (wx->proxy w)))))]
[get-edit-target-window (entry-point
(lambda () (let ([w (send wx get-edit-target-window)])
(and w (wx->proxy w)))))]
[get-focus-object (entry-point
(lambda () (let ([o (send wx get-focus-object)])
(and o (wx-object->proxy o)))))]
[get-edit-target-object (entry-point
(lambda () (let ([o (send wx get-edit-target-object)])
(and o (wx-object->proxy o)))))]
[on-message (lambda (m) (void))])
(private-field
[wx #f]
[wx-panel #f]
[finish (entry-point
(lambda (top-level hide-panel?)
(set! wx-panel (make-object wx-vertical-panel% #f this top-level null))
(send (send wx-panel area-parent) add-child wx-panel)
(send top-level set-container wx-panel)
(when hide-panel?
(send wx-panel show #f))
top-level))])
(sequence
(super-init (lambda () (set! wx (mk-wx finish)) wx) (lambda () wx-panel) mismatches label parent arrow-cursor))))
(define subwindow<%>
(interface (window<%> subarea<%>)))
(define control<%>
(interface (subwindow<%>)
command))
(define-local-member-name hidden-child? label-checker)
(define-keywords control%-keywords
window%-keywords
subarea%-keywords
area%-keywords)
(define basic-control%
(class100* (make-window% #f (make-subarea% area%)) (control<%>) (mk-wx mismatches lbl parent cb cursor)
(rename [super-set-label set-label])
(private-field [label lbl][callback cb])
(override
[get-label (lambda () label)]
[get-plain-label (lambda () (and (string? label) (wx:label->plain-label label)))]
[set-label (entry-point
(lambda (l)
((label-checker)
'(method control<%> set-label) l)
(let ([l (if (string? l)
(string->immutable-string l)
l)])
(send wx set-label l)
(set! label l))))])
(public
[hidden-child? (lambda () #f)] ; module-local method
[label-checker (lambda () check-label-string/false)] ; module-local method
[command (lambda (e) (void (callback this e)))]) ; no entry/exit needed
(private-field
[wx #f])
(sequence
(when (string? label)
(set! label (string->immutable-string label)))
(super-init (lambda () (set! wx (mk-wx)) wx) (lambda () wx) mismatches label parent cursor)
(unless (hidden-child?)
(as-exit (lambda () (send parent after-new-child this)))))))
;--------------------- Final mred class construction --------------------
(define frame%
(class100*/kw basic-top-level-window% ()
[(label [parent #f] [width #f] [height #f] [x #f] [y #f] [style null])
top-level-window%-keywords]
(inherit on-traverse-char on-system-menu-char)
(sequence
(let ([cwho '(constructor frame)])
(check-label-string cwho label)
(check-frame-parent/false cwho parent)
(check-dimension cwho width)
(check-dimension cwho height)
(check-init-pos-integer cwho x)
(check-init-pos-integer cwho y)
(check-style cwho #f '(no-resize-border no-caption no-system-menu mdi-parent mdi-child toolbar-button hide-menu-bar)
style)
(when (memq 'mdi-child style)
(when (memq 'mdi-parent style)
(raise-type-error (who->name cwho)
"style list, 'mdi-child and 'mdi-parent are mutually exclusive"
style)))))
(rename [super-on-subwindow-char on-subwindow-char])
(private-field
[wx #f]
[status-line? #f]
[modified? #f])
(override
[on-subwindow-char (lambda (w event)
(super-on-subwindow-char w event)
(or (on-menu-char event)
(on-system-menu-char event)
(on-traverse-char event)))])
(public
[on-menu-char (entry-point
(lambda (e)
(check-instance '(method frame% on-menu-char) wx:key-event% 'key-event% #f e)
(send wx handle-menu-key e)))]
[on-toolbar-button-click (lambda () (void))]
[create-status-line (entry-point (lambda () (unless status-line? (send wx create-status-line) (set! status-line? #t))))]
[set-status-text (lambda (s) (send wx set-status-text s))]
[has-status-line? (lambda () status-line?)]
[iconize (entry-point (lambda (on?) (send wx iconize on?)))]
[is-iconized? (entry-point (lambda () (send wx iconized?)))]
[set-icon (case-lambda
[(i) (send wx set-icon i)]
[(i b) (send wx set-icon i b)]
[(i b l?) (send wx set-icon i b l?)])]
[maximize (entry-point (lambda (on?) (send wx maximize on?)))]
[get-menu-bar (entry-point (lambda () (let ([mb (send wx get-the-menu-bar)])
(and mb (wx->mred mb)))))]
[modified (entry-point
(case-lambda
[() modified?]
[(m)
(set! modified? m)
(send wx set-modified m)]))])
(sequence
(as-entry
(lambda ()
(super-init
(lambda (finish)
(set! wx (finish (make-object wx-frame% this this
(and parent (mred->wx parent)) label
(or x -11111) (or y -11111)
(or width -1) (or height -1)
style)
(memq 'mdi-parent style)))
(send wx set-mdi-parent (memq 'mdi-parent style))
wx)
(lambda ()
(let ([cwho '(constructor frame)])
(check-container-ready cwho parent)
(when (memq 'mdi-child style)
(let ([pwx (and parent (mred->wx parent))])
(unless (and pwx (send pwx get-mdi-parent))
(raise-mismatch-error (who->name cwho) "parent for 'mdi-child frame is not an 'mdi-parent frame: " parent))))))
label parent))))))
(define dialog%
(class100*/kw basic-top-level-window% ()
[(label [parent #f] [width #f] [height #f] [x #f] [y #f] [style null])
top-level-window%-keywords]
(inherit on-traverse-char on-system-menu-char)
(sequence
(let ([cwho '(constructor dialog)])
(check-label-string cwho label)
(check-top-level-parent/false cwho parent)
(for-each (lambda (x) (check-dimension cwho x)) (list width height x y))
(check-style cwho #f '(no-caption resize-border) style)))
(rename [super-on-subwindow-char on-subwindow-char])
(private-field [wx #f])
(override
[on-subwindow-char (lambda (w event)
(super-on-subwindow-char w event)
(or (on-system-menu-char event)
(on-traverse-char event)))])
(sequence
(as-entry
(lambda ()
(super-init (lambda (finish)
(set! wx (finish (make-object wx-dialog% this this
(and parent (mred->wx parent)) label #t
(or x -1) (or y -1) (or width 0) (or height 0)
style)
#f))
wx)
(lambda ()
(let ([cwho '(constructor dialog)])
(check-container-ready cwho parent)))
label parent))))))
(define (get-top-level-windows)
(remq root-menu-frame (map wx->mred (wx:get-top-level-windows))))
(define (get-top-level-focus-window)
(ormap (lambda (f) (and (send f is-act-on?) (wx->mred f))) (wx:get-top-level-windows)))
(define (get-top-level-edit-target-window)
(let loop ([l (wx:get-top-level-windows)][f #f][s 0][ms 0])
(if (null? l)
(and f (wx->mred f))
(let* ([f2 (car l)]
[s2 (send f2 get-act-date/seconds)]
[ms2 (send f2 get-act-date/milliseconds)])
(if (or (not f)
(> s2 s)
(and (= s2 s) (> ms2 ms)))
(loop (cdr l) f2 s2 ms2)
(loop (cdr l) f s ms))))))
(define message%
(class100*/kw basic-control% () [(label parent [style null]) control%-keywords]
(override
[label-checker (lambda () check-label-string-or-bitmap)]) ; module-local method
(sequence
(let ([cwho '(constructor message)])
(check-label-string/bitmap/iconsym cwho label)
(check-container-parent cwho parent)
(check-style cwho #f '(deleted) style))
(as-entry
(lambda ()
(super-init (lambda () (make-object wx-message% this this
(mred->wx-container parent)
label -1 -1 style))
(lambda ()
(let ([cwho '(constructor message)])
(check-container-ready cwho parent)))
label parent void #f))))))
(define button%
(class100*/kw basic-control% () [(label parent callback [style null]) control%-keywords]
(override
[label-checker (lambda () check-label-string-or-bitmap)]) ; module-local method
(sequence
(let ([cwho '(constructor button)])
(check-label-string-or-bitmap cwho label)
(check-container-parent cwho parent)
(check-callback cwho callback)
(check-style cwho #f '(border deleted) style))
(as-entry
(lambda ()
(super-init (lambda () (make-object wx-button% this this
(mred->wx-container parent) (wrap-callback callback)
label -1 -1 -1 -1 style))
(lambda ()
(let ([cwho '(constructor button)])
(check-container-ready cwho parent)))
label parent callback #f))))))
(define check-box%
(class100*/kw basic-control% () [(label parent callback [style null] [value #f]) control%-keywords]
(sequence
(let ([cwho '(constructor check-box)])
(check-label-string-or-bitmap cwho label)
(check-container-parent cwho parent)
(check-callback cwho callback)
(check-style cwho #f '(deleted) style)))
(override
[label-checker (lambda () check-label-string-or-bitmap)]) ; module-local method
(private-field
[wx #f])
(public
[get-value (entry-point (lambda () (send wx get-value)))]
[set-value (entry-point (lambda (v) (send wx set-value v)))])
(sequence
(as-entry
(lambda ()
(super-init (lambda ()
(set! wx (make-object wx-check-box% this this
(mred->wx-container parent) (wrap-callback callback)
label -1 -1 -1 -1 style))
wx)
(lambda ()
(let ([cwho '(constructor check-box)])
(check-container-ready cwho parent)))
label parent callback #f)))
(when value (set-value #t)))))
(define radio-box%
(class100*/kw basic-control% ()
[(label choices parent callback [style '(vertical)] [selection 0]) control%-keywords]
(private-field [chcs choices])
(sequence
(let ([cwho '(constructor radio-box)])
(check-label-string/false cwho label)
(unless (and (list? chcs) (pair? chcs)
(or (andmap label-string? chcs)
(andmap (lambda (x) (is-a? x wx:bitmap%)) chcs)))
(raise-type-error (who->name cwho) "non-empty list of strings (up to 200 characters) or bitmap% objects" chcs))
(check-container-parent cwho parent)
(check-callback cwho callback)
(check-orientation cwho style)
(check-non-negative-integer cwho selection)))
(private-field
[wx #f])
(private
[check-button
(lambda (method n)
(check-non-negative-integer `(method radio-box% ,method) n)
(unless (< n (length chcs))
(raise-mismatch-error (who->name `(method radio-box% ,method)) "no such button: " n)))])
(override
[enable (entry-point
(case-lambda
[(on?) (send wx enable on?)]
[(which on?) (check-button 'enable which)
(send wx enable which on?)]))]
[is-enabled? (entry-point
(case-lambda
[() (send wx is-enabled?)]
[(which) (check-button 'is-enabled? which)
(send wx is-enabled? which)]))])
(public
[get-number (lambda () (length chcs))]
[get-item-label (lambda (n)
(check-button 'get-item-label n)
(list-ref chcs n))]
[get-item-plain-label (lambda (n)
(check-button 'get-item-plain-label n)
(wx:label->plain-label (list-ref chcs n)))]
[get-selection (entry-point (lambda () (send wx get-selection)))]
[set-selection (entry-point
(lambda (v)
(check-button 'set-selection v)
(send wx set-selection v)))])
(sequence
(as-entry
(lambda ()
(when (andmap string? chcs)
(set! chcs (map string->immutable-string chcs)))
(super-init (lambda ()
(set! wx (make-object wx-radio-box% this this
(mred->wx-container parent) (wrap-callback callback)
label -1 -1 -1 -1 chcs 0 style))
wx)
(lambda ()
(let ([cwho '(constructor radio-box)])
(check-container-ready cwho parent)
(unless (< selection (length choices))
(raise-mismatch-error (who->name cwho)
(format "initial selection is too large, given only ~a choices: "
(length choices))
selection))))
label parent callback #f)))
(when (positive? selection)
(set-selection selection)))))
(define slider%
(class100*/kw basic-control% ()
[(label min-value max-value parent callback [init-value min-value] [style '(horizontal)])
control%-keywords]
(private-field [minv min-value][maxv max-value])
(sequence
(let ([cwho '(constructor slider)])
(check-label-string/false cwho label)
(check-slider-integer cwho minv)
(check-slider-integer cwho maxv)
(check-container-parent cwho parent)
(check-callback cwho callback)
(check-slider-integer cwho init-value)
(check-style cwho '(vertical horizontal) '(plain vertical-label horizontal-label deleted) style)))
(private-field
[wx #f])
(public
[get-value (entry-point (lambda () (send wx get-value)))]
[set-value (entry-point
(lambda (v)
(check-slider-integer '(method slider% set-value) v)
(unless (<= minv v maxv)
(raise-mismatch-error (who->name '(method slider% set-value))
(format "slider's range is ~a to ~a; cannot set the value to: "
minv maxv)
v))
(send wx set-value v)))])
(sequence
(as-entry
(lambda ()
(super-init (lambda ()
(set! wx (make-object wx-slider% this this
(mred->wx-container parent) (wrap-callback callback)
label init-value minv maxv style))
wx)
(lambda ()
(let ([cwho '(constructor slider)])
(check-container-ready cwho parent)))
label parent callback #f))))))
(define gauge%
(class100*/kw basic-control% ()
[(label range parent [style '(horizontal)]) control%-keywords]
(sequence
(let ([cwho '(constructor gauge)])
(check-label-string/false cwho label)
(check-container-parent cwho parent)
(check-gauge-integer cwho range)
(check-orientation cwho style)))
(private-field
[wx #f])
(public
[get-value (entry-point (lambda () (send wx get-value)))]
[set-value (entry-point
(lambda (v)
(check-range-integer '(method gauge% set-value) v)
(when (> v (send wx get-range))
(raise-mismatch-error (who->name '(method gauge% set-value))
(format "gauge's range is 0 to ~a; cannot set the value to: "
(send wx get-range))
v))
(send wx set-value v)))]
[get-range (entry-point (lambda () (send wx get-range)))]
[set-range (entry-point
(lambda (v)
(check-gauge-integer '(method gauge% set-range) v)
(send wx set-range v)))])
(sequence
(as-entry
(lambda ()
(super-init (lambda ()
(set! wx (make-object wx-gauge% this this
(mred->wx-container parent)
label range style))
wx)
(lambda ()
(let ([cwho '(constructor gauge)])
(check-container-ready cwho parent)))
label parent void #f))))))
(define list-control<%>
(interface (control<%>)
clear append
get-number
get-string find-string
get-selection
get-string-selection
set-selection
set-string-selection))
(define (-1=>false v) (if (negative? v) #f v))
(define basic-list-control%
(class100* basic-control% (list-control<%>) (mk-wx mismatches label parent selection callback)
(public
[append (entry-point (lambda (i) (send wx append i)))]
[clear (entry-point (lambda () (send wx clear)))]
[get-number (entry-point (lambda () (send wx number)))]
[get-string (entry-point (lambda (n) (check-item 'get-string n) (send wx get-string n)))]
[get-selection (entry-point (lambda () (and (positive? (send wx number)) (-1=>false (send wx get-selection)))))]
[get-string-selection (entry-point (lambda () (and (positive? (send wx number)) (send wx get-string-selection))))]
[set-selection (entry-point (lambda (s) (check-item 'set-selection s) (send wx set-selection s)))]
[set-string-selection (entry-point
(lambda (s) (unless (send wx set-string-selection s)
(raise-mismatch-error (who->name '(method list-control<%> set-string-selection))
"no item matching the given string: " s))))]
[find-string (entry-point (lambda (x) (-1=>false (send wx find-string x))))])
(private-field
[wx #f])
(private
[check-item
(lambda (method n)
(check-non-negative-integer `(method list-control<%> ,method) n)
(let ([m (send wx number)])
(unless (< n m)
(raise-mismatch-error (who->name `(method list-control<%> ,method))
(if (zero? m)
"control has no items; given index: "
(format "control has only ~a items, indexed 0 to ~a; given out-of-range index: "
m (sub1 m)))
n))))])
(sequence
(as-entry
(lambda ()
(super-init (lambda () (set! wx (mk-wx)) wx) mismatches label parent callback #f)))
(when selection
(set-selection selection)))))
(define (check-list-control-args cwho label choices parent callback)
(check-label-string/false cwho label)
(unless (and (list? choices) (andmap label-string? choices))
(raise-type-error (who->name cwho) "list of strings (up to 200 characters)" choices))
(check-container-parent cwho parent)
(check-callback cwho callback))
(define (check-list-control-selection cwho choices selection)
(unless (< selection (length choices))
(raise-mismatch-error (who->name cwho)
(format "initial selection is too large, given only ~a choices: "
(length choices))
selection)))
(define choice%
(class100*/kw basic-list-control% ()
[(label choices parent callback [style null] [selection 0])
control%-keywords]
(sequence
(let ([cwho '(constructor choice)])
(check-list-control-args cwho label choices parent callback)
(check-style cwho #f '(vertical-label horizontal-label deleted) style)
(check-non-negative-integer cwho selection))
(super-init (lambda () (make-object wx-choice% this this
(mred->wx-container parent) (wrap-callback callback)
label -1 -1 -1 -1 choices style))
(lambda ()
(let ([cwho '(constructor choice)])
(check-container-ready cwho parent)
(unless (= 0 selection)
(check-list-control-selection cwho choices selection))))
label parent
(and (positive? selection) selection)
callback))))
(define list-box%
(class100*/kw basic-list-control% ()
[(label choices parent callback [style '(single)] [selection #f])
control%-keywords]
(sequence
(let ([cwho '(constructor list-box)])
(check-list-control-args cwho label choices parent callback)
(check-style cwho '(single multiple extended) '(vertical-label horizontal-label deleted) style)
(check-non-negative-integer/false cwho selection)))
(rename [super-append append])
(override
[append (entry-point
(case-lambda
[(i) (super-append i)]
[(i d) (send wx append i d)]))])
(public
[delete (entry-point (lambda (n) (check-item 'delete n) (send wx delete n)))]
[get-data (entry-point (lambda (n) (check-item 'get-data n) (send wx get-data n)))]
[get-selections (entry-point (lambda () (send wx get-selections)))]
[number-of-visible-items (entry-point (lambda () (send wx number-of-visible-items)))]
[is-selected? (entry-point (lambda (n) (check-item 'is-selected? n) (send wx selected? n)))]
[set (entry-point (lambda (l)
(unless (and (list? l) (andmap label-string? l))
(raise-type-error (who->name '(method list-box% set))
"list of strings (up to 200 characters)" l))
(send wx set l)))]
[set-string (entry-point
(lambda (n d)
(check-non-negative-integer '(method list-box% set-string) n) ; int error before string
(check-label-string '(method list-box% set-string) d) ; string error before range mismatch
(check-item 'set-string n)
(send wx set-string n d)))]
[set-data (entry-point (lambda (n d) (check-item 'set-data n) (send wx set-data n d)))]
[get-first-visible-item (entry-point (lambda () (send wx get-first-item)))]
[set-first-visible-item (entry-point (lambda (n)
(check-item 'set-first-visible-item n)
(send wx set-first-visible-item n)))]
[select (entry-point
(case-lambda
[(n) (check-item 'select n) (send wx select n #t)]
[(n on?) (check-item 'select n) (send wx select n on?)]))])
(private-field
[wx #f])
(private
[check-item
(entry-point
(lambda (method n)
(check-non-negative-integer `(method list-box% ,method) n)
(let ([m (send wx number)])
(unless (< n m)
(raise-mismatch-error (who->name `(method list-box% ,method))
(if (zero? m)
"list has no items; given index: "
(format "list has only ~a items, indexed 0 to ~a; given out-of-range index: "
m (sub1 m)))
n)))))])
(sequence
(super-init (lambda ()
(let-values ([(kind style)
(cond
[(memq 'single style) (values 'single (remq 'single style))]
[(memq 'multiple style) (values 'multiple (remq 'multiple style))]
[else (values 'extended (remq 'extended style))])])
(set! wx (make-object wx-list-box% this this
(mred->wx-container parent) (wrap-callback callback)
label kind
-1 -1 -1 -1 choices style)))
wx)
(lambda ()
(let ([cwho '(constructor list-box)])
(check-container-ready cwho parent)
(when selection
(check-list-control-selection cwho choices selection))))
label parent (and (pair? choices) selection) callback))))
(define text-field%
(class100*/kw basic-control% ()
[(label parent callback [init-value ""] [style '(single)])
control%-keywords]
(sequence
(let ([cwho '(constructor text-field)])
(check-label-string/false cwho label)
(check-container-parent cwho parent)
(check-callback cwho callback)
(check-string cwho init-value)
(check-style cwho '(single multiple) '(hscroll password vertical-label horizontal-label deleted) style)))
(private-field
[wx #f])
(public
[get-editor (entry-point (lambda () (send wx get-editor)))]
[get-value (lambda () (send wx get-value))] ; note: wx method doesn't expect as-entry
[set-value (entry-point
(lambda (v)
(check-string '(method text-control<%> set-value) v)
(send wx set-value v)))])
(sequence
;; Technically a bad way to change margin defaults, since it's
;; implemented with an update after creation:
(when (eq? horiz-margin no-val) (set! horiz-margin 2))
(when (eq? vert-margin no-val) (set! vert-margin 2))
(as-entry
(lambda ()
(super-init (lambda ()
(set! wx (make-object wx-text-field% this this
(mred->wx-container parent) (wrap-callback callback)
label init-value style))
wx)
(lambda ()
(let ([cwho '(constructor text-field)])
(check-container-ready cwho parent)))
label parent callback ibeam))))))
;; Not exported:
(define tab-group%
(class100 basic-control% (label choices parent callback [style null])
(override
[hidden-child? (lambda () #t)])
(sequence
(let ([cwho '(constructor tab-group)])
(check-list-control-args cwho label choices parent callback)
(check-style cwho #f '(deleted border) style))
(super-init (lambda () (make-object wx-tab-group% this this
style
(mred->wx-container parent)
(wrap-callback callback)
label
choices
style))
(lambda ()
(let ([cwho '(constructor tab-group)])
(check-container-ready cwho parent)))
label parent callback #f))))
;; Not exported:
(define group-box%
(class100 basic-control% (label parent [style null])
(override
[hidden-child? (lambda () #t)])
(sequence
(let ([cwho '(constructor group-box)])
(check-label-string cwho label)
(check-container-parent cwho parent)
(check-style cwho #f '(deleted) style))
(super-init (lambda () (make-object wx-group-box% this this
style
(mred->wx-container parent)
label
style))
(lambda ()
(let ([cwho '(constructor group-box)])
(check-container-ready cwho parent)))
label parent void #f))))
;-------------------- Canvas class constructions --------------------
(define canvas-default-size 20) ; a default size for canvases tht fits borders without losing client sizes
(define canvas-scroll-size 10)
(define canvas-control-border-extra (case (system-type)
[(windows) 2]
[else 0]))
(define canvas<%>
(interface (subwindow<%>)
min-client-width min-client-height
on-char on-event on-paint on-scroll on-tab-in
warp-pointer get-dc))
(define-keywords canvas%-keywords
window%-keywords
subarea%-keywords
area%-keywords)
(define basic-canvas%
(class100* (make-window% #f (make-subarea% area%)) (canvas<%>) (mk-wx mismatches parent)
(public
[on-char (lambda (e) (send wx do-on-char e))]
[on-event (lambda (e) (send wx do-on-event e))]
[on-paint (lambda () (when wx (send wx do-on-paint)))]
[on-scroll (lambda (e) (send wx do-on-scroll e))]
[on-tab-in (lambda () (void))]
[min-client-width (param (lambda () wx) min-client-width)]
[min-client-height (param (lambda () wx) min-client-height)]
[warp-pointer (entry-point (lambda (x y) (send wx warp-pointer x y)))]
[get-dc (entry-point (lambda () (send wx get-dc)))])
(private-field
[wx #f])
(sequence
(as-entry
(lambda ()
(super-init (lambda () (set! wx (mk-wx)) wx) (lambda () wx) mismatches #f parent #f))))))
(define default-paint-cb (lambda (canvas dc) (void)))
(define canvas%
(class100*/kw basic-canvas% ()
[(parent [style null] [paint-callback default-paint-cb] [label #f])
canvas%-keywords]
(private-field [paint-cb paint-callback])
(inherit get-client-size get-dc set-label)
(rename [super-on-paint on-paint])
(sequence
(let ([cwho '(constructor canvas)])
(check-container-parent cwho parent)
(check-style cwho #f '(border hscroll vscroll gl deleted control-border no-autoclear) style)
(check-callback cwho paint-callback)
(check-label-string/false cwho label)))
(public
[swap-gl-buffers (lambda () (send (send (send wx get-dc) get-gl-context) swap-buffers))]
[with-gl-context (lambda (thunk)
(send (send (send wx get-dc) get-gl-context) call-as-current thunk))]
[accept-tab-focus (entry-point
(case-lambda
[() (send wx get-tab-focus)]
[(on?) (send wx set-tab-focus (and on? #t))]))]
[get-virtual-size (entry-point
(lambda () (double-boxed
0 0
(lambda (x y) (send wx get-virtual-size x y)))))]
[get-view-start (entry-point
(lambda () (double-boxed
0 0
(lambda (x y) (send wx view-start x y)))))]
[scroll (entry-point (lambda (x y)
(when x (check-fraction '(method canvas% scroll) x))
(when y (check-fraction '(method canvas% scroll) y))
(send wx scroll (or x -1) (or y -1))))]
[init-auto-scrollbars
(lambda (w h x y)
(when w (check-gauge-integer '(method canvas% init-auto-scrollbars) w))
(when h (check-gauge-integer '(method canvas% init-auto-scrollbars) h))
(check-fraction '(method canvas% init-auto-scrollbars) x)
(check-fraction '(method canvas% init-auto-scrollbars) y)
(let-values ([(cw ch) (get-client-size)])
(send wx set-scrollbars (if w 1 0) (if h 1 0)
(or w 0) (or h 0) 1 1
(if w (inexact->exact (floor (* x (max 0 (- w cw))))) 0)
(if h (inexact->exact (floor (* y (max 0 (- h ch))))) 0)
#t)))]
[init-manual-scrollbars
(lambda (x-len y-len x-page y-page x-val y-val)
(let ([who '(method canvas% init-auto-scrollbars)])
(when x-len (check-range-integer who x-len))
(when y-len (check-range-integer who y-len))
(check-gauge-integer who x-page)
(check-gauge-integer who y-page)
(check-range-integer who x-val)
(check-range-integer who y-val)
(when (and x-len (< x-len x-val))
(raise-mismatch-error (who->name who)
(format "horizontal value: ~e larger than the horizontal range: "
x-val)
x-len))
(when (and y-len (< y-len y-val))
(raise-mismatch-error (who->name who)
(format "vertical value: ~e larger than the vertical range: "
y-val)
y-len)))
(send wx set-scrollbars (if x-len 1 0) (if y-len 1 0)
(or x-len 0) (or y-len 0) x-page y-page x-val y-val #f))]
[get-scroll-pos (entry-point (lambda (d) (send wx get-scroll-pos d)))]
[set-scroll-pos (entry-point (lambda (d v) (send wx set-scroll-pos d v)))]
[get-scroll-range (entry-point (lambda (d) (send wx get-scroll-range d)))]
[set-scroll-range (entry-point (lambda (d v) (send wx set-scroll-range d v)))]
[get-scroll-page (entry-point (lambda (d) (send wx get-scroll-page d)))]
[set-scroll-page (entry-point (lambda (d v) (send wx set-scroll-page d v)))])
(override
[on-paint (lambda ()
(if (eq? paint-cb default-paint-cb)
(super-on-paint)
(paint-cb this (get-dc))))])
(private-field
[wx #f])
(sequence
(super-init (lambda ()
(let ([ds (+ (cond
[(memq 'control-border style) (+ 4 canvas-control-border-extra)]
[(memq 'border style) 4]
[else 0])
(if (or (memq 'vscroll style) (memq 'hscroll style))
canvas-default-size
1))])
(set! wx (make-object wx-canvas% this this
(mred->wx-container parent)
-1 -1 ds ds
style)))
wx)
(lambda ()
(let ([cwho '(constructor canvas)])
(check-container-ready cwho parent)))
parent)
(when label
(set-label label))
(send parent after-new-child this))))
(define editor-canvas%
(class100*/kw basic-canvas% ()
[(parent [editor #f] [style null] [scrolls-per-page 100] [label #f]
[wheel-step no-val] [line-count no-val]
[horizontal-inset 5] [vertical-inset 5])
canvas%-keywords]
(sequence
(let ([cwho '(constructor editor-canvas)])
(check-container-parent cwho parent)
(check-instance cwho internal-editor<%> "text% or pasteboard%" #t editor)
(check-style cwho #f '(hide-vscroll hide-hscroll no-vscroll no-hscroll deleted control-border) style)
(check-gauge-integer cwho scrolls-per-page)
(check-label-string/false cwho label)
(unless (eq? wheel-step no-val)
(check-wheel-step cwho wheel-step))
(unless (or (not line-count) (eq? line-count no-val))
((check-bounded-integer 1 1000 #t) cwho line-count))
(unless (eq? horizontal-inset 5)
(check-margin-integer cwho horizontal-inset))
(unless (eq? vertical-inset 5)
(check-margin-integer cwho vertical-inset))))
(inherit set-label)
(private-field
[force-focus? #f]
[scroll-to-last? #f]
[scroll-bottom? #f])
(public
[call-as-primary-owner (lambda (f) (send wx call-as-primary-owner f))]
[allow-scroll-to-last
(entry-point
(case-lambda
[() scroll-to-last?]
[(on?) (set! scroll-to-last? (and on? #t))
(send wx allow-scroll-to-last on?)]))]
[scroll-with-bottom-base
(entry-point
(case-lambda
[() scroll-bottom?]
[(on?) (set! scroll-bottom? (and on? #t))
(send wx scroll-with-bottom-base on?)]))]
[lazy-refresh
(entry-point
(case-lambda
[() (send wx get-lazy-refresh)]
[(on?) (send wx set-lazy-refresh on?)]))]
[force-display-focus
(entry-point
(case-lambda
[() force-focus?]
[(on?) (set! force-focus? (and on? #t))
(send wx force-display-focus on?)]))]
[allow-tab-exit (entry-point
(case-lambda
[() (send wx is-tabable?)]
[(on?) (send wx set-tabable (and on? #t))]))]
[set-line-count
(entry-point
(lambda (n)
((check-bounded-integer 1 1000 #t) '(method editor-canvas% set-line-count) n)
(send wx set-line-count n)))]
[get-line-count
(entry-point
(lambda ()
(send wx get-line-count)))]
[scroll-to (case-lambda
[(x y w h refresh?) (send wx scroll-to x y w h refresh?)]
[(x y w h refresh? bias) (send wx scroll-to x y w h refresh? bias)])]
[get-editor (entry-point (lambda () (send wx get-editor)))]
[set-editor (entry-point
(case-lambda
[(m) (send wx set-editor m)]
[(m upd?) (send wx set-editor m upd?)]))]
[(ws wheel-step)
(case-lambda
[() (let ([v (send wx get-wheel-step)])
(if (zero? v) #f v))]
[(wheel-step)
(check-wheel-step '(method editor-canvas% wheel-step) wheel-step)
(send wx set-wheel-step (or wheel-step 0))])]
[(vi vertical-inset)
(entry-point
(case-lambda
[() (send wx get-y-margin)]
[(m)
(check-margin-integer '(method editor-canvas% vertical-inset) m)
(as-exit (lambda () (send wx set-y-margin m)))]))]
[(hi horizontal-inset)
(entry-point
(case-lambda
[() (send wx get-x-margin)]
[(m)
(check-margin-integer '(method editor-canvas% horizontal-inset) m)
(as-exit (lambda () (send wx set-x-margin m)))]))])
(private-field
[wx #f])
(sequence
(super-init (lambda ()
(let* ([no-h? (or (memq 'no-vscroll style)
(memq 'hide-vscroll style))]
[no-v? (or (memq 'no-hscroll style)
(memq 'hide-hscroll style))]
[get-ds (lambda (no-this? no-other?)
(+ (if (memq 'control-border style)
canvas-control-border-extra
0)
(cond
[(and no-this? no-other?) 14]
[no-this? canvas-default-size]
[else (+ canvas-scroll-size canvas-default-size)])))])
(set! wx (make-object wx-editor-canvas% this this
(mred->wx-container parent) -1 -1
(get-ds no-h? no-v?)
(get-ds no-v? no-h?)
#f style scrolls-per-page #f))
wx))
(lambda ()
(let ([cwho '(constructor editor-canvas)])
(check-container-ready cwho parent)))
parent)
(unless (eq? wheel-step no-val)
(ws wheel-step))
(when label
(set-label label))
(when editor
(set-editor editor))
(send parent after-new-child this)
(unless (or (not line-count) (eq? line-count no-val))
(set-line-count line-count))
(unless (or (eq? vertical-inset 5))
(vi vertical-inset))
(unless (or (eq? horizontal-inset 5))
(hi horizontal-inset)))))
;-------------------- Final panel interfaces and class constructions --------------------
(define-keywords pane%-keywords
subarea%-keywords
container%-keywords
area%-keywords)
(define pane%
(class100*/kw (make-subarea% (make-container% area%)) ()
[(parent) pane%-keywords]
(private-field [wx #f])
(sequence
(let* ([who (cond ; yuck! - we do this to make h-p and v-p subclasses of p
[(is-a? this vertical-pane%) 'vertical-pane]
[(is-a? this horizontal-pane%) 'horizontal-pane]
[(is-a? this grow-box-spacer-pane%) 'grow-box-spacer-pane]
[else 'pane])]
[cwho `(constructor ,who)])
(check-container-parent cwho parent)
(as-entry
(lambda ()
(super-init (lambda () (set! wx (make-object (case who
[(vertical-pane) wx-vertical-pane%]
[(horizontal-pane) wx-horizontal-pane%]
[(grow-box-spacer-pane) wx-grow-box-pane%]
[else wx-pane%])
this this (mred->wx-container parent) null)) wx)
(lambda () wx)
(lambda ()
(check-container-ready cwho parent))
parent)
(send (send wx area-parent) add-child wx)))
(send parent after-new-child this)))))
(define vertical-pane% (class100*/kw pane% () [(parent) pane%-keywords] (sequence (super-init parent))))
(define horizontal-pane% (class100*/kw pane% () [(parent) pane%-keywords] (sequence (super-init parent))))
(define grow-box-spacer-pane% (class100*/kw pane% () [(parent) pane%-keywords] (sequence (super-init parent))))
(define-keywords panel%-keywords
window%-keywords
subarea%-keywords
container%-keywords
area%-keywords)
(define panel%
(class100*/kw (make-area-container-window% (make-window% #f (make-subarea% (make-container% area%)))) (subwindow<%>)
[(parent [style null]) panel%-keywords]
(private-field [wx #f])
(sequence
(let* ([who (cond ; yuck! - we do this to make h-p and v-p subclasses of p
[(is-a? this tab-panel%) 'tab-panel]
[(is-a? this group-box-panel%) 'group-box-panel]
[(is-a? this vertical-panel%) 'vertical-panel]
[(is-a? this horizontal-panel%) 'horizontal-panel]
[else 'panel])]
[cwho `(constructor ,who)])
(check-container-parent cwho parent)
(check-style cwho #f '(border deleted) style)
(as-entry
(lambda ()
(super-init (lambda () (set! wx (make-object (case who
[(vertical-panel tab-panel group-box-panel) wx-vertical-panel%]
[(horizontal-panel) wx-horizontal-panel%]
[else wx-panel%])
this this (mred->wx-container parent) style)) wx)
(lambda () wx)
(lambda () (check-container-ready cwho parent))
#f parent #f)
(unless (memq 'deleted style)
(send (send wx area-parent) add-child wx))))
(send parent after-new-child this)))))
(define vertical-panel% (class100*/kw panel% () [(parent [style null]) panel%-keywords] (sequence (super-init parent style))))
(define horizontal-panel% (class100*/kw panel% () [(parent [style null]) panel%-keywords] (sequence (super-init parent style))))
(define list-append append)
(define tab-panel%
(class100*/kw vertical-panel% ()
[(choices parent callback [style null]) panel%-keywords]
(sequence
(let ([cwho '(constructor tab-panel)])
(unless (and (list? choices) (andmap label-string? choices))
(raise-type-error (who->name cwho) "list of strings (up to 200 characters)" choices))
(check-callback cwho callback)
(check-container-parent cwho parent)
(check-style cwho #f '(deleted no-border) style))
(super-init parent (if (memq 'deleted style)
'(deleted)
null)))
(private-field
[tabs (make-object tab-group% #f choices this (lambda (c e) (callback this e))
(if (memq 'no-border style)
null
'(border)))])
(sequence
(send (mred->wx this) set-first-child-is-hidden))
(private-field
[save-choices (map string->immutable-string choices)]
[hidden-tabs? #f])
(public
[get-number (lambda () (length save-choices))]
[append (entry-point
(lambda (n)
(check-label-string '(method tab-panel% append) n)
(let ([n (string->immutable-string n)])
(set! save-choices (list-append save-choices (list n)))
(as-exit (lambda () (send (mred->wx tabs) append n))))))]
[get-selection (lambda () (and (pair? save-choices)
(send (mred->wx tabs) get-selection)))]
[set-selection (entry-point
(lambda (i)
(check-item 'set-selection i)
(as-exit (lambda () (send (mred->wx tabs) set-selection i)))))]
[delete (entry-point
(lambda (i)
(check-item 'delete i)
(set! save-choices (let loop ([p 0][l save-choices])
(if (= p i)
(cdr l)
(cons (car l) (loop (add1 p) (cdr l))))))
(as-exit (lambda () (send (mred->wx tabs) delete i)))))])
(private
[check-item
(lambda (method n)
(check-non-negative-integer `(method tab-panel% ,method) n)
(let ([m (length save-choices)])
(unless (< n m)
(raise-mismatch-error (who->name `(method tab-panel% ,method))
(if (zero? m)
"panel has no tabs; given index: "
(format "panel has only ~a tabls, indexed 0 to ~a; given out-of-range index: "
m (sub1 m)))
n))))])))
(define group-box-panel%
(class100*/kw vertical-panel% ()
[(label parent [style null]) panel%-keywords]
(sequence
(let ([cwho '(constructor group-box-panel)])
(check-label-string cwho label)
(check-container-parent cwho parent)
(check-style cwho #f '(deleted) style))
;; Technically a bad way to change margin defaults, since it's
;; implemented with an update after creation:
(when (eq? horiz-margin no-val) (set! horiz-margin 2))
(when (eq? vert-margin no-val) (set! vert-margin 2))
(super-init parent (if (memq 'deleted style)
'(deleted)
null)))
(private-field
[gbox (make-object group-box% label this null)]
[lbl label])
(sequence
(send (mred->wx this) set-first-child-is-hidden))
(override
[set-label (entry-point
(lambda (s)
(check-label-string '(method group-box-panel% set-label) s)
(set! lbl (if (immutable? s)
s
(string->immutable-string s)))
(send gbox set-label s)))]
[get-label (lambda () lbl)])))
;;;;;;;;;;;;;;;;;;;;;; Menu classes ;;;;;;;;;;;;;;;;;;;;;;
(define (find-pos l i eq?)
(let loop ([l l][n 0])
(cond
[(null? l) #f]
[(eq? (car l) i) n]
[else (loop (cdr l) (add1 n))])))
(define (menu-parent-only who p)
(unless (is-a? p internal-menu<%>)
(raise-type-error (constructor-name who) "parent menu% or popup-menu% object" p)))
(define (menu-or-bar-parent who p)
(unless (or (is-a? p internal-menu<%>) (is-a? p menu-bar%))
(raise-type-error (constructor-name who) "built-in menu-item-container<%> object" p)))
(define wx-menu-item%
(class100* wx:menu-item% (wx<%>) (mr mn-dat)
(private-field
[menu-data mn-dat]
[mred mr]
[keymap #f]
[wx-menu #f]
[enabled? #t])
(public
[get-keymap (lambda () keymap)]
[set-keymap (lambda (k) (set! keymap k))]
[swap-keymap (lambda (parent k)
(send (send (mred->wx parent) get-container) swap-item-keymap keymap k)
(set-keymap k))]
[get-mred (lambda () mred)]
[get-menu-data (lambda () menu-data)] ; for meta-shortcuts
[get-container (lambda () wx-menu)]
[set-wx-menu (lambda (wx) (set! wx-menu wx))]
[is-enabled? (lambda () enabled?)]
[set-enabled (lambda (on?) (set! enabled? on?))])
(sequence
(super-init))))
(define wx-menu-bar%
(class100* wx:menu-bar% (wx<%>) (mr)
(inherit delete)
(rename [super-append append]
[super-enable-top enable-top])
(private-field
[mred mr]
[items null]
[disabled null]
[disabled? #f]
[keymap (make-object wx:keymap%)])
(public
[get-container (lambda () this)]
[handle-key (lambda (event)
(as-exit
(lambda ()
(or (send keymap handle-key-event this event)
(and (wx:shortcut-visible-in-label? #t)
(send event get-meta-down)
(char? (send event get-key-code))
(let ([c (send event get-key-code)])
(and (or (char-alphabetic? c)
(char-numeric? c))
(let ([re (key-regexp c)])
(ormap
(lambda (i)
(let* ([data (send (mred->wx i) get-menu-data)]
[label (car data)]
[menu (cdr data)])
(if (regexp-match re label)
(begin
(send menu select)
#t)
#f)))
items)))))))))]
[on-demand (lambda () (as-exit (lambda () (send mred on-demand))))]
[get-mred (lambda () mred)]
[get-items (lambda () items)]
[append-item (lambda (item menu title)
(super-append menu title)
(when disabled?
(super-enable-top (length items) #f))
(set! items (append items (list item)))
(send keymap chain-to-keymap (send (mred->wx item) get-keymap) #f))]
[all-enabled? (lambda () (not disabled?))]
[enable-all (lambda (on?)
(set! disabled? (not on?))
(let loop ([n (sub1 (length items))])
(unless (negative? n)
(if on?
(unless (memq (list-ref items n) disabled)
(super-enable-top n #t))
(super-enable-top n #f))
(loop (sub1 n)))))]
[delete-item (lambda (i)
(let ([p (position-of i)])
(set! items (remq i items))
(set! disabled (remq i disabled))
(delete #f p)
(send keymap remove-chained-keymap (send (mred->wx i) get-keymap))))]
[position-of (lambda (i) (find-pos items i eq?))])
(override
[enable-top (lambda (p on?)
(let ([i (list-ref items p)])
(if on?
(when (memq i disabled)
(set! disabled (remq i disabled))
(unless disabled?
(super-enable-top p #t)))
(unless (memq i disabled)
(set! disabled (cons i disabled))
(super-enable-top p #f)))))])
(sequence
(super-init))))
(define wx-menu%
(class100* wx:menu% (wx<%>) (mr popup-label popup-callback)
(private-field
[mred mr]
[items null]
[keymap (make-object wx:keymap%)]
[popup-grabber #f])
(inherit delete-by-position)
(rename [super-delete delete]
[super-enable enable])
(public
[get-container (lambda () this)]
[get-keymap (lambda () keymap)]
[get-mred (lambda () mred)]
[get-items (lambda () items)]
[append-item (lambda (i iwx)
(set! items (append items (list i)))
(unless (or (is-a? i separator-menu-item%)
(not (send iwx is-enabled?)))
(let ([k (send iwx get-keymap)])
(when k
(send keymap chain-to-keymap k #f)))))]
[delete-sep (lambda (i iwx)
(delete-by-position (find-pos items i eq?))
(set! items (remq i items)))]
[swap-item-keymap (lambda (old-k new-k)
(when old-k (send keymap remove-chained-keymap old-k))
(when new-k (send keymap chain-to-keymap new-k #f)))]
[popup-grab (lambda (c)
(if popup-grabber
#f
(begin
(set! popup-grabber c)
#t)))]
[popup-release (lambda () (set! popup-grabber #f))]
[get-popup-grabber (lambda () popup-grabber)])
(override
[delete (lambda (id i)
(super-delete id)
(set! items (remq i items))
(let ([k (send (mred->wx i) get-keymap)])
(when k
(send keymap remove-chained-keymap k))))]
[enable (lambda (iwx id on?)
; Only called if the item is not deleted
(unless (eq? (send iwx is-enabled?) (and on? #t))
(send iwx set-enabled (and on? #t))
(super-enable id on?)))])
(sequence
(super-init popup-label popup-callback))))
;; Most of the work is in the item. Anything that appears in a menubar or
;; menu has an item. Submenus are created as instances of menu%, but
;; menu% has a get-item method for manipulating the menu w.r.t. the parent
;; (e.g., changing the title or enabled state). A popup menu, created
;; as an instance of popup-menu%, has no item.
;;
;; A menu bar is created as a menu-bar%, given a frame as its parent. The
;; frame must not already have a menu bar.
;;
;; Plain labeled items are created as instances of menu-item% or
;; checkable-menu-item%. The parent must be a menu-item-container<%>,
;; which is a menu%, popup-menu%, or menu-bar%
(define menu-item<%>
(interface ()
get-parent
delete restore is-deleted?))
(define labelled-menu-item<%>
(interface (menu-item<%>)
get-label set-label get-plain-label
get-help-string set-help-string
enable is-enabled?
on-demand))
(define submenu-item<%>
(interface (labelled-menu-item<%>) get-menu))
(define separator-menu-item%
(class100* mred% (menu-item<%>) (parent)
(sequence (menu-parent-only 'separator-menu-item parent))
(private-field
[prnt parent]
[wx #f]
[shown? #f]
[wx-parent #f])
(public
[get-parent (lambda () prnt)]
[restore (entry-point
(lambda ()
(unless shown?
(send wx-parent append-separator)
(send wx-parent append-item this wx)
(set! shown? #t))))]
[delete (entry-point
(lambda ()
(when shown?
(send wx-parent delete-sep this wx)
(set! shown? #f))))]
[is-deleted? (lambda () (not shown?))])
(sequence
(as-entry
(lambda ()
(set! wx (make-object wx-menu-item% this #f))
(set! wx-parent (send (mred->wx prnt) get-container))
(super-init wx)))
(restore))))
(define (strip-tab s) (car (regexp-match (format "^[^~a]*" #\tab) s)))
(define basic-labelled-menu-item%
(class100* mred% (labelled-menu-item<%>) (prnt lbl help-str wx-sub chkble? keymap set-wx demand-callback)
(private-field
[parent prnt]
[label lbl]
[help-string help-str]
[wx-submenu wx-sub]
[checkable? chkble?]
[callback demand-callback]
[wx #f]
[wx-parent #f]
[plain-label (string->immutable-string (wx:label->plain-label label))]
[in-menu? (is-a? parent internal-menu<%>)]
[shown? #f]
[enabled? #t])
(private
[do-enable (lambda (on?)
(when shown?
(if in-menu?
(send wx-parent enable wx (send wx id) on?)
(send wx-parent enable-top (send wx-parent position-of this) on?)))
(set! enabled? (and on? #t)))])
(public
[on-demand (lambda () (callback this))]
[get-parent (lambda () parent)]
[get-label (lambda () label)]
[set-label (entry-point
(lambda (l)
(check-label-string '(method labelled-menu-item<%> set-label) l)
(set! label (string->immutable-string l))
(set-car! (send wx get-menu-data) l) ; for meta-shortcuts
(set! plain-label (string->immutable-string (wx:label->plain-label l)))
(when shown?
(if in-menu?
(send wx-parent set-label (send wx id) l)
(send wx-parent set-label-top (send wx-parent position-of this) label)))))]
[get-plain-label (lambda () plain-label)]
[get-help-string (lambda () help-string)]
[set-help-string (entry-point
(lambda (s)
(check-label-string/false '(method labelled-menu-item<%> set-help-string) s)
(set! help-string (and s (string->immutable-string s)))
(when in-menu?
(send wx-parent set-help-string (send wx id) help-string))))]
[enable (lambda (on?) (do-enable on?))]
[is-enabled? (lambda () enabled?)]
[restore (entry-point
(lambda ()
(unless shown?
(if in-menu?
(begin
(if wx-submenu
(send wx-parent append (send wx id) label wx-submenu help-string)
(send wx-parent append (send wx id) label help-string checkable?))
(send wx-parent append-item this wx))
(send wx-parent append-item this wx-submenu label))
(set! shown? #t)
(do-enable enabled?))))]
[delete (entry-point
(lambda ()
(when shown?
(if in-menu?
(send wx-parent delete (send wx id) this)
(send wx-parent delete-item this))
(set! shown? #f))))]
[is-deleted? (lambda () (not shown?))])
(sequence
(as-entry
(lambda ()
(when help-string
(set! help-string (string->immutable-string help-string)))
(set! wx (set-wx (make-object wx-menu-item% this (cons label #f))))
(set! wx-parent (send (mred->wx parent) get-container))
(super-init wx)
(when keymap (send wx set-keymap keymap))))
(restore))))
(define selectable-menu-item<%>
(interface (labelled-menu-item<%>)
command
get-shortcut set-shortcut
get-x-shortcut-prefix set-x-shortcut-prefix))
(define (char-name c print?)
(case c
[(#\return) (if (eq? (system-type) 'macos) "Return" "Enter")]
[(#\tab) "Tab"]
[(#\space) "Space"]
[(#\backspace) "Backspace"]
[(#\rubout) "Delete"]
[(#\:) (if print? ":" "Colon")]
[(#\;) (if print? ";" "Semicolon")]
[else c]))
(define basic-selectable-menu-item%
(class100* basic-labelled-menu-item% (selectable-menu-item<%>) (lbl checkable? mnu cb shrtcut help-string set-wx demand-callback)
(inherit is-enabled?)
(rename [super-restore restore] [super-set-label set-label]
[super-is-deleted? is-deleted?]
[super-is-enabled? is-enabled?]
[super-get-label get-label])
(private-field
[menu mnu]
[callback cb]
[label lbl]
[shortcut shrtcut]
[wx #f])
(public
[command (lambda (e)
(check-instance '(method selectable-menu-item<%> command) wx:control-event% 'control-event% #f e)
(void (callback this e)))])
(private-field
[x-prefix 'meta])
(private
[calc-labels (lambda (label)
(let* ([new-label (if shortcut
(string-append
(strip-tab label)
(case (system-type)
[(unix) (format "~a~a~a" #\tab
(case x-prefix
[(meta) "Meta+"]
[(alt) "Alt+"]
[(ctl-m) "Ctl+M "]
[(ctl) "Ctl+"])
(char-name
(char-upcase shortcut)
#t))]
[(windows) (format "~aCtl+~a" #\tab
(char-name (char-upcase shortcut) #t))]
[(macos macosx) (format "~aCmd+~a" #\tab
(char-name (char-upcase shortcut) #t))]))
(strip-tab label))]
[key-binding (and shortcut
(case (system-type)
[(unix) (format "~a~a"
(case x-prefix
[(meta) ":m:"]
[(alt) ":a:"]
[(ctl-m) ":c:m;:"]
[(ctl) ":c:"])
(char-name (char-downcase shortcut) #f))]
[(windows) (format ":c:~a" (char-name (char-downcase shortcut) #f))]
[(macos macosx) (format ":d:~a" (char-name (char-downcase shortcut) #f))]))]
[keymap (and key-binding
(let ([keymap (make-object wx:keymap%)])
(send keymap add-function "menu-item"
;; keymap function callback already in exit mode:
(lambda (edit event)
(when (is-enabled?)
(callback this (make-object wx:control-event% 'menu)))))
(send keymap map-function key-binding "menu-item")
keymap))])
(values new-label keymap)))])
(private
[do-set-label (entry-point
(lambda (l)
(check-label-string '(method labelled-menu-item<%> set-label) l)
(let-values ([(new-label keymap) (calc-labels l)])
(set! label (string->immutable-string l))
(super-set-label new-label)
(if (or (super-is-deleted?)
(not (super-is-enabled?)))
(send wx set-keymap keymap)
(send wx swap-keymap menu keymap)))))])
(override
[get-label (lambda () label)]
[set-label (lambda (s) (do-set-label s))])
(public
[set-shortcut (lambda (c)
(check-char/false '(method selectable-menu-item<%> set-shortcut) c)
(set! shortcut c) (do-set-label (super-get-label)))]
[get-shortcut (lambda () shortcut)]
[get-x-shortcut-prefix (lambda () x-prefix)]
[set-x-shortcut-prefix (lambda (p)
(unless (memq p '(meta alt ctl-m ctl))
(raise-type-error (who->name '(method selectable-menu-item<%> set-x-shortcut-prefix))
"symbol: meta, alt, ctl-m, or ctl" p))
(set! x-prefix p) (do-set-label (super-get-label)))])
(sequence
(set! label (string->immutable-string label))
(let-values ([(new-label keymap) (calc-labels label)])
(super-init menu new-label help-string #f checkable? keymap (lambda (x) (set! wx x) (set-wx x)) demand-callback)))))
(define (check-shortcut-args who label menu callback shortcut help-string demand-callback)
(let ([cwho `(constructor ,who)])
(check-label-string cwho label)
(menu-parent-only who menu)
(check-callback cwho callback)
(check-char/false cwho shortcut)
(check-label-string/false cwho help-string)
(check-callback1 cwho demand-callback)))
(define menu-item%
(class100 basic-selectable-menu-item% (label parent callback [shortcut #f] [help-string #f] [demand-callback void])
(sequence
(check-shortcut-args 'menu-item label parent callback shortcut help-string demand-callback)
(super-init label #f parent callback shortcut help-string (lambda (x) x) demand-callback))))
(define checkable-menu-item%
(class100 basic-selectable-menu-item% (label parent callback [shortcut #f] [help-string #f] [demand-callback void] [checked #f])
(sequence
(check-shortcut-args 'checkable-menu-item label parent callback shortcut help-string demand-callback))
(private-field
[mnu parent]
[wx #f])
(public
[check (entry-point (lambda (on?) (send (send (mred->wx mnu) get-container) check (send wx id) on?)))]
[is-checked? (entry-point (lambda () (send (send (mred->wx mnu) get-container) checked? (send wx id))))])
(sequence
(super-init label #t mnu callback shortcut help-string (lambda (x) (set! wx x) x) demand-callback)
(when checked (check #t)))))
(define menu-item-container<%> (interface () get-items on-demand))
(define internal-menu<%> (interface ()))
(define menu%
(class100* basic-labelled-menu-item% (menu-item-container<%> internal-menu<%>) (label parent [help-string #f] [demand-callback void])
(private-field
[callback demand-callback])
(sequence
(check-label-string '(constructor menu) label)
(menu-or-bar-parent 'menu parent)
(check-label-string/false '(constructor menu) help-string)
(check-callback1 '(constructor menu) demand-callback))
(public
[get-items (entry-point (lambda () (send wx-menu get-items)))])
(override
[on-demand (lambda ()
(callback this)
(for-each
(lambda (i)
(when (is-a? i labelled-menu-item<%>)
(send i on-demand)))
(send wx-menu get-items)))])
(private-field
[wx-menu #f])
(sequence
(as-entry
(lambda ()
(set! wx-menu (make-object wx-menu% this #f void))
(super-init parent label help-string wx-menu #f (send wx-menu get-keymap) (lambda (x) x) void)
(let ([wx-item (mred->wx this)])
(set-cdr! (send wx-item get-menu-data) wx-menu) ; for meta-shortcuts
(send wx-item set-wx-menu wx-menu)))))))
(define popup-menu%
(class100* mred% (menu-item-container<%> internal-menu<%>) ([title #f][popdown-callback void][demand-callback void])
(private-field
[callback demand-callback])
(public
[get-popup-target
(lambda ()
(send wx get-popup-grabber))]
[get-items (entry-point (lambda () (send wx get-items)))]
[on-demand (lambda ()
(callback this)
(for-each
(lambda (i)
(when (is-a? i labelled-menu-item<%>)
(send i on-demand)))
(send wx get-items)))])
(private-field
[wx #f])
(sequence
(check-label-string/false '(constructor popup-menu) title)
(check-callback '(constructor popup-menu) popdown-callback)
(check-callback1 '(constructor popup-menu) demand-callback)
(as-entry
(lambda ()
(set! wx (make-object wx-menu% this title
(lambda (mwx e)
(let ([go
(lambda ()
(let ([wx (wx:id-to-menu-item (send e get-menu-id))])
(when wx
(send (wx->mred wx) command (make-object wx:control-event% 'menu)))
(dynamic-wind
void
(lambda ()
(popdown-callback this (make-object wx:control-event%
(if wx
'menu-popdown
'menu-popdown-none))))
(lambda () (send mwx popup-release)))))])
(if (eq? 'windows (system-type))
(wx:queue-callback go wx:middle-queue-key)
(go))))))
(super-init wx))))))
(define menu-bar%
(class100* mred% (menu-item-container<%>) (parent [demand-callback void])
(sequence
(unless (or (is-a? parent frame%) (eq? parent 'root))
(raise-type-error (constructor-name 'menu-bar) "frame% object or 'root" parent))
(check-callback1 '(constructor menu-bar) demand-callback)
(if (eq? parent 'root)
(unless (current-eventspace-has-menu-root?)
(raise-mismatch-error (constructor-name 'menu-bar) "no menu bar allowed in the current eventspace for: " parent))
(when (as-entry (lambda () (send (mred->wx parent) get-the-menu-bar)))
(raise-mismatch-error (constructor-name 'menu-bar) "the specified frame already has a menu bar: " parent))))
(private-field
[callback demand-callback]
[prnt (if (eq? parent 'root)
(let ([f (make-object (class frame%
(define/override (on-exit)
(exit))
(super-make-object "Root")))])
(as-entry
(lambda ()
(when root-menu-frame
(raise-mismatch-error (constructor-name 'menu-bar) "already has a menu bar: " parent))
(send (mred->wx f) designate-root-frame)
(set! root-menu-frame f)))
f)
parent)]
[wx #f]
[wx-parent #f]
[shown? #f])
(public
[get-frame (lambda () prnt)]
[get-items (entry-point (lambda () (send wx get-items)))]
[enable (entry-point (lambda (on?) (send wx enable-all on?)))]
[is-enabled? (entry-point (lambda () (send wx all-enabled?)))]
[on-demand (lambda ()
(callback this)
(for-each
(lambda (i) (send i on-demand))
(send wx get-items)))])
(sequence
(as-entry
(lambda ()
(set! wx (make-object wx-menu-bar% this))
(set! wx-parent (mred->wx prnt))
(super-init wx)
(send wx-parent set-menu-bar wx)
(send wx-parent self-redraw-request))))))
(wx:set-menu-tester (lambda (m) (is-a? m popup-menu%)))
;;;;;;;;;;;;;;;;;;;;;;;;;;; END SECURE LEVEL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Everything past this point is written at the user's level, so there
;; are no entry/edit operations.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Standard Key Bindings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define std-keymap (make-object wx:keymap%))
(let* ([k std-keymap]
[mouse-paste (lambda (edit event)
(when (send event button-down?)
(cond
[(is-a? edit wx:text%)
(let ([x-box (box (send event get-x))]
[y-box (box (send event get-y))]
[eol-box (box #f)])
(send edit global-to-local x-box y-box)
(let ([click-pos (send edit find-position
(unbox x-box)
(unbox y-box)
eol-box)])
(send edit set-position click-pos)))]
[else (void)])
(send edit paste)))]
[mouse-popup-menu (lambda (edit event)
(when (send event button-up?)
(let ([a (send edit get-admin)])
(when a
(let ([m (make-object popup-menu%)])
(append-editor-operation-menu-items m)
;; Remove shortcut indicators (because they might not be correct)
(for-each
(lambda (i)
(when (is-a? i selectable-menu-item<%>)
(send i set-shortcut #f)))
(send m get-items))
(let-values ([(x y) (send edit
dc-location-to-editor-location
(send event get-x)
(send event get-y))])
(send a popup-menu m (+ x 5) (+ y 5))))))))])
(wx:add-text-keymap-functions k)
(send k add-function "mouse-paste" mouse-paste)
(send k add-function "mouse-popup-menu" mouse-popup-menu)
(map
(lambda (key func) (send k map-function key func))
(append
(case (system-type)
[(windows) '(":c:c" ":c:x" ":c:v" ":c:k" ":c:z" ":c:a")]
[(macos macosx) '(":d:c" ":d:x" ":d:v" ":d:k" ":d:z" ":d:a")]
[(unix) '(":m:w" ":c:w" ":c:y" ":c:k" ":c:s:_" ":m:a")])
'(":middlebutton"))
'("copy-clipboard" "cut-clipboard" "paste-clipboard" "delete-to-end-of-line"
"undo" "select-all" "mouse-paste"))
(send k map-function ":rightbuttonseq" "mouse-popup-menu")
(when (eq? (system-type) 'unix)
(send k map-function ":c:a" "beginning-of-line")
(send k map-function ":c:e" "end-of-line")))
(define (check-installer who)
(lambda (p)
(unless (and (procedure? p)
(procedure-arity-includes? p 1))
(raise-type-error who
"procedure of arity 1"
p))
p))
(define current-text-keymap-initializer
(make-parameter (let ([default-text-keymap-initializer
(lambda (k)
(check-instance 'default-text-keymap-initializer wx:keymap% 'keymap% #f k)
;; Level of indirection to protect std-keymap:
(let ([naya (make-object wx:keymap%)])
(send naya chain-to-keymap std-keymap #f)
(send k chain-to-keymap naya #f)))])
default-text-keymap-initializer)
(check-installer 'default-text-keymap-initializer)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; REPL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (-graphical-read-eval-print-loop user-esp override-ports?)
;; The REPL buffer class
(define esq:text%
(class100 text% ()
(inherit insert last-position get-text erase change-style clear-undos)
(rename [super-on-char on-char])
(private-field [prompt-pos 0] [locked? #f])
(override
[can-insert? (lambda (start end) (and (>= start prompt-pos) (not locked?)))]
[can-delete? (lambda (start end) (and (>= start prompt-pos) (not locked?)))]
[on-char (lambda (c)
(super-on-char c)
(when (and (memq (send c get-key-code) '(#\return #\newline #\003))
(not locked?))
(set! locked? #t)
(evaluate (get-text prompt-pos (last-position)))))])
(public
[new-prompt (lambda ()
(output "> ")
(set! prompt-pos (last-position))
(set! locked? #f)
(clear-undos))]
[output (lambda (str)
(let ([l? locked?])
(set! locked? #f)
(insert str)
(set! locked? l?)))]
[reset (lambda ()
(set! locked? #f)
(set! prompt-pos 0)
(erase)
(new-prompt))])
(sequence
(super-init)
(let ([s (last-position)]
[m (regexp-match "^(.*), (Copyright.*)$" (banner))])
(insert (format "Welcome to ~a." (cadr m)))
(let ([e (last-position)])
(insert #\newline)
(change-style (send (make-object wx:style-delta% 'change-bold) set-delta-foreground "BLUE") s e))
(output (caddr m)))
(insert "This is a simple window for evaluating MrEd Scheme expressions.") (insert #\newline)
(let ([s (last-position)])
(insert "Quit now and run DrScheme to get a better window.")
(let ([e (last-position)])
(insert #\newline)
(change-style
(send (make-object wx:style-delta% 'change-italic) set-delta-foreground "RED")
s e)))
(insert "The current input port always returns eof.") (insert #\newline)
(new-prompt))))
;; GUI creation
(define frame (make-object (class100 frame% args
(inherit accept-drop-files)
(override
[on-close (lambda ()
(custodian-shutdown-all user-custodian)
(semaphore-post waiting))]
[on-drop-file (lambda (f) (evaluate (format "(load ~s)" f)))])
(sequence
(apply super-init args) (accept-drop-files #t)))
"MrEd REPL" #f 500 400))
(define repl-buffer (make-object esq:text%))
(define repl-display-canvas (make-object editor-canvas% frame))
(define esq-eventspace (wx:current-eventspace))
(define (queue-output proc)
(parameterize ((wx:current-eventspace esq-eventspace))
(wx:queue-callback proc #f)))
;; User space initialization
(define user-custodian (make-custodian))
(define user-output-port
(let ([lock (make-semaphore 1)])
(make-custom-output-port
#f ; always ready for a non-blocking write
(lambda (s start end flush?)
(queue-output (lambda () (send repl-buffer output (substring s start end))))
(- end start))
void ; no flush action
void))) ; no close action
(define user-eventspace
(or user-esp
(parameterize ((current-custodian user-custodian))
(wx:make-eventspace))))
;; Evaluation
(define (evaluate expr-str)
(parameterize ((wx:current-eventspace user-eventspace))
(wx:queue-callback
(lambda ()
(dynamic-wind
void
(lambda ()
(call-with-values
(lambda () (eval (read (open-input-string expr-str))))
(lambda results
(for-each
(lambda (v)
(parameterize ([current-output-port user-output-port])
(print v)
(newline)))
results))))
(lambda ()
(queue-output (lambda () (send repl-buffer new-prompt)))))))))
(define waiting (make-semaphore 0))
(let ([mb (make-object menu-bar% frame)])
(let ([m (make-object menu% "&File" mb)])
(make-object menu-item% "Load File..." m (lambda (i e) (let ([f (get-file #f frame)]) (and f (evaluate (format "(load ~s)" f))))))
(unless (current-eventspace-has-standard-menus?)
(make-object menu-item%
(if (eq? (system-type) 'windows)
"E&xit"
"&Quit")
m (lambda (i e) (send frame on-close) (send frame show #f)) #\q)))
(let ([m (make-object menu% "&Edit" mb)])
(append-editor-operation-menu-items m #f)))
;; Just a few extra key bindings:
((current-text-keymap-initializer) (send repl-buffer get-keymap))
(send repl-buffer auto-wrap #t)
;; Go
(when override-ports?
(parameterize ((wx:current-eventspace user-eventspace))
(wx:queue-callback
(lambda ()
(current-output-port user-output-port)
(current-error-port user-output-port)
(current-input-port (make-custom-input-port (lambda (s) eof) #f void)))
#t)))
(send repl-display-canvas set-editor repl-buffer)
(send frame show #t)
(send repl-display-canvas focus)
(wx:yield waiting))
(define graphical-read-eval-print-loop
(case-lambda
[() (-graphical-read-eval-print-loop #f #t)]
[(esp)
(graphical-read-eval-print-loop esp (not esp))]
[(esp override-ports?)
(unless (or (not esp) (wx:eventspace? esp))
(raise-type-error 'graphical-read-eval-print-loop "eventspace or #f" esp))
(-graphical-read-eval-print-loop esp override-ports?)]))
(define box-width 300)
(define (no-stretch a) (send a stretchable-width #f) (send a stretchable-height #f))
(define protect&
(let ([re (regexp "&")])
(lambda (s)
(regexp-replace* re s "\\&\\&"))))
(define message-box/custom
(opt-lambda (title message
button1
button2
button3
[parent #f]
[style '(no-default)]
[close-result #f])
(check-label-string 'message-box/custom title)
(check-string/false 'message-box/custom message)
(check-label-string-or-bitmap/false 'message-box/custom button1)
(check-label-string-or-bitmap/false 'message-box/custom button2)
(check-label-string-or-bitmap/false 'message-box/custom button3)
(check-top-level-parent/false 'message-box/custom parent)
(check-style 'message-box/custom
'(default=1 default=2 default=3 no-default)
'(disallow-close number-order caution stop)
style)
(let* ([strings (let loop ([s message])
(let ([m (regexp-match (let ([nl (string #\newline #\return)])
(format "([^~a]*)[~a](.*)" nl nl))
s)])
(if m
(cons (cadr m) (loop (caddr m)))
(list s))))]
[single? (and (< (length strings) 10)
(andmap (lambda (s) (< (string-length s) 60)) strings))]
[f (make-object (class100 dialog% ()
(public
[get-message
(lambda () message)])
(override
[can-close? (lambda ()
(if (memq 'disallow-close style)
(begin
(wx:bell)
#f)
#t))]
[on-subwindow-event
(lambda (w e)
(if (send e button-down?)
(if (is-a? w button%)
#f
(if (or single?
(not (is-a? w editor-canvas%))
(let-values ([(w h) (send w get-client-size)])
(< (send e get-x) w)))
(begin
(send w popup-menu
(let ([m (make-object popup-menu%)])
(make-object menu-item%
"Copy Message"
m
(lambda (i e)
(send (wx:get-the-clipboard)
set-clipboard-string
message
(send e get-time-stamp))))
m)
(send e get-x)
(send e get-y))
#t)
#f))
#f))])
(sequence
(super-init title parent box-width))))]
[result close-result]
[icon-id (cond
[(memq 'stop style) 'stop]
[(memq 'caution style) 'caution]
[else 'app])])
(let-values ([(msg-pnl btn-pnl extra-width btn-h-align msg-h-align msg-v-align)
(case (system-type)
[(macosx) (let ([p (make-object horizontal-pane% f)])
(send f min-width 300)
(send p set-alignment 'center 'top)
(let ([m (make-object message% icon-id p)])
(send m horiz-margin 16)
(send m vert-margin 16))
(let* ([rhs-pnl (make-object vertical-pane% p)]
[msg-pnl (make-object vertical-pane% rhs-pnl)]
[btn-pnl (make-object vertical-pane% rhs-pnl)])
(send msg-pnl vert-margin 16)
(when single?
(send msg-pnl horiz-margin 8))
(send btn-pnl vert-margin 8)
(send msg-pnl min-height 64)
(send btn-pnl stretchable-height #f)
(values msg-pnl btn-pnl 96 'right 'left 'top)))]
[else (let ([p (instantiate horizontal-pane% (f) [alignment '(center top)])])
(make-object message% icon-id p)
(values (make-object vertical-pane% p) f 0 'center 'center 'center))])])
(if single?
(begin
(send msg-pnl set-alignment (if (= (length strings) 1) msg-h-align 'left) msg-v-align)
(for-each (lambda (s) (make-object message% (protect& s) msg-pnl)) strings)
(send f stretchable-width #f)
(send f stretchable-height #f))
(let* ([e (make-object text%)]
[c (make-object editor-canvas% msg-pnl e '(no-hscroll))])
(send f resize (+ 400 extra-width) 200)
(send c set-line-count (min 5 (length strings)))
(send c allow-tab-exit #t)
(send f reflow-container)
(send e auto-wrap #t)
(send e insert message)
(send e set-position 0)
(send e hide-caret #t)
(send e set-cursor (make-object wx:cursor% 'arrow) #t)
(send e lock #t)))
(let* ([p (make-object horizontal-pane% btn-pnl)]
[mk-button (lambda (title v default?)
(let ([b (make-object button% title p (lambda (b e) (set! result v) (send f show #f))
(if default? '(border) null))])
(when default? (send b focus))))])
(send p set-alignment btn-h-align 'center)
(send p stretchable-height #f)
(send p stretchable-width #t) ; to get panel's centering
(let ([mk-1 (lambda ()
(when button1
(mk-button button1 1 (memq 'default=1 style))))]
[mk-2 (lambda ()
(when button2
(mk-button button2 2 (memq 'default=2 style))))]
[mk-3 (lambda ()
(when button3
(mk-button button3 3 (memq 'default=3 style))))])
(cond
[(or (memq 'number-order style)
(not (memq (system-type) '(macos macosx))))
(mk-1)
(mk-2)
(mk-3)]
[else
(mk-3)
(make-object horizontal-pane% p)
(mk-2)
(mk-1)])))
(send f center)
(send f show #t)
result))))
(define message-box
(case-lambda
[(title message) (message-box title message #f '(ok))]
[(title message parent) (message-box title message parent '(ok))]
[(title message parent style)
(check-label-string 'message-box title)
(check-string/false 'message-box message)
(check-top-level-parent/false 'message-box parent)
(check-style 'message-box '(ok ok-cancel yes-no) '(caution stop) style)
(let-values ([(one two one-v two-v close-val default)
(cond
[(memq 'ok style)
(values "OK" #f 'ok #f 1 'default=1)]
[(memq 'ok-cancel style)
(values "OK" "Cancel" 'ok 'cancel 2 'default=1)]
[(memq 'yes-no style)
(values "&Yes" "&No" 'yes 'no #f 'no-default)])])
(case (message-box/custom title message
one two #f
parent
(append
(cond
[(memq 'stop style) '(stop)]
[(memq 'caution style) '(caution)]
[else null])
(if close-val
(list default)
(list default 'disallow-close)))
close-val)
[(1) one-v]
[(2) two-v]))]))
(define (number->string* n)
(let ([s (number->string n)])
(regexp-replace "[.]([0-9][0-9][0-9])[0-9]*$"
s
".\\1")))
(define get-ps-setup-from-user
(case-lambda
[() (get-ps-setup-from-user #f #f #f null)]
[(message) (get-ps-setup-from-user message #f #f null)]
[(message parent) (get-ps-setup-from-user message parent #f null)]
[(message parent pss) (get-ps-setup-from-user message parent pss null)]
[(message parent pss-in style)
(define _
(begin
;; Calls from C++ have wrong kind of window:
(when (is-a? parent wx:window%)
(set! parent (as-entry (lambda () (wx->mred parent)))))
(check-label-string/false 'get-ps-setup-from-user message)
(check-top-level-parent/false 'get-ps-setup-from-user parent)
(check-instance 'get-ps-setup-from-user wx:ps-setup% 'ps-setup% #t pss-in)
(check-style 'get-ps-setup-from-user #f null style)))
(define pss (or pss-in (wx:current-ps-setup)))
(define f (make-object dialog% "PostScript Setup" parent))
(define papers
'("A4 210 x 297 mm" "A3 297 x 420 mm" "Letter 8 1/2 x 11 in" "Legal 8 1/2 x 14 in"))
(define p (make-object horizontal-pane% f))
(define paper (make-object choice% #f papers p void))
(define _0 (make-object vertical-pane% p))
(define cancel (make-object button% "Cancel" p (lambda (b e) (done #f))))
(define ok (make-object button% "OK" p (lambda (b e) (done #t)) '(border)))
(define unix? (eq? (system-type) 'unix))
(define dp (make-object horizontal-pane% f))
(define orientation (make-object radio-box% "Orientation:" '("Portrait" "Landscape") dp void))
(define destination (and unix? (make-object radio-box% "Destination:"
'("Printer" "Preview" "File") dp void)))
(define ssp (make-object horizontal-pane% f))
(define sp (make-object vertical-pane% ssp))
(define def-scale "0100.000")
(define def-offset "0000.000")
(define xscale (make-object text-field% "Horizontal Scale:" sp void def-scale))
(define xoffset (make-object text-field% "Horizontal Translation:" sp void def-offset))
(define sp2 (make-object vertical-pane% ssp))
(define yscale (make-object text-field% "Vertical Scale:" sp2 void def-scale))
(define yoffset (make-object text-field% "Vertical Translation:" sp2 void def-offset))
(define l2 (make-object check-box% "PostScript Level 2" f void))
(define cp (and unix? (make-object horizontal-pane% f)))
(define command (and unix? (make-object text-field% "Print Command:" cp void)))
(define vcommand (and unix? (make-object text-field% "Preview Command:" f void)))
(define ok? #f)
(define (done ?)
(send f show #f)
(set! ok? ?))
(define-values (xsb ysb xtb ytb) (values (box 0) (box 0) (box 0) (box 0)))
(send paper set-selection (or (find-pos papers (send pss get-paper-name) equal?) 0))
(send orientation set-selection (if (eq? (send pss get-orientation) 'landscape) 1 0))
(when unix?
(send destination set-selection (case (send pss get-mode)
[(printer) 0] [(preview) 1] [(file) 2]))
(send command set-value (send pss get-command))
(send vcommand set-value (send pss get-preview-command)))
(send sp set-alignment 'right 'top)
(send sp2 set-alignment 'right 'top)
(send pss get-scaling xsb ysb)
(send xscale set-value (number->string* (unbox xsb)))
(send yscale set-value (number->string* (unbox ysb)))
(send pss get-translation xtb ytb)
(send xoffset set-value (number->string* (unbox xtb)))
(send yoffset set-value (number->string* (unbox ytb)))
(send xscale stretchable-width #f)
(send yscale stretchable-width #f)
(send xoffset stretchable-width #f)
(send yoffset stretchable-width #f)
(send l2 set-value (send pss get-level-2))
(send f set-alignment 'center 'top)
(map no-stretch (list f xscale yscale xoffset yoffset dp))
(send f center)
(send f show #t)
(if ok?
(let ([s (make-object wx:ps-setup%)]
[gv (lambda (c b)
(or (string->number (send c get-value)) (unbox b)))])
(send s set-paper-name (send paper get-string-selection))
(send s set-orientation (if (positive? (send orientation get-selection))
'landscape
'portrait))
(when unix?
(send s set-mode (case (send destination get-selection)
[(0) 'printer]
[(1) 'preview]
[(2) 'file])))
(send s set-scaling (gv xscale xsb) (gv yscale ysb))
(send s set-translation (gv xoffset xtb) (gv yoffset ytb))
(send s set-level-2 (send l2 get-value))
(when (eq? (system-type) 'unix)
(send s set-command (send command get-value))
(send s set-preview-command (send vcommand get-value)))
s)
#f)]))
(define get-text-from-user
(case-lambda
[(title message) (get-text-from-user title message #f "" null)]
[(title message parent) (get-text-from-user title message parent "" null)]
[(title message parent init-val) (get-text-from-user title message parent init-val null)]
[(title message parent init-val style)
(check-label-string 'get-text-from-user title)
(check-label-string/false 'get-text-from-user message)
(check-top-level-parent/false 'get-text-from-user parent)
(check-string 'get-text-from-user init-val)
(check-style 'get-text-from-user #f '(password) style)
(let* ([f (make-object dialog% title parent box-width)]
[ok? #f]
[done (lambda (?) (lambda (b e) (set! ok? ?) (send f show #f)))])
(send f set-label-position 'vertical)
(let ([t (make-object text-field% message f (lambda (t e) (when (eq? (send e get-event-type) 'text-field-enter)
((done #t) #f #f)))
init-val (cons 'single style))]
[p (make-object horizontal-pane% f)])
(send p set-alignment 'right 'center)
(send f stretchable-height #f)
(make-object button% "Cancel" p (done #f))
(make-object button% "OK" p (done #t) '(border))
(send (send t get-editor) select-all)
(send t focus)
(send f center)
(send f show #t)
(and ok? (send t get-value))))]))
(define get-choices-from-user
(case-lambda
[(title message choices) (get-choices-from-user title message choices #f null '(single))]
[(title message choices parent) (get-choices-from-user title message choices parent null '(single))]
[(title message choices parent init-vals) (get-choices-from-user title message choices parent init-vals '(single))]
[(title message choices parent init-vals style)
(check-label-string 'get-choices-from-user title)
(check-label-string/false 'get-choices-from-user message)
(unless (and (list? choices) (andmap label-string? choices))
(raise-type-error 'get-choices-from-user "list of strings (up to 200 characters)" choices))
(check-top-level-parent/false 'get-choices-from-user parent)
(unless (and (list? init-vals) (andmap (lambda (x) (and (integer? x) (exact? x) (not (negative? x)))) init-vals))
(raise-type-error 'get-choices-from-user "list of exact non-negative integers" init-vals))
(check-style 'get-choices-from-user '(single multiple extended) null style)
(when (and (memq 'single style) (> (length init-vals) 1))
(raise-mismatch-error 'get-choices-from-user
(format "multiple initial-selection indices provided with ~e style: " 'single)
init-vals))
(let* ([f (make-object dialog% title parent box-width (min 300 (max 150 (* 14 (length choices)))))]
[ok-button #f]
[update-ok (lambda (l) (send ok-button enable (not (null? (send l get-selections)))))]
[ok? #f]
[done (lambda (?) (lambda (b e) (set! ok? ?) (send f show #f)))])
(send f set-label-position 'vertical)
(let ([l (make-object list-box% message choices f
(lambda (l e)
(update-ok l)
(when (eq? (send e get-event-type) 'list-box-dclick)
((done #t) #f #f)))
style)]
[p (make-object horizontal-pane% f)])
(for-each (lambda (i)
(when (>= i (send l get-number))
(raise-mismatch-error
'get-choices-from-user
(format "inital-selection list specifies an out-of-range index (~e choices provided): "
(send l get-number))
i))
(send l select i #t)) init-vals)
(send p set-alignment 'right 'center)
(send p stretchable-height #f)
(make-object button% "Cancel" p (done #f))
(set! ok-button (make-object button% "OK" p (done #t) '(border)))
(update-ok l)
(send f center)
(when (and (pair? init-vals)
((car init-vals) . > . 1))
;; Make sure initial selection is visible:
(send f reflow-container)
(send l set-first-visible-item (sub1 (car init-vals))))
(send f show #t)
(and ok? (send l get-selections))))]))
(define last-visted-directory #f)
(define (files->list s)
(let ([s (open-input-string s)])
(let loop ()
(let ([n (read s)])
(if (eof-object? n)
null
(begin
(read-char s) ; drop space
(cons (read-string n s)
(loop))))))))
(define (mk-file-selector who put? multi? dir? force-unix?)
(lambda (message parent directory filename extension style filters)
;; Calls from C++ have wrong kind of window:
(when (is-a? parent wx:window%)
(set! parent (as-entry (lambda () (wx->mred parent)))))
(check-label-string/false who message)
(check-top-level-parent/false who parent)
(check-string/false who directory) (check-string/false who filename) (check-string/false who extension)
(check-style who #f (cond
[put? null]
[dir? '(enter-packages)]
[else '(packages enter-packages)]) style)
(unless (and (list? filters)
(andmap (lambda (p)
(and (list? p)
(= (length p) 2)
(string? (car p))
(string? (cadr p))))
filters))
(raise-type-error who "list of 2-string lists" filters))
(if (not (or (eq? (system-type) 'unix)
force-unix?))
(let ([s (wx:file-selector message directory filename extension
;; file types:
(apply string-append
(map (lambda (s) (format "~a|~a|" (car s) (cadr s)))
filters))
;; style:
(cons
(cond
[dir? 'dir]
[put? 'put]
[multi? 'multi]
[else 'get])
style)
;; parent:
(and parent (mred->wx parent)))])
(if (and multi? s)
(files->list s)
s))
(letrec ([ok? #f]
[typed-name #f]
[dir (or directory last-visted-directory (current-directory))]
[f (make-object dialog% (if dir? "Select Directory" (if put? "Save" "Open")) parent 500 300)]
[__ (when message
(let ([p (make-object vertical-pane% f)])
(send p stretchable-height #f)
(make-object message% (protect& message) p)))]
[dir-pane (instantiate horizontal-pane% (f) (stretchable-height #f))]
[m (make-object message% (protect& dir) dir-pane)]
[lp (make-object horizontal-pane% f)]
[change-dir (lambda (d) (let ([sd (send d get-string-selection)])
(set! dir (simplify-path (build-path dir sd)))
(reset-directory)))]
[dirs (make-object (class list-box%
(rename [super-on-subwindow-char on-subwindow-char])
(define/override (on-subwindow-char w e)
(cond
[(and (send e get-meta-down)
(eq? (send e get-key-code) 'down))
(change-dir w)]
[(and (send e get-meta-down)
(eq? (send e get-key-code) 'up))
(send dirs set-selection 0)
(change-dir dirs)]
[else
(super-on-subwindow-char w e)]))
(super-instantiate ()))
#f null lp (lambda (d e)
(update-ok)
(when (eq? (send e get-event-type) 'list-box-dclick)
(change-dir d))))]
[files (make-object list-box% #f null lp (lambda (d e)
(update-ok)
(when (eq? (send e get-event-type) 'list-box-dclick)
(done)))
(if multi? '(multiple) '(single)))]
[do-text-name (lambda ()
(let ([v (send dir-text get-value)])
(if (or dir? (directory-exists? v))
(begin
(set! dir v)
(reset-directory))
;; Maybe specifies a file:
(let-values ([(super file)
(with-handlers ([void #f])
(let-values ([(base name dir?) (split-path v)])
(let ([super (and (not dir?)
(or (and (string? base)
(directory-exists? base)
base)
(and (eq? base 'relative)
(directory-exists? dir) dir)))])
(if super
(values super name)
(values #f #f)))))])
(if super
(begin
(set! dir super)
(set! typed-name file)
(done))
(begin
(set! dir v)
(reset-directory)))))))]
[dir-text (make-object text-field% #f f (lambda (t e)
(if (eq? (send e get-event-type) 'text-field-enter)
(do-text-name)
(begin
; typing in the box; disable the lists and enable ok
(send dirs enable #f)
(send files enable #f)
(when create-button
(send create-button enable #t))
(send ok-button enable #t)))))]
[bp (make-object horizontal-pane% f)]
[dot-check (make-object check-box% "Show files/directories that start with \".\"" bp (lambda (b e) (reset-directory)))]
[spacer (make-object vertical-pane% bp)]
[create-button (and dir? (make-object button% "Create" bp
(lambda (b e)
(with-handlers ([void
(lambda (exn)
(message-box "Error"
(exn-message exn)
f
'(ok stop)))])
(make-directory (send dir-text get-value))
(do-text-name)))))]
[cancel-button (make-object button% "Cancel" bp (lambda (b e) (set! ok? #f) (send f show #f)))]
[ok-button (make-object button%
(if dir? "Goto" "OK")
bp (lambda (b e)
(if (send (if dir? dirs files) is-enabled?)
;; normal mode
(if dir?
(change-dir dirs)
(done))
;; handle typed text
(do-text-name)))
'(border))]
[update-ok (lambda () (send ok-button enable (not (null? (send (if dir? dirs files) get-selections)))))]
[select-this-dir (and dir?
(make-object button% "<- &Select" dir-pane
(lambda (b e)
(send f show #f)
(done))))]
[reset-directory (lambda ()
(wx:begin-busy-cursor)
(let ([dir-exists? (directory-exists? dir)])
(send m set-label (if dir-exists?
(begin
(unless directory
(set! last-visted-directory dir))
(protect& dir))
(string-append "BAD DIRECTORY: " dir)))
(when select-this-dir
(send select-this-dir enable dir-exists?))
(when create-button
(send create-button enable (not dir-exists?))))
(send dir-text set-value dir)
(let ([l (with-handlers ([void (lambda (x) null)])
(directory-list dir))]
[dot? (send dot-check get-value)])
(let-values ([(ds fs)
(let loop ([l l][ds null][fs null])
(cond
[(null? l) (values (cons ".." (quicksort ds string-locale<?))
(quicksort fs string-locale<?))]
[(and (not dot?) (char=? (string-ref (car l) 0) #\.)) (loop (cdr l) ds fs)]
[(file-exists? (build-path dir (car l))) (loop (cdr l) ds (cons (car l) fs))]
[else (loop (cdr l) (cons (car l) ds) fs)]))])
(send dirs set ds)
(send files set fs)
(send dirs enable #t)
(unless dir?
(send files enable #t))
(update-ok)
(wx:end-busy-cursor))))]
[get-filename (lambda ()
(if dir?
dir
(let ([mk (lambda (f) (simplify-path (build-path dir f)))])
(let ([l (map mk (if typed-name
(list typed-name)
(map (lambda (p) (send (if dir? dirs files) get-string p))
(send (if dir? dirs files) get-selections))))])
(if multi? l (car l))))))]
[done (lambda ()
(let ([name (get-filename)])
(unless (and put? (file-exists? name)
(eq? (message-box "Warning" (format "Replace ~s?" name) f '(yes-no)) 'no)
(set! typed-name #f))
(set! ok? #t)
(send f show #f))))])
(send bp stretchable-height #f)
(send m stretchable-width #t)
(reset-directory)
(when filename
(let ([d (send dir-text get-value)])
(send dir-text set-value (build-path d filename))
(set! typed-name filename)
(send ok-button enable #t)))
(when put?
(send dir-text focus))
(when dir?
(send files enable #f))
(send f center)
(send f show #t)
(and ok? (get-filename))))))
; We duplicate the case-lambda for `get-file', `get-file-list', and `put-file' so that they have the
; right arities and names
(define default-filters '(("Any" "*.*")))
(define get-file
(case-lambda
[() (get-file #f #f #f #f #f null)]
[(message) (get-file message #f #f #f #f null)]
[(message parent) (get-file message parent #f #f #f null)]
[(message parent directory) (get-file message parent directory #f #f null)]
[(message parent directory filename) (get-file message parent directory filename #f null)]
[(message parent directory filename extension) (get-file message parent directory filename extension null)]
[(message parent directory filename extension style)
(get-file message parent directory filename extension style default-filters)]
[(message parent directory filename extension style filters)
((mk-file-selector 'get-file #f #f #f #f) message parent directory filename extension style filters)]))
(define get-file-list
(case-lambda
[() (get-file-list #f #f #f #f #f null)]
[(message) (get-file-list message #f #f #f #f null)]
[(message parent) (get-file-list message parent #f #f #f null)]
[(message parent directory) (get-file-list message parent directory #f #f null)]
[(message parent directory filename) (get-file-list message parent directory filename #f null)]
[(message parent directory filename extension) (get-file-list message parent directory filename extension null)]
[(message parent directory filename extension style)
(get-file-list message parent directory filename extension style default-filters)]
[(message parent directory filename extension style filters)
((mk-file-selector 'get-file-list #f #t #f #f) message parent directory filename extension style filters)]))
(define put-file
(case-lambda
[() (put-file #f #f #f #f #f null)]
[(message) (put-file message #f #f #f #f null)]
[(message parent) (put-file message parent #f #f #f null)]
[(message parent directory) (put-file message parent directory #f #f null)]
[(message parent directory filename) (put-file message parent directory filename #f null)]
[(message parent directory filename extension) (put-file message parent directory filename extension null)]
[(message parent directory filename extension style)
(put-file message parent directory filename extension style default-filters)]
[(message parent directory filename extension style filters)
((mk-file-selector 'put-file #t #f #f #f) message parent directory filename extension style filters)]))
(define get-directory
(case-lambda
[() (get-directory #f #f #f null)]
[(message) (get-directory message #f #f null)]
[(message parent) (get-directory message parent #f null)]
[(message parent directory) (get-directory message parent directory null)]
[(message parent directory style)
((mk-file-selector 'get-directory #f #f #t #f) message parent directory #f #f style null)]))
(define get-color-from-user
(case-lambda
[() (get-color-from-user #f #f #f null)]
[(message) (get-color-from-user message #f #f null)]
[(message parent) (get-color-from-user message parent #f null)]
[(message parent color) (get-color-from-user message parent color null)]
[(message parent color style)
(check-label-string/false 'get-color-from-user message)
(check-top-level-parent/false 'get-color-from-user parent)
(check-instance 'get-color-from-user wx:color% 'color% #t color)
(check-style 'get-color-from-user #f null style)
(if (not (eq? (system-type) 'unix))
(wx:get-color-from-user message (and parent (mred->wx parent)) color)
(letrec ([ok? #f]
[f (make-object dialog% "Choose Color" parent)]
[done (lambda (ok) (lambda (b e) (set! ok? ok) (send f show #f)))]
[canvas (make-object (class100 canvas% ()
(override
[on-paint (lambda () (repaint #f #f))])
(sequence (super-init f))))]
[p (make-object vertical-pane% f)]
[repaint (lambda (s e)
(let ([c (make-object wx:color%
(send red get-value)
(send green get-value)
(send blue get-value))])
(wx:fill-private-color (send canvas get-dc) c)))]
[make-color-slider (lambda (l) (make-object slider% l 0 255 p repaint))]
[red (make-color-slider "Red:")]
[green (make-color-slider "Green:")]
[blue (make-color-slider "Blue:")]
[bp (make-object horizontal-pane% f)])
(when color
(send red set-value (send color red))
(send green set-value (send color green))
(send blue set-value (send color blue)))
(make-object button% "Cancel" bp (done #f))
(send (make-object button% "OK" bp (done #t) '(border)) focus)
(send bp set-alignment 'right 'center)
(send p set-alignment 'right 'center)
(send p stretchable-height #f)
(send canvas min-height 50)
(send f center)
(send f show #t)
(and ok?
(make-object wx:color%
(send red get-value)
(send green get-value)
(send blue get-value)))))]))
(define get-font-from-user
(case-lambda
[() (get-font-from-user #f #f #f null)]
[(message) (get-font-from-user message #f #f null)]
[(message parent) (get-font-from-user message parent #f null)]
[(message parent font) (get-font-from-user message parent font null)]
[(message parent font style)
(check-label-string/false 'get-font-from-user message)
(check-top-level-parent/false 'get-font-from-user parent)
(check-instance 'get-font-from-user wx:font% 'font% #t font)
(check-style 'get-font-from-user #f null style)
(letrec ([ok? #f]
[f (make-object dialog% "Choose Font" parent 500 300)]
[refresh-sample (lambda (b e) (let ([f (get-font)])
(send ok-button enable f)
(when f
(let ([s (send (send edit get-style-list) find-named-style "Standard")])
(send s set-delta (font->delta f))))))]
[p (make-object horizontal-pane% f)]
[face (make-object list-box% #f
(let ([l (wx:get-face-list)])
(if (memq (system-type) '(macos macosx))
(quicksort l (lambda (a b)
(cond
[(eq? (char-alphabetic? (string-ref a 0))
(char-alphabetic? (string-ref b 0)))
(string-locale<? a b)]
[else (char-alphabetic? (string-ref a 0))])))
(quicksort l string-ci<?)))
p refresh-sample)]
[p2 (make-object vertical-pane% p)]
[p3 (instantiate horizontal-pane% (p2) [stretchable-width #f])]
[style (let ([pnl (instantiate group-box-panel% ("Style" p3) [stretchable-height #f] [stretchable-width #f])])
(make-object radio-box% #f '("Normal" "Italic" "Slant") pnl refresh-sample))]
[weight (let ([pnl (instantiate group-box-panel% ("Weight" p3) [stretchable-height #f] [stretchable-width #f])])
(make-object radio-box% #f '("Normal" "Bold" "Light") pnl refresh-sample))]
[p4 (instantiate vertical-pane% (p3) [alignment '(left center)])]
[underlined (make-object check-box% "Underlined" p4 refresh-sample)]
[smoothing (make-object choice% "Smoothing:" '("Default" "Some" "Full" "None") p4 refresh-sample)]
[sip (make-object check-box% "Size in Pixels" p4 refresh-sample)]
[sym (make-object check-box% "Map as Symbol" p4 refresh-sample)]
[size (make-object slider% "Size:" 4 127 p2 refresh-sample 12)]
[sample (make-object text-field% "Sample" f void "The quick brown fox jumped over the lazy dog" '(multiple))]
[edit (send sample get-editor)]
[done (lambda (ok) (lambda (b e) (set! ok? ok) (send f show #f)))]
[get-font (lambda () (let ([face (send face get-string-selection)])
(and face
(make-object wx:font% (send size get-value) face
(if (send sym get-value)
'symbol
'default)
(case (send style get-selection) [(0) 'normal] [(1) 'italic] [(2) 'slant])
(case (send weight get-selection) [(0) 'normal] [(1) 'bold] [(2) 'light])
(send underlined get-value)
(case (send smoothing get-selection)
[(0) 'default]
[(1) 'partly-smoothed]
[(2) 'smoothed]
[(3) 'unsmoothed])
(send sip get-value)))))]
[bp (instantiate horizontal-pane% (f) [stretchable-height #f])]
[ms-button (if (eq? (system-type) 'windows)
(begin0
(make-object button% "Use System Dialog..." bp
(lambda (b e)
(let ([new-font (wx:get-font-from-user
message
(mred->wx f)
(get-font))])
(when new-font
(reset-font new-font)))))
;; Spacer:
(make-object pane% bp))
(void))]
[cancel-button (make-object button% "Cancel" bp (done #f))]
[ok-button (make-object button% "OK" bp (done #t) '(border))]
[reset-font
(lambda (font)
(let* ([facen (if font
(send font get-face)
(get-family-builtin-face 'default))]
[f (and facen (send face find-string facen))])
(and f (>= f 0) (send face set-selection f)))
(when font
(send style set-selection (case (send font get-style) [(normal) 0] [(italic) 1] [(slant) 2]))
(send weight set-selection (case (send font get-weight) [(normal) 0] [(bold) 1] [(light) 2]))
(send underlined set-value (send font get-underlined))
(send size set-value (send font get-point-size))
(send sip set-value (send font get-size-in-pixels)))
(refresh-sample (void) (void)))])
(send bp set-alignment 'right 'center)
(send face min-width (max 200 (let-values ([(w h) (send face get-graphical-min-size)]) w)))
(reset-font font)
(send f center)
(send f show #t)
(and ok? (get-font)))]))
(define (play-sound f async?)
(if (not (eq? (system-type) 'unix))
(wx:play-sound f async?)
(begin
(unless (string? f)
(raise-type-error 'play-sound "string" f))
(let* ([subpath (system-library-subpath)]
[make-pattern (lambda (s) (string-append ".*" s ".*"))]
[b (box
(cond
[(regexp-match (make-pattern "linux") subpath)
;; use play interface to sox
"play ~s"]
[(regexp-match (make-pattern "solaris") subpath)
"audioplay ~s"]
[else
(raise-mismatch-error
'play-sound
"not supported by default on this platform"
subpath)]))])
; see if user has overridden defaults
(let ([r (get-preference '|MrEd:playcmd| (lambda () #f))])
(when (and r (string? r))
(set-box! b r)))
((if async? (lambda (x) (process x) #t) system)
(format (unbox b) (expand-path f)))))))
(define get-display-size
(opt-lambda ([full-screen? #f])
(let ([xb (box 0)]
[yb (box 0)])
(wx:display-size xb yb (if full-screen? 1 0))
(values (unbox xb) (unbox yb)))))
(define (get-display-left-top-inset)
(let ([xb (box 0)]
[yb (box 0)])
(wx:display-origin xb yb)
(values (unbox xb) (unbox yb))))
;; Currently only used for PS print and preview
(wx:set-executer
(let ([orig-err (current-error-port)])
(lambda (prog . args)
(let ([cmd (string-append
prog
(let loop ([args args])
(if (null? args)
""
(format " ~s~a" (car args) (loop (cdr args))))))])
(let-values ([(in out pid err x) (apply values (process cmd))])
(close-output-port out)
(let ([echo (lambda (p)
(thread (lambda ()
(dynamic-wind
void
(lambda ()
(let loop ()
(let ([l (read-line p)])
(unless (eof-object? l)
(fprintf orig-err "~a~n" l)
(loop)))))
(lambda () (close-input-port p))))))])
(echo in)
(echo err)
(void)))))))
(define register-collecting-blit
(case-lambda
[(canvas x y w h on off) (register-collecting-blit canvas x y w h on off 0 0 0 0)]
[(canvas x y w h on off on-x) (register-collecting-blit canvas x y w h on off on-x 0 0 0)]
[(canvas x y w h on off on-x on-y) (register-collecting-blit canvas x y w h on off on-x on-y 0 0)]
[(canvas x y w h on off on-x on-y off-x) (register-collecting-blit canvas x y w h on off on-x on-y off-x 0)]
[(canvas x y w h on off on-x on-y off-x off-y)
(check-instance 'register-collecting-blit canvas% 'canvas% #f canvas)
(wx:register-collecting-blit (mred->wx canvas) x y w h on off on-x on-y off-x off-y)]))
(define unregister-collecting-blit
(lambda (canvas)
(check-instance 'unregister-collecting-blit canvas% 'canvas% #f canvas)
(wx:unregister-collecting-blit (mred->wx canvas))))
(define bitmap-dc%
(class100 wx:bitmap-dc% ([bitmap #f])
(inherit set-bitmap)
(sequence
(super-init)
(when bitmap
(set-bitmap bitmap)))))
(define post-script-dc%
(class100 wx:post-script-dc% ([interactive? #t][parent #f][use-paper-bbox? #f])
(sequence
(check-top-level-parent/false '(constructor post-script-dc) parent)
(as-entry
(lambda ()
(let ([p (and parent (mred->wx parent))])
(as-exit (lambda () (super-init interactive? p use-paper-bbox?)))))))))
(define printer-dc%
(class100 wx:printer-dc% ([parent #f])
(sequence
(check-top-level-parent/false '(constructor printer-dc) parent)
(as-entry
(lambda ()
(let ([p (and parent (mred->wx parent))])
(as-exit (lambda () (super-init p)))))))))
(define (find-item-editor item)
(let ([o (let loop ([i item])
(let ([p (send i get-parent)])
(cond
[(not p) #f]
[(is-a? p popup-menu%)
(let ([p (send p get-popup-target)])
(if (is-a? p window<%>)
(let ([f (send p get-top-level-window)])
(and f (send f get-edit-target-object)))
p))]
[(is-a? p menu%) (loop p)]
[else (let ([f (send p get-frame)])
(and f (send f get-edit-target-object)))])))])
(and (is-a? o wx:editor<%>)
o)))
(define append-editor-operation-menu-items
(case-lambda
[(m) (append-editor-operation-menu-items m #t)]
[(m text-only?)
(menu-parent-only 'append-editor-operation-menu-items m)
(let* ([mk (lambda (name key op)
(make-object (class100 menu-item% ()
(inherit enable)
(override
[on-demand
(lambda ()
(let ([o (find-item-editor this)])
(enable (and o
(send o can-do-edit-operation? op)))))])
(sequence
(super-init
name m
(lambda (i e)
(let* ([o (find-item-editor i)])
(and o
(send o do-edit-operation op))))
key)))))]
[mk-sep (lambda () (make-object separator-menu-item% m))])
(mk "&Undo" #\z 'undo)
(mk "Redo" #f 'redo)
(mk-sep)
(mk "&Copy" #\c 'copy)
(mk "Cu&t" #\x 'cut)
(mk "&Paste" #\v 'paste)
(if (eq? (system-type) 'windows)
(mk "Delete" #f 'clear)
(mk "Clear" #f 'clear))
(mk "Select &All" #\a 'select-all)
(unless text-only?
(mk-sep)
(mk "Insert Text Box" #f 'insert-text-box)
(mk "Insert Pasteboard Box" #f 'insert-pasteboard-box)
(mk "Insert Image..." #f 'insert-image))
(void))]))
(define (append-editor-font-menu-items m)
(menu-parent-only 'append-editor-font-menu-items m)
(let ([mk (lambda (name m cb)
(make-object menu-item% name m
(lambda (i e)
(let* ([o (find-item-editor i)])
(and o (cb o))))))]
[mk-sep (lambda (m) (make-object separator-menu-item% m))]
[mk-menu (lambda (name) (make-object menu% name m))])
(let ([family (mk-menu "Font")]
[size (mk-menu "Size")]
[style (mk-menu "Style")]
[weight (mk-menu "Weight")]
[underline (mk-menu "Underline")]
[alignment (mk-menu "Alignment")]
[color (mk-menu "Color")]
[background (mk-menu "Background")])
; Font menu
(for-each (lambda (l f)
(mk l family
(lambda (e)
(send e change-style (make-object wx:style-delta% 'change-family f)))))
'("Standard" "Decorative" "Roman" "Script" "Swiss" "Fixed" "Symbol")
'(default decorative roman script swiss modern symbol))
(mk-sep family)
(mk "Choose..." family (lambda (e) (let ([f (get-font-from-user)])
(when f
(send e change-style (font->delta f))))))
; Size menu
(let ([bigger (make-object menu% "Bigger" size)]
[smaller (make-object menu% "Smaller" size)]
[add-change-size
(lambda (m ls dss xss)
(for-each (lambda (l ds xs)
(mk l m (lambda (e)
(let ([d (make-object wx:style-delta%)])
(send d set-size-add ds)
(send d set-size-mult xs)
(send e change-style d)))))
ls dss xss))])
(add-change-size bigger
'("+1" "+2" "+4" "+8" "+16" "+32")
'(1 2 4 8 16 32)
'(1 1 1 1 1 1))
(mk-sep bigger)
(add-change-size bigger
'("x2" "x3" "x4" "x5")
'(0 0 0 0)
'(2 3 4 5))
(add-change-size smaller
'("-1" "-2" "-4" "-8" "-16" "-32")
'(1 -2 -4 -8 -16 -32)
'(1 1 1 1 1 1))
(mk-sep smaller)
(add-change-size smaller
'("/2" "/3" "/5" "/5")
'(0 0 0 0)
'(#i1/2 #i1/3 #i1/4 #i1/5))
(for-each (lambda (s)
(mk (number->string s) size (lambda (e)
(let ([d (make-object wx:style-delta%)])
(send d set-size-add s)
(send d set-size-mult 0)
(send e change-style d)))))
'(9 10 12 14 16 24 32 48)))
(let ([mk-cg (lambda (cmd arg)
(lambda (e) (send e change-style (make-object wx:style-delta% cmd arg))))])
; Style
(for-each (lambda (name s)
(mk name style (mk-cg 'change-style s)))
'("Normal" "Italic" "Slant")
'(normal italic slant))
; Weight
(for-each (lambda (name s)
(mk name weight (mk-cg 'change-weight s)))
'("Normal" "Bold" "Light")
'(normal bold light))
; Underline
(mk "No Underline" underline (mk-cg 'change-underline #f))
(mk "Underline" underline (mk-cg 'change-underline #t))
(mk "Toggle" underline (lambda (e) (send e change-style (make-object wx:style-delta% 'change-toggle-underline))))
; Alignment
(for-each (lambda (name s)
(mk name alignment (mk-cg 'change-alignment s)))
'("Top" "Center" "Bottom")
'(top center bottom))
(let ([colors '("Black" "White" "Red" "Orange" "Yellow" "Green" "Blue" "Purple" "Cyan" "Magenta" "Grey")])
; Colors
(for-each (lambda (c)
(mk c color (lambda (e) (let ([d (make-object wx:style-delta%)])
(send d set-delta-foreground c)
(send e change-style d)))))
colors)
; Background
(mk "Transparent" background (lambda (e) (let ([d (make-object wx:style-delta%)])
(send d set-transparent-text-backing-on #t)
(send e change-style d))))
(for-each (lambda (c)
(mk c background (lambda (e) (let ([d (make-object wx:style-delta%)])
(send d set-delta-background c)
(send e change-style d)))))
colors))))))
(define (who->name who)
(cond
[(symbol? who) who]
[(eq? (car who) 'method) (string->symbol (format "~a in ~a" (caddr who) (cadr who)))]
[(eq? (car who) 'iconstructor) (iconstructor-name (cadr who))]
[else (constructor-name (cadr who))]))
(define (label-string? s)
(and (string? s) (<= 0 (string-length s) 200)))
(define (check-instance who class class-name false-ok? v)
(unless (or (and false-ok? (not v)) (is-a? v class))
(raise-type-error (who->name who) (format "~a object~a" class-name (if false-ok? " or #f" "")) v)))
(define (check-string/false who str)
(unless (or (not str) (string? str))
(raise-type-error (who->name who) "string or #f" str)))
(define (check-string who str)
(unless (string? str)
(raise-type-error (who->name who) "string" str)))
(define (check-label-string who str)
(unless (label-string? str)
(raise-type-error (who->name who) "string (up to 200 characters)" str)))
(define (check-label-string/false who str)
(unless (or (not str) (label-string? str))
(raise-type-error (who->name who) "string (up to 200 characters) or #f" str)))
(define (check-char/false who c)
(unless (or (not c) (char? c))
(raise-type-error (who->name who) "character or #f" c)))
(define (check-callback who callback)
(unless (and (procedure? callback)
(procedure-arity-includes? callback 2))
(raise-type-error (who->name who) "procedure of arity 2" callback)))
(define (check-callback1 who callback)
(unless (and (procedure? callback)
(procedure-arity-includes? callback 1))
(raise-type-error (who->name who) "procedure of arity 1" callback)))
(define (check-bounded-integer min max false-ok?)
(lambda (who range)
(unless (or (and false-ok? (not range))
(and (integer? range) (exact? range) (<= min range max)))
(raise-type-error (who->name who)
(format "exact integer in [~a, ~a]~a"
min max
(if false-ok? " or #f" ""))
range))))
(define check-range-integer (check-bounded-integer 0 10000 #f))
(define check-slider-integer (check-bounded-integer -10000 10000 #f))
(define check-init-pos-integer (check-bounded-integer -10000 10000 #t))
(define check-margin-integer (check-bounded-integer 0 1000 #f))
(define check-gauge-integer (check-bounded-integer 1 10000 #f))
(define (check-wheel-step cwho wheel-step)
(when (and wheel-step
(not (and (integer? wheel-step)
(exact? wheel-step)
(<= 1 wheel-step 10000))))
(raise-type-error (who->name cwho)
"#f or exact integer in [1,10000]"
wheel-step)))
(define (check-fraction who x)
(unless (and (real? x) (<= 0.0 x 1.0))
(raise-type-error (who->name who)
"real number in [0.0, 1.0]"
x)))
(define (-check-non-negative-integer who i false-ok?)
(when (or i (not false-ok?))
(unless (and (integer? i) (exact? i) (not (negative? i)))
(raise-type-error (who->name who)
(if false-ok?
"non-negative exact integeror #f"
"non-negative exact integer" )
i))))
(define (check-non-negative-integer who i)
(-check-non-negative-integer who i #f))
(define (check-non-negative-integer/false who i)
(-check-non-negative-integer who i #t))
(define check-dimension (check-bounded-integer 0 10000 #t))
(define check-non#f-dimension (check-bounded-integer 0 10000 #f))
(define (check-label-string-or-bitmap who label)
(unless (or (label-string? label) (is-a? label wx:bitmap%))
(raise-type-error (who->name who) "string (up to 200 characters) or bitmap% object" label)))
(define (check-label-string-or-bitmap/false who label)
(unless (or (not label) (label-string? label) (is-a? label wx:bitmap%))
(raise-type-error (who->name who) "string (up to 200 characters), bitmap% object, or #f" label)))
(define (check-label-string/bitmap/iconsym who label)
(unless (or (label-string? label) (is-a? label wx:bitmap%)
(memq label '(app caution stop)))
(raise-type-error (who->name who) "string (up to 200 characters), bitmap% object, or icon symbol" label)))
(define (check-style who reqd other-allowed style)
(unless (and (list? style) (andmap symbol? style))
(raise-type-error (who->name who) "list of style symbols" style))
(when reqd
(letrec ([or-together (lambda (l)
(if (= (length l) 2)
(format "~a or ~a" (car l) (cadr l))
(let loop ([l l])
(if (null? (cdr l))
(format "or ~a" (car l))
(format "~a, ~a" (car l) (loop (cdr l)))))))])
(unless (ormap (lambda (i) (memq i reqd)) style)
(raise-type-error (who->name who)
(format "style list, missing ~a"
(if (= (length reqd) 1)
(car reqd)
(string-append
"one of "
(or-together reqd))))
style))))
(if (and (not reqd) (null? other-allowed))
(unless (null? style)
(raise-type-error (who->name who) "empty style list" style))
(let* ([l (append (or reqd null) other-allowed)]
[bad (ormap (lambda (x) (if (memq x l) #f x)) style)])
(when bad
(raise-type-error (who->name who) (format "style list, ~e not allowed" bad) style))
(let loop ([l style])
(unless (null? l)
(when (memq (car l) (cdr l))
(raise-type-error (who->name who) (format "style list, ~e allowed only once" (car l)) style))
(loop (cdr l)))))))
(define (sleep/yield secs)
(unless (and (real? secs) (not (negative? secs)))
(raise-type-error 'sleep/yield "non-negative real number" secs))
(let ([s (make-semaphore)])
(thread (lambda () (sleep secs) (semaphore-post s)))
(wx:yield s)))
(define get-window-text-extent
(let ([bm #f][dc #f])
(case-lambda
[(string font)
(check-string 'get-window-text-extent string)
(check-instance 'get-window-text-extent wx:font% 'font% #f font)
(unless bm
(set! bm (make-object wx:bitmap% 2 2))
(set! dc (make-object wx:bitmap-dc%))
(send dc set-bitmap bm))
(unless (send bm ok?)
(error 'get-window-text-extent "couldn't allocate sizing bitmap"))
(let-values ([(w h d a) (send dc get-text-extent string font)])
(values (inexact->exact w) (inexact->exact h)))])))
(define x-has-xft? 'unknown)
(define mswin-system #f)
(define mswin-default #f)
(define (look-for-font name)
(if (ormap (lambda (n) (string-ci=? name n)) (wx:get-face-list))
name
"MS San Serif"))
(define (get-family-builtin-face family)
(unless (memq family '(default decorative roman script swiss modern system symbol))
(raise-type-error 'get-family-builtin-face "family symbol" family))
(case (system-type)
[(unix)
;; Detect Xft by looking for a font with a space in front of its name:
(when (eq? x-has-xft? 'unknown)
(set! x-has-xft? (ormap (lambda (s) (regexp-match #rx"^ " s)) (wx:get-face-list))))
(if x-has-xft?
(case family
[(system) " Sans"]
[(default) " Sans"]
[(roman) " Serif"]
[(decorative) " Nimbus Sans L"]
[(modern) " Monospace"]
[(swiss) " Nimbus Sans L"]
[(script) " URW Chancery L"]
[(symbol) " Standard Symbols L,Nimbus Sans L"])
(case family
[(system) "-b&h-lucida"]
[(default) "-b&h-lucida"]
[(roman) "-adobe-times"]
[(decorative) "-adobe-helvetica"]
[(modern) "-adobe-courier"]
[(swiss) "-b&h-lucida"]
[(script) "-itc-zapfchancery"]
[(symbol) "-adobe-symbol"]))]
[(windows)
(case family
[(system)
(unless mswin-system
(set! mswin-system (look-for-font "Tahoma")))
mswin-system]
[(default)
(unless mswin-default
(set! mswin-default (look-for-font "Microsoft Sans Serif")))
mswin-default]
[(default) "MS Sans Serif"]
[(roman) "Times New Roman"]
[(decorative) "Arial"]
[(modern) "Courier New"]
[(swiss) "Arial"]
[(script) "Arial"]
[(symbol) "Symbol"])]
[(macos)
(case family
[(system) "systemfont"]
[(default) "applicationfont"]
[(roman) "Times"]
[(decorative) "Geneva"]
[(modern) "Monaco"]
[(swiss) "Helvetica"]
[(script) "Zaph Chancery"]
[(symbol) "Symbol"])]
[(macosx)
(case family
[(system) "systemfont"]
[(default) "applicationfont"]
[(roman) "Times"]
[(decorative) "Arial"]
[(modern) "Courier New"]
[(swiss) "Helvetica"]
[(script) "Apple Chancery"]
[(symbol) "Symbol"])]))
(define (send-message-to-window x y m)
(check-slider-integer 'send-message-to-window x)
(check-slider-integer 'send-message-to-window y)
(let ([w (wx:location->window x y)])
(and w (let ([f (wx->proxy w)])
(and f
(not (eq? f root-menu-frame))
(send f on-message m))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Timers:
(define timer%
(class wx:timer%
(init [notify-callback void]
[interval #f]
[just-once? #f])
(inherit start)
(define -notify-callback notify-callback)
(define/override (notify) (-notify-callback))
(super-make-object)
(when interval
(start interval just-once?))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Initialize AFM path:
(with-handlers ([not-break-exn? void])
(let ([pss (wx:current-ps-setup)])
(unless (send pss get-afm-path)
(send pss set-afm-path (collection-path "afm")))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(wx:set-dialogs get-file put-file get-ps-setup-from-user message-box)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; snip-class% and editor-data-class% loaders
(let ([load-one
(lambda (str id %)
(let ([m (with-handlers ([void (lambda (x) #f)])
(and (regexp-match "^[(].*[)]$" str)
(read (open-input-string str))))])
(if (and (list? m)
(eq? 'lib (car m))
(andmap string? (cdr m)))
(let ([result (dynamic-require m id)])
(if (is-a? result %)
result
(error 'load-class "not a ~a% instance" id)))
#f)))])
;; install the getters:
(wx:set-snip-class-getter
(lambda (name)
(load-one name 'snip-class wx:snip-class%)))
(wx:set-editor-data-class-getter
(lambda (name)
(load-one name 'editor-data-class wx:editor-data-class%))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define readable-snip<%>
(interface ()
read-one-special))
(define empty-string (make-string 0))
;; open-input-text-editor : (instanceof text%) num num -> input-port
;; creates a user port whose input is taken from the text%,
;; starting at position `start-in'
;; and ending at position `end'.
(define open-input-text-editor
(case-lambda
[(text start end)
;; Check arguments:
(unless (text . is-a? . text%)
(raise-type-error 'open-input-text-editor "text% object" text))
(check-non-negative-integer 'open-input-text-editor start)
(unless (or (eq? end 'end)
(and (integer? end) (exact? end) (not (negative? end))))
(raise-type-error 'open-input-text-editor "non-negative exact integer or 'end" end))
(let ([last (send text last-position)])
(when (start . > . last)
(raise-mismatch-error 'open-input-text-editor
(format "start index outside the range [0,~a]: " last)
start))
(unless (eq? end 'end)
(unless (<= start end last)
(raise-mismatch-error 'open-input-text-editor
(format "end index outside the range [~a,~a]: " start last)
end))))
;; Create the port:
(with-method ([gsp (text get-snip-position)])
(let-values ([(pipe-r pipe-w) (make-pipe)])
(let* ([get-text-generic (generic wx:snip% get-text)]
[get-count-generic (generic wx:snip% get-count)]
[next-generic (generic wx:snip% next)]
[end (if (eq? end 'end) (send text last-position) end)]
[snip (send text find-snip start 'after-or-none)]
[next? #f]
[pos 0]
[lock-semaphore (make-semaphore 1)]
[update-str-to-snip
(lambda (to-str)
(if snip
(let ([snip-start (gsp snip)])
(cond
[(snip-start . >= . end)
(set! snip #f)
(set! next? #f)
0]
[(is-a? snip wx:string-snip%)
(set! next? #t)
(let ([c (min (send-generic snip get-count-generic) (- end snip-start))])
(display (send-generic snip get-text-generic 0 c) pipe-w)
(read-string-avail!* to-str pipe-r))]
[else
(set! next? #f)
0]))
(begin
(set! next? #f)
0)))]
[next-snip
(lambda (to-str)
(set! snip (send-generic snip next-generic))
(set! pos 0)
(update-str-to-snip to-str))]
[read-chars (lambda (to-str)
(cond
[next?
(next-snip to-str)]
[snip
(let ([the-snip snip])
(lambda (file line col ppos)
(if (is-a? the-snip readable-snip<%>)
(with-handlers ([exn:special-comment?
(lambda (exn)
;; implies "done"
(next-snip empty-string)
(raise exn))])
(let-values ([(val size done?)
(send the-snip read-one-special pos file line col ppos)])
(if done?
(next-snip empty-string)
(set! pos (add1 pos)))
(values val size)))
(begin
(next-snip empty-string)
(values (send the-snip copy) 1)))))]
[else eof]))]
[close (lambda () (void))]
[port (make-custom-input-port
(lambda (s)
(if (char-ready? pipe-r)
(read-string-avail!* s pipe-r)
(parameterize ([break-enabled #f])
(if (semaphore-try-wait? lock-semaphore)
;; If there's an error here, the
;; port will remain locked.
(let ([v (read-chars s)])
(semaphore-post lock-semaphore)
v)
(make-semaphore-peek lock-semaphore)))))
#f ; no peek
close)])
(if (is-a? snip wx:string-snip%)
;; Specilal handling for initial snip string in case
;; it starts too early:
(let* ([snip-start (gsp snip)]
[skip (- start snip-start)]
[c (min (- (send-generic snip get-count-generic) skip)
(- end snip-start))])
(set! next? #t)
(display (send-generic snip get-text-generic skip c) pipe-w))
(update-str-to-snip empty-string))
(port-count-lines! port)
port)))]
[(text start) (open-input-text-editor text start 'end)]
[(text) (open-input-text-editor text 0 'end)]))
(define (text-editor-load-handler filename expected-module)
(unless (and (string? filename)
(or (relative-path? filename)
(absolute-path? filename)))
(raise-type-error 'text-editor-load-handler "path string" filename))
(let-values ([(in-port src) (build-input-port filename)])
(dynamic-wind
(lambda () (void))
(lambda ()
(parameterize ([read-accept-compiled #t])
(if expected-module
(with-module-reading-parameterization
(lambda ()
(let* ([first (read-syntax src in-port)]
[module-ized-exp (check-module-form first expected-module filename)]
[second (read in-port)])
(unless (eof-object? second)
(raise-syntax-error
'text-editor-load-handler
(format "expected only a `module' declaration for `~s', but found an extra expression"
expected-module)
second))
(eval module-ized-exp))))
(let loop ([last-time-values (list (void))])
(let ([exp (read-syntax src in-port)])
(if (eof-object? exp)
(apply values last-time-values)
(call-with-values (lambda () (eval exp))
(lambda x (loop x)))))))))
(lambda ()
(close-input-port in-port)))))
;; build-input-port : string -> (values input any)
;; constructs an input port for the load handler. Also
;; returns a value representing the source of code read from the file.
;; if the file's first lines begins with #!, skips the first chars of the file.
(define (build-input-port filename)
(let ([p (open-input-file filename)])
(port-count-lines! p)
(let ([p (cond
[(regexp-match-peek "^WXME01[0-9][0-9] ## " p)
(let ([t (make-object text%)])
(send t insert-file p 'standard)
(close-input-port p)
(open-input-text-editor t))]
[else p])])
(port-count-lines! p) ; in case it's new
(let loop ()
;; Wrap regexp check with `with-handlers' in case the file
;; starts with non-text input
(when (with-handlers ([not-break-exn? (lambda (x) #f)])
(regexp-match-peek "^#!" p))
;; Throw away chars/specials up to eol,
;; and continue if line ends in backslash
(let lloop ([prev #f])
(let ([c (read-char-or-special p)])
(if (or (eof-object? c)
(eq? c #\return)
(eq? c #\newline))
(when (eq? prev #\\)
(loop))
(lloop c))))))
(values p filename))))
(define (open-input-graphical-file filename)
(let-values ([(p name) (build-input-port filename)])
p))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define make-namespace-with-mred
(opt-lambda ([flag 'mred])
(unless (memq flag '(initial mred empty))
(raise-type-error 'make-namespace-with-mred
"flag symbol, one of 'mred, 'initial, or 'empty"
flag))
(let ([orig (current-namespace)]
[mred-name ((current-module-name-resolver)
'(lib "mred.ss" "mred") #f #f)]
[ns (make-namespace (if (eq? flag 'empty) 'empty 'initial))])
(parameterize ([current-namespace ns])
(namespace-attach-module orig mred-name)
(when (eq? flag 'mred)
(namespace-require '(lib "mred.ss" "mred"))
(namespace-require '(lib "class.ss"))))
ns)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax propagate
(lambda (stx)
(syntax-case stx ()
[(_ n ...)
(let ([ns (syntax->list (syntax (n ...)))])
(with-syntax ([(k:n ...)
(map
(lambda (n)
(datum->syntax-object
n
(string->symbol
(format
"wx:~a"
(syntax-e n)))
#f))
ns)])
(syntax (begin
(provide (rename k:n n) ...)))))])))
(propagate add-color<%>
add-editor-keymap-functions
add-text-keymap-functions
add-pasteboard-keymap-functions
begin-busy-cursor
bell
bitmap%
brush%
brush-list%
editor-data%
editor-data-class%
editor-data-class-list<%>
check-for-break
clipboard<%>
clipboard-client%
color%
color-database<%>
control-event%
current-eventspace
current-ps-setup
cursor%
dc<%>
get-display-depth
end-busy-cursor
event%
event-dispatch-handler
eventspace?
find-graphical-system-path
flush-display
font%
font-list%
font-name-directory<%>
get-face-list
get-resource
get-the-editor-data-class-list
get-the-snip-class-list
image-snip%
is-busy?
is-color-display?
key-event%
keymap%
make-eventspace
editor-admin%
editor-set-x-selection-mode
editor-snip-editor-admin<%>
editor-stream-in%
editor-stream-in-base%
editor-stream-in-string-base%
editor-stream-out%
editor-stream-out-base%
editor-stream-out-string-base%
editor-wordbreak-map%
mouse-event%
mult-color<%>
pen%
pen-list%
point%
ps-setup%
read-editor-global-footer
read-editor-global-header
read-editor-version
region%
scroll-event%
snip%
snip-admin%
snip-class%
snip-class-list<%>
special-control-key
special-option-key
label->plain-label
string-snip%
style<%>
style-delta%
style-list%
tab-snip%
write-editor-global-footer
write-editor-global-header
write-editor-version
write-resource
queue-callback
yield
eventspace-shutdown?
get-panel-background
send-event
gl-context<%>)
(define the-color-database (wx:get-the-color-database))
(define the-font-name-directory (wx:get-the-font-name-directory))
(define the-clipboard (wx:get-the-clipboard))
(define the-font-list (wx:get-the-font-list))
(define the-pen-list (wx:get-the-pen-list))
(define the-brush-list (wx:get-the-brush-list))
(define the-style-list (wx:get-the-style-list))
(define the-editor-wordbreak-map (wx:get-the-editor-wordbreak-map))
(define file-creator-and-type
(case-lambda
[(fn) (wx:file-creator-and-type fn)]
[(fn c t) (wx:file-creator-and-type fn c t)]))
(provide button%
canvas%
check-box%
choice%
dialog%
frame%
gauge%
tab-panel%
group-box-panel%
list-box%
editor-canvas%
message%
pane%
horizontal-pane%
vertical-pane%
grow-box-spacer-pane%
panel%
horizontal-panel%
vertical-panel%
radio-box%
slider%
text-field%
window<%>
area<%>
top-level-window<%>
subarea<%>
subwindow<%>
area-container<%>
area-container-window<%>
canvas<%>
control<%>
list-control<%>
menu-item<%>
separator-menu-item%
selectable-menu-item<%>
labelled-menu-item<%>
menu-item%
checkable-menu-item%
menu-item-container<%>
menu%
menu-bar%
popup-menu%
get-top-level-windows
editor-snip%
editor<%>
text%
pasteboard%
graphical-read-eval-print-loop
message-box
message-box/custom
get-file
get-file-list
put-file
get-directory
get-choices-from-user
get-text-from-user
get-ps-setup-from-user
play-sound
get-display-size
get-display-left-top-inset
get-color-from-user
get-font-from-user
append-editor-operation-menu-items
append-editor-font-menu-items
get-top-level-focus-window
get-top-level-edit-target-window
register-collecting-blit
unregister-collecting-blit
bitmap-dc%
post-script-dc%
printer-dc%
current-text-keymap-initializer
sleep/yield
get-window-text-extent
get-family-builtin-face
send-message-to-window
the-clipboard
the-editor-wordbreak-map
the-brush-list
the-color-database
the-font-name-directory
the-pen-list
the-font-list
the-style-list
timer%
readable-snip<%>
open-input-text-editor
open-input-graphical-file
text-editor-load-handler
application-about-handler
application-preferences-handler
application-quit-handler
application-file-handler
current-eventspace-has-standard-menus?
current-eventspace-has-menu-root?
eventspace-handler-thread
make-namespace-with-mred
file-creator-and-type)
) ;; end of module