added ability to have user-defined keybindings in PLaneT

svn: r4267
This commit is contained in:
Robby Findler 2006-09-07 03:18:39 +00:00
parent 5d37cb79e7
commit 81a70fe101
6 changed files with 130 additions and 42 deletions

View File

@ -148,7 +148,9 @@
mixin
basics-mixin
basics<%>
create-root-menubar))
create-root-menubar
add-keybindings-item
planet-spec?))
(define-signature drscheme:program^
(frame%))

View File

@ -3,6 +3,7 @@
(require (lib "name-message.ss" "mrlib")
(lib "string-constant.ss" "string-constants")
(lib "unitsig.ss")
(lib "match.ss")
(lib "class.ss")
(lib "string.ss")
(lib "list.ss")
@ -29,6 +30,8 @@
(define basics<%> (interface (frame:standard-menus<%>)))
(define last-keybindings-planet-attempt "")
(define basics-mixin
(mixin (frame:standard-menus<%>) (basics<%>)
(inherit get-edit-target-window get-edit-target-object get-menu-bar)
@ -187,37 +190,85 @@
(let ([filename (get-file (string-constant keybindings-choose-user-defined-file)
this)])
(when filename
(add-keybindings-file filename))))))
(add-keybindings-item/update-prefs filename))))))
(new menu-item%
(parent keybindings-menu)
(label (string-constant keybindings-add-user-defined-keybindings/planet))
(callback
(λ (x y)
(let ([planet-spec (get-text-from-user (string-constant drscheme)
(string-constant keybindings-type-planet-spec)
this
last-keybindings-planet-attempt)])
(when planet-spec
(set! last-keybindings-planet-attempt planet-spec))
(cond
[(planet-string-spec? planet-spec)
=>
(λ (planet-sexp-spec)
(add-keybindings-item/update-prefs planet-sexp-spec))]
[else
(message-box (string-constant drscheme)
(format (string-constant keybindings-planet-malformed-spec)
planet-spec))])))))
(let ([ud (preferences:get 'drscheme:user-defined-keybindings)])
(unless (null? ud)
(new separator-menu-item% (parent keybindings-menu))
(for-each (λ (path)
(for-each (λ (item)
(new menu-item%
(label (format (string-constant keybindings-menu-remove)
(path->string path)))
(if (path? item)
(path->string item)
(format "~s" item))))
(parent keybindings-menu)
(callback
(λ (x y) (remove-keybindings-file path)))))
(λ (x y) (remove-keybindings-item item)))))
ud)))))))
(unless (current-eventspace-has-standard-menus?)
(make-object separator-menu-item% menu)))
(super-new)))
(define (add-keybindings-file path)
(define (add-keybindings-item/update-prefs item)
(when (add-keybindings-item item)
(preferences:set 'drscheme:user-defined-keybindings
(cons item
(preferences:get 'drscheme:user-defined-keybindings)))))
(define (planet-string-spec? p)
(let ([sexp
(with-handlers ([exn:fail:read? (λ (x) #f)])
(read (open-input-string p)))])
(and sexp
(planet-spec? sexp)
sexp)))
(define (planet-spec? p)
(match p
[`(planet ,(? string?) (,(? string?) ,(? string?) ,(? number?))) #t]
[`(planet ,(? string?) (,(? string?) ,(? string?) ,(? number?) ,(? number?))) #t]
[else #f]))
;; add-keybindings-item : keybindings-item[path or planet spec] -> boolean
;; boolean indicates if the addition happened sucessfully
(define (add-keybindings-item item)
(with-handlers ([exn? (λ (x)
(message-box (string-constant drscheme)
(exn-message x)))])
(keymap:add-user-keybindings-file path)
(preferences:set 'drscheme:user-defined-keybindings
(cons path
(preferences:get 'drscheme:user-defined-keybindings)))))
(define (remove-keybindings-file path)
(keymap:remove-user-keybindings-file path)
(format (string-constant keybindings-error-installing-file)
(if (path? item)
(path->string item)
(format "~s" item))
(exn-message x)))
#f)])
(keymap:add-user-keybindings-file item)
#t))
(define (remove-keybindings-item item)
(keymap:remove-user-keybindings-file item)
(preferences:set
'drscheme:user-defined-keybindings
(remq path
(preferences:get 'drscheme:user-defined-keybindings))))
(remove item
(preferences:get 'drscheme:user-defined-keybindings))))
;; install-plt-file : (union #f dialog% frame%) -> void
;; asks the user for a .plt file, either from the web or from

View File

@ -66,14 +66,19 @@
(preferences:set-default 'drscheme:show-interactions-on-execute #t boolean?)
(preferences:set-default 'drscheme:open-in-tabs #f boolean?)
(preferences:set-default 'drscheme:toolbar-shown #t boolean?)
(preferences:set-default 'drscheme:user-defined-keybindings '() (λ (x) (and (list? x) (andmap path? x))))
(preferences:set-default 'drscheme:user-defined-keybindings
'()
(λ (x) (and (list? x)
(andmap (λ (x) (or (path? x) (drscheme:frame:planet-spec? x)))
x))))
(preferences:set-un/marshall
'drscheme:user-defined-keybindings
(λ (in) (map path->bytes in))
(λ (ex) (if (and (list? ex)
(andmap bytes? ex))
(map bytes->path ex)
'())))
(λ (in) (map (λ (x) (if (path? x) (path->bytes x) x))
in))
(λ (ex) (if (list? ex)
(map (λ (x) (if (bytes? x) (bytes->path x) x)) ex)
'())))
(let ([number-between-zero-and-one?
(λ (x) (and (number? x) (<= 0 x 1)))])
@ -364,12 +369,8 @@
(autosave:restore-autosave-files/gui)
;; install user's keybindings
(with-handlers ([exn?
(λ (exn)
(message-box (string-constant drscheme)
(exn-message exn)))])
(for-each keymap:add-user-keybindings-file
(preferences:get 'drscheme:user-defined-keybindings)))
(for-each drscheme:frame:add-keybindings-item
(preferences:get 'drscheme:user-defined-keybindings))
;; the initial window doesn't set the
;; unit object's state correctly, yet.

View File

@ -966,18 +966,22 @@
"method.")
(keymap:remove-user-keybindings-file
(-> path? any)
(-> any/c any)
(user-keybindings-path)
"Removes the keymap previously added by"
"@flink keymap:add-user-keybindings-file %"
".")
(keymap:add-user-keybindings-file
(-> path? any)
(user-keybindings-path)
"Chains the keymap defined by \\var{user-keybindings-path} to "
(-> any/c any)
(user-keybindings-path-or-require-spec)
"Chains the keymap defined by \\var{user-keybindings-path-or-require-spec} to "
"the global keymap, returned by "
"@flink keymap:get-global %"
".")
"."
""
"If \\var{user-keybindings-path-or-require-spec} is a path, the module is loaded"
"directly from that path. Otherwise, \\var{user-keybindings-path-or-require-spec}"
"is treated like an argument to \\scheme|require|.")
(keymap:add-to-right-button-menu
(case->
(((is-a?/c popup-menu%) (is-a?/c editor<%>) (is-a?/c event%) . -> . void?) . -> . void?)

View File

@ -23,27 +23,50 @@
(define user-keybindings-files (make-hash-table 'equal))
(define (add-user-keybindings-file path)
(define (add-user-keybindings-file spec)
(hash-table-get
user-keybindings-files
path
spec
(λ ()
(let ([sexp (and (file-exists? path)
(call-with-input-file path read))])
(let* ([path (spec->path spec)]
[sexp (and (file-exists? path)
(call-with-input-file path read))])
(match sexp
[`(module ,name (lib "keybinding-lang.ss" "framework") ,@(x ...))
(let ([km (dynamic-require path '#%keymap)])
(hash-table-put! user-keybindings-files path km)
(let ([km (dynamic-require spec '#%keymap)])
(hash-table-put! user-keybindings-files spec km)
(send global chain-to-keymap km #t))]
[else (error 'add-user-keybindings-file
(string-constant user-defined-keybinding-malformed-file)
(path->string path))])))))
(define (remove-user-keybindings-file path)
(define (spec->path p)
(cond
[(path? p) p]
[else
(let* ([mod-name ((current-module-name-resolver) p #f #f)]
[str (symbol->string mod-name)]
[pth (substring str 1 (string-length str))])
(let-values ([(base name _) (split-path pth)])
(let ([filenames
(sort
(filter (λ (x) (substring? (path->string name) x))
(map path->string (directory-list base)))
(λ (x y) (> (string-length x) (string-length y))))])
(when (null? filenames)
(error 'spec->path "could not convert ~s, found no filenames for ~s" p mod-name))
(build-path base (car filenames)))))]))
(define (substring? s1 s2)
(and (<= (string-length s1)
(string-length s2))
(string=? s1 (substring s2 0 (string-length s1)))))
(define (remove-user-keybindings-file spec)
(let/ec k
(let ([km (hash-table-get user-keybindings-files path (λ () (k (void))))])
(let ([km (hash-table-get user-keybindings-files spec (λ () (k (void))))])
(send global remove-chained-keymap km)
(hash-table-remove! user-keybindings-files path))))
(hash-table-remove! user-keybindings-files spec))))
(define (remove-chained-keymap ed keymap-to-remove)
(let ([ed-keymap (send ed get-keymap)])

View File

@ -621,9 +621,16 @@ please adhere to these guidelines:
(keybindings-sort-by-name "Sort by Name")
(keybindings-sort-by-key "Sort by Key")
(keybindings-add-user-defined-keybindings "Add User-defined Keybindings...")
(keybindings-add-user-defined-keybindings/planet "Add User-defined Keybindings from PLaneT...")
(keybindings-menu-remove "Remove ~a")
(keybindings-choose-user-defined-file "Please choose a file containing keybindings.")
(keybindings-planet-malformed-spec "The PLaneT spec is malformed: ~a") ; the string will be what the user typed in
(keybindings-type-planet-spec "Please enter a PLaneT require spec (without the `require')")
; first ~a will be a string naming the file or planet package where the keybindings come from;
; second ~a will be an error message
(keybindings-error-installing-file "Error when installing the keybindings ~a:\n\n~a")
(user-defined-keybinding-error "Error running keybinding ~a\n\n~a")
(user-defined-keybinding-malformed-file "The file ~a does not contain a module written in the (lib \"keybinding-lang.ss\" \"framework\") language.")