racket/collects/slideshow/tool.rkt
2010-10-13 15:55:16 -05:00

368 lines
15 KiB
Racket

#|
todo:
pict snip :
- snipclass for running snips outside of drscheme
- need to toggle the picts back to scheme code when
important things happen (save, execute, etc).
- should save the true pict size when it gets recorded.
- show the true size in the GUI
- when a snip is deleted from inside the pasteboard, remove it from the caches
- check that when a snip is inserted, things revert (?).
maybe something better should happen?
- test up-to-date? flag
|#
(module tool mzscheme
(require drscheme/tool
mred
mzlib/class
mzlib/unit
mzlib/contract
string-constants
framework
texpict/mrpict
texpict/pict-value-snip
mzlib/list
"private/pict-box-lib.ss"
"private/image-snipr.ss")
(provide tool@
get-snp/poss
build-lib-pict-stx)
(define orig-inspector (current-code-inspector))
(define orig-lcp (current-library-collection-paths))
(define-syntax syntax/cert
(syntax-rules ()
[(_ stx tmpl) (let ([stx stx])
(syntax-recertify
(syntax/loc stx tmpl)
stx
orig-inspector
#f))]))
(define tool@
(unit
(import drscheme:tool^)
(export drscheme:tool-exports^)
(define original-output-port (current-output-port))
(define (oprintf . args) (apply fprintf original-output-port args))
(define sc-hide-picts (string-constant slideshow-hide-picts))
(define sc-show-picts (string-constant slideshow-show-picts))
(define sc-cannot-show-picts (string-constant slideshow-cannot-show-picts))
(define sc-insert-pict-box (string-constant slideshow-insert-pict-box))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; pict box
;;
(define pict-pasteboard%
(class pasteboard%
(inherit get-admin)
(define/augment (after-insert snip before x y)
(let ([admin (get-admin)])
(when (is-a? admin editor-snip-editor-admin<%>)
(send (send admin get-snip) inserted-snip)))
(inner (void) after-insert snip before x y))
(super-new)))
(define pict-snip%
(class* decorated-editor-snip% (readable-snip<%>)
(inherit get-editor)
(define show-picts? #f)
;; only for use in the copy method and the read snipclass method
(define/public (set-show-picts b) (set! show-picts? b))
;; up-to-date? : boolean
;; indicates if the recent shapes cache is up to date
(define up-to-date? #f)
;; only for use in the copy method and the read snipclass method
(define/public (set-up-to-date b) (set! up-to-date? b))
;; bitmap-table : hash-table[snip -o> bitmap]
;; maps from the true (Scheme) snip to its current bitmap
(define bitmap-table (make-hash-table))
;; only for use in the copy method and the read snipclass method
(define/public (set-bitmap-table bt) (set! bitmap-table bt))
(define/override (make-editor) (make-object pict-pasteboard%))
(define/override (get-corner-bitmap) slideshow-bm)
(define/override (copy)
(let* ([cp (make-object pict-snip%)]
[ed (send cp get-editor)])
(send (get-editor) copy-self-to ed)
(let ([bt (make-hash-table)])
(hash-table-for-each bitmap-table (lambda (k v) (hash-table-put! bt (send k copy) v)))
(send cp set-bitmap-table bt)
(send cp set-show-picts show-picts?)
(send cp set-up-to-date up-to-date?)
cp)))
(define/override (get-menu)
(let ([menu (instantiate popup-menu% () (title #f))])
(cond
[show-picts?
(make-object checkable-menu-item%
sc-hide-picts
menu
(lambda (x y)
(hide-picts)))]
[up-to-date?
(make-object checkable-menu-item%
sc-show-picts
menu
(lambda (x y)
(show-picts)))]
[else
(let ([m (make-object menu-item%
sc-cannot-show-picts
menu
(lambda (x y) void))])
(send m enable #f))])
menu))
(define/public (update-bitmap-table sub-snips sub-bitmaps)
(let ([hidden-table (make-hash-table)])
(let loop ([snip (send (get-editor) find-first-snip)])
(cond
[snip
(when (is-a? snip image-snip/r%)
(hash-table-put! hidden-table (send snip get-orig-snip) snip))
(loop (send snip next))]
[else (void)]))
(for-each (lambda (snip bitmap)
(hash-table-put! bitmap-table snip bitmap)
(let ([showing (hash-table-get hidden-table snip (lambda () #f))])
(when showing
(send showing set-bitmap bitmap))))
sub-snips
sub-bitmaps)
(set! up-to-date? #t)))
(define/private (show-picts)
(let ([pb (get-editor)])
(set! show-picts? #t)
(send pb begin-edit-sequence)
(set! system-insertion? #t)
(hash-table-for-each
bitmap-table
(lambda (snip bitmap)
(let ([bm-snip (make-object image-snip/r% bitmap snip)])
(let-values ([(x y) (snip-location pb snip)])
(send snip release-from-owner)
(send pb insert bm-snip x y)))))
(set! system-insertion? #f)
(send pb end-edit-sequence)))
(define/private (hide-picts)
(let ([pb (get-editor)])
(set! show-picts? #f)
(send pb begin-edit-sequence)
(let ([all-snips (let loop ([snip (send pb find-first-snip)])
(cond
[snip (cons snip (loop (send snip next)))]
[else null]))])
(set! system-insertion? #t)
(for-each (lambda (snip)
(when (is-a? snip image-snip/r%)
(let ([real-snip (send snip get-orig-snip)])
(let-values ([(x y) (snip-location pb snip)])
(send snip release-from-owner)
(send pb insert real-snip x y)))))
all-snips)
(set! system-insertion? #f))
(send pb end-edit-sequence)))
;; called on user thread
(define/public (read-special file line col pos)
(let ([ans-chan (make-channel)])
(parameterize ([current-eventspace drs-eventspace])
(queue-callback
(lambda ()
(channel-put ans-chan (get-snp/poss this)))))
(let ([snp/poss (channel-get ans-chan)])
(build-lib-pict-stx
(lambda (ids)
(with-syntax ([(ids ...) ids]
[this this]
[build-bitmap/check build-bitmap/check]
[drs-eventspace drs-eventspace]
[(subsnips ...) (map snp/pos-snp snp/poss)]
[(bitmap-ids ...) (generate-ids "drawer-id" (map snp/pos-snp snp/poss))])
(syntax
(let ([bitmap-ids (build-bitmap/check ids (pict-width ids) (pict-height ids) draw-pict pict?)] ...)
(parameterize ([current-eventspace drs-eventspace])
(queue-callback
(lambda () ;; drs eventspace
(send this update-bitmap-table
(list subsnips ...)
(list bitmap-ids ...)))))))))
snp/poss))))
(define/override (write stream-out)
(send stream-out put (if show-picts? 1 0))
(send stream-out put (if up-to-date? 1 0))
(send (get-editor) write-to-file stream-out))
(define/override (make-snip) (new pict-snip%))
(define system-insertion? #f)
(define/public (inserted-snip)
(unless system-insertion?
(set! up-to-date? #f)
(when show-picts?
(hide-picts))))
(inherit show-border set-snipclass)
(super-new)
(show-border #t)
(set-snipclass lib-pict-snipclass)))
(define lib-pict-snipclass%
(class snip-class%
(define/override (read stream-in)
(let* ([snip (new pict-snip%)]
[editor (send snip get-editor)]
[show-picts? (not (zero? (send stream-in get-exact)))]
[up-to-date? (not (zero? (send stream-in get-exact)))])
(send editor read-from-file stream-in #f)
(send snip set-up-to-date up-to-date?)
(send snip set-show-picts show-picts?)
(let ([bt (make-hash-table)])
(let loop ([snip (send editor find-first-snip)])
(cond
[(is-a? snip snip%)
(when (is-a? snip image-snip/r%)
(let ([orig (send snip get-orig-snip)]
[bm (send snip get-bitmap)])
(hash-table-put! bt orig bm)))
(loop (send snip next))]
[else (void)]))
(send snip set-bitmap-table bt))
snip))
(super-new)))
;; build-bitmap/check : pict number number (pict dc number number -> void) (any -> boolean) -> bitmap
;; called on user-thread with a pict that the user made
(define (build-bitmap/check pict w h draw-pict pict?)
(unless (pict? pict)
(error 'pict-snip "expected a pict to be the result of each embedded snip, got ~e"
pict))
(let* ([bm (make-object bitmap%
(max 1 (add1 (inexact->exact (ceiling w))))
(max 1 (add1 (inexact->exact (ceiling h)))))]
[bdc (make-object bitmap-dc% bm)])
(send bdc clear)
(send bdc set-smoothing 'aligned)
(draw-pict pict bdc 0 0)
(send bdc set-bitmap #f)
bm))
(define (set-box/f b v) (when (box? b) (set-box! b v)))
(define slideshow-bm
(let ([bm (make-object bitmap% (build-path (collection-path "slideshow") "slideshow.bmp"))])
(and (send bm ok?)
bm)))
(define drs-eventspace (current-eventspace))
(define (add-special-menu-item menu frame)
(let* ([find-insertion-point ;; -> (union #f editor<%>)
;; returns the editor (if there is one) with the keyboard focus
(lambda ()
(let ([editor (send frame get-edit-target-object)])
(and editor
(is-a? editor editor<%>)
(let loop ([editor editor])
(let ([focused (send editor get-focus-snip)])
(if (and focused
(is-a? focused editor-snip%))
(loop (send focused get-editor))
editor))))))]
[insert-snip
(lambda (make-obj)
(let ([editor (find-insertion-point)])
(when editor
(let ([snip (make-obj)])
(send editor insert snip)
(send editor set-caret-owner snip 'display)))))]
[demand-callback ;; : menu-item% -> void
;; enables the menu item when there is an editor available.
(lambda (item)
(send item enable (find-insertion-point)))])
(instantiate menu:can-restore-menu-item% ()
(label sc-insert-pict-box)
(parent menu)
(demand-callback demand-callback)
(callback
(lambda (menu evt)
(insert-snip
(lambda () (new pict-snip%))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; wire it up
;;
(define (phase1) (void))
(define (phase2) (void))
(define orig-namespace (current-namespace))
(define (pict->image-snip p)
(let* ([pict-width (dynamic-require 'texpict/mrpict 'pict-width)]
[pict-height (dynamic-require 'texpict/mrpict 'pict-height)]
[draw-pict (dynamic-require 'texpict/mrpict 'draw-pict)]
[bm (make-object bitmap%
(max 1 (add1 (inexact->exact (ceiling (pict-width p)))))
(max 1 (add1 (inexact->exact (ceiling (pict-height p))))))]
[bdc (make-object bitmap-dc% bm)])
(send bdc clear)
(send bdc set-smoothing 'aligned)
(draw-pict p bdc 0 0)
(send bdc set-bitmap #f)
(make-object image-snip% bm)))
(drscheme:language:add-snip-value
;; Convert to print?
(lambda (x)
;; if the require fails, then we cannot display the pict.
;; this can happen when, for example, there is no mred module
;; in the namespace
(let ([pict? (with-handlers ((exn:fail? (λ (x) #f)))
(dynamic-require 'texpict/mrpict 'pict?))])
(and pict?
(pict? x))))
;; Converter:
pict->image-snip
;; Namespace setup:
(λ ()
(with-handlers ((exn:fail? void))
;; code running in this thunk cannot fail, or else drscheme gets wedged.
(dynamic-require 'texpict/mrpict #f))))
(define lib-pict-snipclass (make-object lib-pict-snipclass%))
(send lib-pict-snipclass set-version 2)
(send lib-pict-snipclass set-classname (format "~s" '(lib "pict-snipclass.ss" "slideshow")))
(send (get-the-snip-class-list) add lib-pict-snipclass))))