racket/collects/sirmail/utilr.ss
Eli Barzilay 7d50e61c7f * Newlines at EOFs
* Another big chunk of v4-require-isms
* Allow `#lang framework/keybinding-lang' for keybinding files
* Move hierlist sources into "mrlib/hierlist", leave stub behind

svn: r10689
2008-07-09 07:18:06 +00:00

268 lines
8.0 KiB
Scheme

(module utilr mzscheme
(require mzlib/unit
mzlib/class
mred/mred-sig
net/qp-sig
net/base64-sig
(prefix unihead: net/unihead)
mzlib/etc
mzlib/string)
(require "sirmails.ss")
(provide util@)
(define-unit util@
(import mred^
base64^
qp^)
(export sirmail:utils^)
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Utilities ;;
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define crlf (string #\return #\linefeed))
(define (split s re)
(regexp-split re s))
(define (drop-last-if-empty orig-l)
(let loop ([l orig-l][accum null])
(cond
[(null? l) orig-l]
[(null? (cdr l))
(if (equal? #"" (car l))
(reverse accum)
orig-l)]
[else (loop (cdr l) (cons (car l) accum))])))
(define (splice l sep)
(if (null? l)
#""
(let ([p (open-output-bytes)])
(let loop ([l l])
(write-bytes (car l) p)
(unless (null? (cdr l))
(display sep p)
(loop (cdr l))))
(get-output-bytes p))))
(define (split-crlf/preserve-last s)
(split s #rx#"\r\n"))
(define (split-crlf s)
(drop-last-if-empty (split-crlf/preserve-last s)))
(define (split-lf s)
(drop-last-if-empty (split s #rx#"\n")))
(define (crlf->lf s)
(splice (split-crlf s) #"\n"))
(define (crlf->lf/preserve-last s)
(splice (split-crlf/preserve-last s) #"\n"))
(define (lf->crlf s)
(splice (split-lf s) #"\r\n"))
(define (string-crlf->lf s)
(regexp-replace* #rx"\r\n" s "\n"))
(define (string-lf->crlf s)
(regexp-replace* #rx"\n" s "\r\n"))
(define (header->lines s)
(regexp-split #rx"\r\n"
;; We don't want the extra empty line at the end:
(substring s 0 (- (string-length s) 2))))
(define (enumerate n)
(let loop ([n n][a null])
(if (zero? n)
a
(loop (sub1 n) (cons n a)))))
(define (find i l)
(let loop ([l l][pos 0])
(if (null? l)
#f
(if (eq? (car l) i)
pos
(loop (cdr l) (add1 pos))))))
(define (string->regexp s)
(regexp-quote s))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (show-error-message-box x main-frame)
(let ([sp (open-output-string)])
;; use error display handler in case
;; errortrace (or something else) is
;; installed
(parameterize ([current-output-port sp]
[current-error-port sp])
((error-display-handler)
(if (exn? x)
(exn-message x)
(format "uncaught exn: ~s" x))
x))
(message-box "Error"
(get-output-string sp)
main-frame
'(ok stop))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (break-really-hard? set-d!)
(let* ([d (make-object dialog% "Danger")]
[p (make-object vertical-pane% d)]
[bp (make-object horizontal-pane% d)]
[result #f])
(send bp stretchable-width #f)
(send bp stretchable-height #f)
(make-object message% "Breaking now is dangerous." p)
(make-object message% "It requires killing the window." p)
(make-object message% "" p)
(make-object message% "Are you sure you want to kill?" p)
(make-object button% "&Kill" bp (lambda (b e)
(set! result #t)
(send d show #f)))
(make-object button% "Cancel" bp (lambda (b e) (send d show #f)))
(set-d! d)
(send d show #t)
result))
;; as-background: (bool window<%> braek-thunk ->)
;; (break-ok-thunk break-not-ok-thunk -> X)
;; (-> Y) -> X
;; Runs a task in the background.
;; The `enable' function is called with #f as the first argument
;; near the start of the background task. The `break-thunk' can be
;; called to interrupt the task. The `enable' function might
;; not get called at all if the task is fast enough.
;; The `go' function performs the task (in a thread created by
;; as-background); it receives thunks that it can call to
;; indicate when breaks are "safe". If the user tries to break
;; at a non-safe point, the user is warned; if the user
;; proceeds, things are killed and `exit' is called. If
;; the user breaks at a safe point, a break signal is sent
;; to the thread for the background task.
;; The `pre-kill' thunk is called before things are killed
;; for a non-"safe" break.
(define (as-background enable go pre-kill)
(let* ([v #f]
[exn #f]
[break-ok? #f]
[breaking-dialog #f]
[adjust-break (make-semaphore 1)]
[change-break-ok (lambda (ok?)
(lambda ()
(semaphore-wait adjust-break)
(set! break-ok? ok?)
(let ([waiting? (and ok? breaking-dialog)])
(when waiting?
(send breaking-dialog show #f)
(set! breaking-dialog #f))
(semaphore-post adjust-break)
(when waiting?
(break-thread (current-thread))))))]
[s (make-semaphore 0)]
[t (thread (lambda ()
(with-handlers ([void (lambda (x)
(set! exn x))])
(set! v (call-with-values
(lambda () (go (change-break-ok #f)
(change-break-ok #t)))
list))
((change-break-ok #f)))
(when breaking-dialog
(send breaking-dialog show #f))
(semaphore-post s)))])
;; If the operation is fast enough, no need to disable then yield then enable,
;; which makes the screen flash and causes events to get dropped. 1/4 second
;; seems "fast enough".
(unless (sync/timeout 0.25 s)
(let ([v (enable #f #f
(lambda ()
(semaphore-wait adjust-break)
(if break-ok?
(break-thread t)
(let ([v (break-really-hard? (lambda (d)
(set! breaking-dialog d)
(semaphore-post adjust-break)))])
(semaphore-wait adjust-break)
(set! breaking-dialog #f)
(semaphore-post adjust-break)
(when v
(pre-kill)
(kill-thread t)
(exit))))))])
(yield s)
(enable #t v void)))
(if exn
(raise exn)
(apply values v))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; the actual fixed width font is already set by
;; the editor:standard-style-list-mixin
(define (make-fixed-width c e wrap? wrap-bm)
(let ([s (send (send e get-style-list)
find-named-style "Standard")])
(send e set-tabs null 8 #f)
(let ([font (send s get-font)]
[dc (send c get-dc)]
[wbox (box 0)]
[hbox (box 0)])
(send e get-view-size wbox hbox)
(let-values ([(w h) (send c get-size)]
[(1w 1h d a) (send dc get-text-extent "X" font)])
(let ([80chars (+ (* 1w 80)
2 ; +2 for caret
(if wrap-bm
(send wrap-bm get-width)
0))])
(when wrap?
(when wrap-bm
(send e set-autowrap-bitmap wrap-bm))
(send e set-max-width 80chars))
(send c min-width
(inexact->exact (round (+ 80chars (- w (unbox wbox)))))))))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define confirm-box
(opt-lambda (title message [parent #f] [style null])
(if (= 1 (message-box/custom
title
message
"&Yes"
"&No"
#f
parent
(append (if (memq 'app style) null '(caution))
'(default=1))
2))
'yes
'no)))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (get-pw-from-user username parent)
(get-text-from-user "Password"
(format "Password for ~a:" username)
parent
""
'(password)))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Decoding `from' names ;;
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define generalize-encoding unihead:generalize-encoding)
(define parse-encoded unihead:decode-for-header)
(define encode-for-header unihead:encode-for-header)))