(module text-box-tool mzscheme (require (lib "tool.ss" "drscheme") (lib "mred.ss" "mred") (lib "framework.ss" "framework") (lib "unitsig.ss") (lib "class.ss") (lib "string-constant.ss" "string-constants") (lib "include-bitmap.ss" "mrlib")) (provide tool@) ;; chunk-string: (listof any) -> (listof any) (define (chunk-string s acc) (cond ((and (null? s) (null? acc)) null) ((null? s) (list (list->string (reverse acc)))) ((char? (car s)) (chunk-string (cdr s) (cons (car s) acc))) ((null? acc) (cons (car s) (chunk-string (cdr s) null))) (else (cons (list->string (reverse acc)) (cons (car s) (chunk-string (cdr s) null)))))) ;; marshall: writable -> string (define (marshall s) (let ((os (open-output-string))) (with-handlers ((exn:fail? (lambda (x) ""))) (write s os) (get-output-string os)))) (define icon (let* ((x (make-object bitmap% 10 10)) (y (make-object bitmap-dc% x))) (send y set-font (make-object font% 24 'default 'normal 'normal )) (send y draw-text "\"" 0 0) x)) (define tool@ (unit/sig drscheme:tool-exports^ (import drscheme:tool^) (define (phase1) (void)) (define (phase2) (void)) (define snipclass-text-box% (class decorated-editor-snipclass% (define/override (make-snip stream-in) (new text-box%)) (super-instantiate ()))) (define snipclass (new snipclass-text-box%)) (send snipclass set-version 1) (send snipclass set-classname "text-box%") (send (get-the-snip-class-list) add snipclass) (define text-box% (class* decorated-editor-snip% (readable-snip<%>) (define/override (make-editor) (new text:keymap%)) (define/override (make-snip) (make-object text-box%)) (inherit get-editor get-admin) (define/override (get-corner-bitmap) icon) (define/override (get-menu) (let ([menu (new popup-menu%)]) (new menu-item% (label "Convert to string") (parent menu) (callback (lambda (x y) (let ([to-ed (find-containing-editor)]) (when to-ed (let ([this-pos (find-this-position)]) (when this-pos (let ([from-ed (get-editor)]) (send to-ed begin-edit-sequence) (send from-ed begin-edit-sequence) (send to-ed delete this-pos (+ this-pos 1)) (let* ((p (open-input-text-editor from-ed 0 'end (lambda (s) (values (box s) 1)))) (contents (let loop ((next (read-char-or-special p))) (cond ((eof-object? next) null) (else (cons next (loop (read-char-or-special p))))))) (repaired-contents (map (lambda (x) (if (string? x) (marshall x) (send (unbox x) copy))) (chunk-string contents null)))) (for-each (lambda (x) (send to-ed insert x this-pos)) (reverse repaired-contents))) (send to-ed end-edit-sequence) (send from-ed end-edit-sequence))))))))) menu)) ;; find-containing-editor : -> (union #f editor) (define/private (find-containing-editor) (let ([admin (get-admin)]) (and admin (send admin get-editor)))) ;; find-this-position : -> (union #f number) (define/private (find-this-position) (let ([ed (find-containing-editor)]) (and ed (send ed get-snip-position this)))) ;; input-port -> (union (listof char) char eof-object? syntax-object) (define/private (get-next port) (let ([v (read-char-or-special port)]) (if (special-comment? v) (get-next port) v))) (define/public (read-special source line column position) (let* ((ed (get-editor)) (port (open-input-text-editor ed)) (str (let loop ((next (get-next port))) (cond ((eof-object? next) null) ((char? next) (cons next (loop (get-next port)))) (else (cons #`(marshall #,next) (loop (get-next port)))))))) #`(let ((marshall (lambda (s) (let ((os (open-output-string))) (with-handlers ((exn:fail? (lambda (x) ""))) (display s os) (get-output-string os)))))) (string-append #,@(chunk-string str null))))) (super-instantiate ()) (inherit set-snipclass) (set-snipclass snipclass))) (define (text-box-mixin %) (class % (inherit get-special-menu get-edit-target-object) (super-new) (new menu-item% (label (string-constant insert-text-box-item)) (parent (get-special-menu)) (callback (lambda (menu event) (let ([c-box (new text-box%)] [text (get-edit-target-object)]) (send text insert c-box) (send text set-caret-owner c-box 'global))))))) (drscheme:get/extend:extend-unit-frame text-box-mixin))))