racket/collects/sirmail/sirmail.rkt
2010-04-27 16:50:15 -06:00

199 lines
5.9 KiB
Racket

;; SirMail: Simple Imap Reader for Mail
;; (with a mail composer, too)
(module sirmail mzscheme
(require mzlib/unit
mzlib/class
mred/mred-sig
mred
framework
mzlib/list)
(require "sirmails.ss"
"pref.ss"
"sirmailr.ss")
(require net/imap-sig
net/smtp-sig
net/head-sig
net/base64-sig
net/mime-sig
net/qp-sig
net/imap
net/smtp
net/head
net/base64
net/mime
net/qp)
(require mrlib/hierlist/hierlist-sig
mrlib/hierlist)
;; For testing purposes, cause GC accounting to be
;; enabled:
(current-memory-use (current-custodian))
;; Constants:
(define inbox-name "Inbox")
(define default-mailbox-options null)
;; ------------------------------------------------------------
;; Every window (reader or sender) is in it's own
;; eventspace. Each should terminate by calling `exit'.
;; We install an exit handler so that we only actually
;; exit when the last window is closed.
(define prim-exit (exit-handler))
(define exit-eventspaces null)
(define exit-sema (make-semaphore 1))
(define (exit-sirmail where)
(let ([evtsp (current-eventspace)])
;; Lock is because a separate process might be calling exit
;; or starting up
(semaphore-wait exit-sema)
(set! exit-eventspaces (remq evtsp exit-eventspaces))
(when (null? exit-eventspaces)
(prim-exit 0))
(semaphore-post exit-sema)))
;; This function is called to start a new window:
(define (start-new-window thunk)
(define evtsp (make-eventspace))
(parameterize ([current-eventspace evtsp])
(semaphore-wait exit-sema)
(set! exit-eventspaces (cons evtsp exit-eventspaces))
(semaphore-post exit-sema)
(queue-callback
(lambda ()
(exit-handler (lambda (x) (exit-sirmail "a")))
(let ([eeh (error-escape-handler)])
(error-escape-handler
(lambda ()
(unless (pair? (get-top-level-windows))
;; Didn't start up...
(exit-sirmail "b"))
(eeh))))
(thunk)
(yield 'wait)
(exit-sirmail "c")))))
;; Reader windows -----------------------------------------------------------
;; This function uses `start-new-window' to open a reader window.
;; A reader window is implemented by an instance of the sirmail@ unit.
(define open-mailbox
(case-lambda
[(mailbox-name) (open-mailbox mailbox-name default-mailbox-options)]
[(mailbox-name mailbox-options)
(start-new-window
(lambda ()
(invoke-unit sirmail@
(import sirmail:environment^
mred^
imap^
smtp^
head^
base64^
mime^
qp^
hierlist^))))]))
;; There's only one Folders window ----------------------------------------
(require "optionr.ss"
"folderr.ss")
(define folders-window #f)
(define folders-lock (make-semaphore 1))
(define (with-folders-lock t)
(dynamic-wind
(lambda () (semaphore-wait folders-lock))
t
(lambda () (semaphore-post folders-lock))))
(define (open-folders-window)
(with-folders-lock
(lambda ()
(if folders-window
(send folders-window show #t)
(let ([shutdown-folders-window
(lambda ()
(with-folders-lock
(lambda ()
(set! folders-window #f)
(exit-sirmail "d"))))]
[mailbox-name inbox-name]
[mailbox-options default-mailbox-options])
(start-new-window
(lambda ()
(set! folders-window
(let ()
(define-compound-unit/infer together@
(import [env : sirmail:environment^]
[s : sirmail:shutdown-folder^]
[mred : mred^]
[imap : imap^]
[hierlist : hierlist^])
(export)
(link option@ folder@))
(invoke-unit together@
(import
sirmail:environment^
sirmail:shutdown-folder^
mred^
imap^
hierlist^)))))))))))
(define (get-active-folder)
(with-folders-lock
(lambda ()
(and folders-window
(send folders-window get-mailbox-name)))))
;; Set Quit handler to try closing all windows --------------------
(define asking-for-quit? #f)
(application-quit-handler
(lambda ()
(if asking-for-quit?
(let ([l (get-top-level-windows)])
(when (pair? l)
;; Createa thread because it's probably a dialog...
(thread (lambda () (send (car l) show #t)))))
(dynamic-wind
(lambda () (set! asking-for-quit? #t))
(lambda ()
(when (= 1 (message-box/custom
"Confirm Quit"
"Really quit?"
"Quit" "Cancel" #f
#f '(default=1)
2))
(let ([l (begin
(semaphore-wait exit-sema)
(begin0
exit-eventspaces
(semaphore-post exit-sema)))])
(for-each
(lambda (evtsp)
(parameterize ([current-eventspace evtsp])
(queue-callback
(lambda ()
(let ([f (get-top-level-edit-target-window)])
(when (and f (f . is-a? . frame%))
(when (send f can-close?)
(send f on-close)
(send f show #f)))))
#f)))
l))))
(lambda () (set! asking-for-quit? #f))))))
;; We start by opening "Inbox" ----------------------------------------
(open-mailbox inbox-name)
;; Wait for an explicit exit
(yield (make-semaphore)))