(unit/sig framework:keymap^ (import mred^ [keys : framework:keys^] [preferences : framework:preferences^] [finder : framework:finder^] [handler : framework:handler^] [scheme-paren : framework:scheme-paren^] [frame : framework:frame^] [mzlib:function : mzlib:function^]) (rename [-get-file get-file]) (define aug-keymap<%> (interface ((class->interface keymap%)) get-chained-keymaps get-map-function-table get-map-function-table/ht)) (define aug-keymap-mixin (mixin ((class->interface keymap%)) (aug-keymap<%>) args (private [chained-keymaps null]) (public [get-chained-keymaps (lambda () chained-keymaps)]) (rename [super-chain-to-keymap chain-to-keymap]) (override [chain-to-keymap (lambda (keymap prefix?) (super-chain-to-keymap keymap prefix?) (set! chained-keymaps (if prefix? (cons keymap chained-keymaps) (append chained-keymaps (list keymap)))))]) (private [function-table (make-hash-table)]) (public [get-function-table (lambda () function-table)]) (rename [super-map-function map-function]) (override [map-function (lambda (keyname fname) (super-map-function (canonicalize-keybinding-string keyname) fname) (hash-table-put! function-table (string->symbol keyname) fname))]) (public [get-map-function-table (lambda () (get-map-function-table/ht (make-hash-table)))] [get-map-function-table/ht (lambda (table) (hash-table-for-each function-table (lambda (keyname fname) (unless (hash-table-get table keyname (lambda () #f)) (hash-table-put! table keyname fname)))) (for-each (lambda (chained-keymap) (when (is-a? chained-keymap aug-keymap<%>) (send chained-keymap get-map-function-table/ht table))) chained-keymaps) table)]) (sequence (apply super-init args)))) (define aug-keymap% (aug-keymap-mixin keymap%)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; ;;;;;;;; ;;;;;;; canonicalize-keybinding-string ;;;;;;;; ;;;;;;; ;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; canonicalize-keybinding-string : string -> string ;; The result can be used with string=? to determine ;; if two key bindings refer to the same key. ;; Assumes a well-formed keystring. (define (canonicalize-keybinding-string str) (let* ([chars (map char-downcase (string->list str))] [separated-keys (map canonicalize-single-keybinding-string (split-out #\; chars))]) (join-strings ";" separated-keys))) ;; join-strings : string (listof string) -> string ;; concatenates strs with sep between each of them (define (join-strings sep strs) (if (null? strs) "" (apply string-append (cons (car strs) (let loop ([sepd-strs (cdr strs)]) (cond [(null? sepd-strs) null] [else (list* sep (car sepd-strs) (loop (cdr sepd-strs)))])))))) ;; canonicalize-single-keybinding-string : (listof char) -> string (define (canonicalize-single-keybinding-string chars) (let* ([neg? (char=? (car chars) #\:)] [mods/key (split-out #\: (if neg? (cdr chars) chars))] [mods (let loop ([mods mods/key]) (cond [(null? mods) null] [(null? (cdr mods)) null] [else (cons (car mods) (loop (cdr mods)))]))] [key (car (mzlib:function:last-pair mods/key))] [shift (if neg? #f 'd/c)] [control (if neg? #f 'd/c)] [alt (if neg? #f 'd/c)] [meta (if neg? #f 'd/c)] [command (if neg? #f 'd/c)] [do-key (lambda (char val) (cond [(eq? val #t) (string char)] [(eq? val #f) (string #\~ char)] [(eq? val 'd/c) #f]))]) (for-each (lambda (mod) (let ([val (not (char=? (car mod) #\~))]) (case (if (char=? (car mod) #\~) (cadr mod) (car mod)) [(#\s) (set! shift val)] [(#\c) (set! control val)] [(#\a) (set! alt val)] [(#\d) (set! command val)] [(#\m) (set! meta val)]))) mods) (join-strings ":" (mzlib:function:filter (lambda (x) x) (list (do-key #\a alt) (do-key #\c control) (do-key #\d command) (do-key #\m meta) (do-key #\s shift) (apply string key)))))) ;; split-out : char (listof char) -> (listof (listof char)) ;; splits a list of characters at its first argument (define (split-out split-char chars) (let loop ([chars chars] [this-split null] [all-split null]) (cond [(null? chars) (reverse (cons (reverse this-split) all-split))] [else (let ([char (car chars)]) (cond [(char=? split-char char) (loop (cdr chars) null (cons (reverse this-split) all-split))] [else (loop (cdr chars) (cons char this-split) all-split)]))]))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; ;;;;;;;; ;;;;;;; end canonicalize-keybinding-string ;;;;;;;; ;;;;;;; ;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (make-meta-prefix-list key) (list (string-append "m:" key) (string-append "ESC;" key))) (define send-map-function-meta (lambda (keymap key func) (for-each (lambda (key) (send keymap map-function key func)) (make-meta-prefix-list key)))) (define setup-global ; Define some useful keyboard functions (let* ([ring-bell (lambda (edit event) (bell))] [mouse-popup-menu (lambda (edit event) (when (send event button-up?) (let ([a (send edit get-admin)]) (when a (let ([m (make-object popup-menu%)]) (append-editor-operation-menu-items m) (for-each (lambda (i) (when (is-a? i selectable-menu-item<%>) (send i set-shortcut #f))) (send m get-items)) (let-values ([(x y) (send edit dc-location-to-editor-location (send event get-x) (send event get-y))]) (send a popup-menu m (+ x 1) (+ y 1))))))))] [up-out-of-editor-snip (lambda (text event) (let ([editor-admin (send text get-admin)]) (when (is-a? editor-admin editor-snip-editor-admin<%>) (let* ([snip (send editor-admin get-snip)] [snip-admin (send snip get-admin)]) (when snip-admin (let ([editor (send snip-admin get-editor)]) (when (is-a? editor text%) (let ([new-pos (+ (send editor get-snip-position snip) (if (= 0 (send text get-end-position)) 0 (send snip get-count)))]) (send editor set-position new-pos new-pos)) (send editor set-caret-owner #f 'display))))))) #t)] [down-into-editor-snip (lambda (dir get-pos) (lambda (text event) (when (= (send text get-start-position) (send text get-end-position)) (let* ([pos (send text get-start-position)] [snip (send text find-snip pos dir)]) (when (and snip (is-a? snip editor-snip%)) (let ([embedded-editor (send snip get-editor)]) (when (is-a? embedded-editor text%) (send embedded-editor set-position (get-pos embedded-editor))) (send text set-caret-owner snip 'display))))) #t))] [right-into-editor-snip (down-into-editor-snip 'after-or-none (lambda (x) 0))] [left-into-editor-snip (down-into-editor-snip 'before-or-none (lambda (x) (send x last-position)))] [toggle-anchor (lambda (edit event) (send edit set-anchor (not (send edit get-anchor))))] [center-view-on-line (lambda (edit event) (let ([new-mid-line (send edit position-line (send edit get-start-position))] [bt (box 0)] [bb (box 0)]) (send edit get-visible-line-range bt bb) (let* ([half (sub1 (quotient (- (unbox bb) (unbox bt)) 2))] [last-pos (send edit position-line (send edit last-position))] [top-pos (send edit line-start-position (max (min (- new-mid-line half) last-pos) 0))] [bottom-pos (send edit line-start-position (max 0 (min (+ new-mid-line half) last-pos)))]) (send edit scroll-to-position top-pos #f bottom-pos))) #t)] [flash-paren-match (lambda (edit event) (send edit on-default-char event) (let ([pos (scheme-paren:backward-match edit (send edit get-start-position) 0)]) (when pos (send edit flash-on pos (+ 1 pos)))) #t)] [collapse-variable-space (lambda (leave-one? edit event) (letrec ([end-pos (send edit last-position)] [find-nonwhite (lambda (pos d) (let loop ([pos pos]) (if (or (and (= d -1) (= pos 0)) (and (= pos end-pos) (= d 1))) pos (let ([c (send edit get-character pos)]) (cond [(char=? #\newline c) pos] [(char-whitespace? c) (loop (+ pos d))] [else pos])))))]) (let ([sel-start (send edit get-start-position)] [sel-end (send edit get-end-position)]) (when (= sel-start sel-end) (let ([start (if (= sel-start 0) 0 (+ (find-nonwhite (- sel-start 1) -1) 1))] [end (find-nonwhite sel-start 1)]) (send edit begin-edit-sequence) (cond ;; funny case when to delete the newline [(and leave-one? (= (+ start 1) end) (< end end-pos) (char=? #\space (send edit get-character start)) (char=? #\newline (send edit get-character end))) (send edit delete end (+ end 1))] [else (send edit delete start end) (cond [leave-one? (send edit insert #\space start) (send edit set-position (+ start 1))] [else (send edit set-position start)])]) (send edit end-edit-sequence))))))] [collapse-space (lambda (edit event) (collapse-variable-space #t edit event))] [remove-space (lambda (edit event) (collapse-variable-space #f edit event))] [collapse-newline (lambda (edit event) (letrec ([find-nonwhite (lambda (pos d offset) (let/ec escape (let ([max (if (> offset 0) (send edit last-position) 0)]) (let loop ([pos pos]) (if (= pos max) (escape pos) (let ([_ (printf "get-char.1: ~s~n" (+ pos offset))] [c (send edit get-character (+ pos offset))]) (cond [(char=? #\newline c) (loop (+ pos d)) (escape pos)] [(char-whitespace? c) (loop (+ pos d))] [else pos])))))))]) (let ([sel-start (send edit get-start-position)] [sel-end (send edit get-end-position)]) (when (= sel-start sel-end) (let* ([pos-line (send edit position-line sel-start #f)] [pos-line-start (send edit line-start-position pos-line)] [pos-line-end (send edit line-end-position pos-line)] [whiteline? (let loop ([pos pos-line-start]) (if (>= pos pos-line-end) #t (and (char-whitespace? (send edit get-character pos)) (loop (add1 pos)))))] [start (find-nonwhite pos-line-start -1 -1)] [end (find-nonwhite pos-line-end 1 0)] [start-line (send edit position-line start #f)] [start-line-start (send edit line-start-position start-line)] [end-line (send edit position-line end #f)] [end-line-start (send edit line-start-position (add1 end-line))]) (cond [(and whiteline? (= start-line pos-line) (= end-line pos-line)) ; Special case: just delete this line (send edit delete pos-line-start (add1 pos-line-end))] [(and whiteline? (< start-line pos-line)) ; Can delete before & after (send* edit (begin-edit-sequence) (delete (add1 pos-line-end) end-line-start) (delete start-line-start pos-line-start) (end-edit-sequence))] [else ; Only delete after (send edit delete (add1 pos-line-end) end-line-start)]))))))] [open-line (lambda (edit event) (let ([sel-start (send edit get-start-position)] [sel-end (send edit get-end-position)]) (if (= sel-start sel-end) (send* edit (insert #\newline) (set-position sel-start)))))] [transpose-chars (lambda (edit event) (let ([sel-start (send edit get-start-position)] [sel-end (send edit get-end-position)]) (when (and (= sel-start sel-end) (not (= sel-start 0)) (not (= sel-start (send edit last-position)))) (let ([sel-start (if (= sel-start (send edit line-end-position (send edit position-line sel-start))) (sub1 sel-start) sel-start)]) (let ([s (send edit get-text sel-start (add1 sel-start))]) (send* edit (begin-edit-sequence) (delete sel-start (add1 sel-start)) (insert s (- sel-start 1)) (set-position (add1 sel-start)) (end-edit-sequence)))))))] [transpose-words (lambda (edit event) (let ([sel-start (send edit get-start-position)] [sel-end (send edit get-end-position)]) (when (= sel-start sel-end) (let ([word-1-start (box sel-start)]) (send edit find-wordbreak word-1-start #f 'caret) (let ([word-1-end (box (unbox word-1-start))]) (send edit find-wordbreak #f word-1-end 'caret) (let ([word-2-end (box (unbox word-1-end))]) (send edit find-wordbreak #f word-2-end 'caret) (let ([word-2-start (box (unbox word-2-end))]) (send edit find-wordbreak word-2-start #f 'caret) (let ([text-1 (send edit get-text (unbox word-1-start) (unbox word-1-end))] [text-2 (send edit get-text (unbox word-2-start) (unbox word-2-end))]) (send* edit (begin-edit-sequence) (insert text-1 (unbox word-2-start) (unbox word-2-end)) (insert text-2 (unbox word-1-start) (unbox word-1-end)) (set-position (unbox word-2-end)) (end-edit-sequence))))))))))] [capitalize-it (lambda (edit char-case1 char-case2) (let ([sel-start (send edit get-start-position)] [sel-end (send edit get-end-position)] [real-end (send edit last-position)]) (when (= sel-start sel-end) (let ([word-end (let ([b (box sel-start)]) (send edit find-wordbreak #f b 'caret) (min real-end (unbox b)))]) (send edit begin-edit-sequence) (let loop ([pos sel-start] [char-case char-case1]) (when (< pos word-end) (let ([c (send edit get-character pos)]) (cond [(char-alphabetic? c) (send edit insert (list->string (list (char-case c))) pos (add1 pos)) (loop (add1 pos) char-case2)] [else (loop (add1 pos) char-case)])))) (send* edit (end-edit-sequence) (set-position word-end))))))] [capitalize-word (lambda (edit event) (capitalize-it edit char-upcase char-downcase))] [upcase-word (lambda (edit event) (capitalize-it edit char-upcase char-upcase))] [downcase-word (lambda (edit event) (capitalize-it edit char-downcase char-downcase))] [kill-word (lambda (edit event) (let ([sel-start (send edit get-start-position)] [sel-end (send edit get-end-position)]) (let ([end-box (box sel-end)]) (send edit find-wordbreak #f end-box 'caret) (send edit kill 0 sel-start (unbox end-box)))))] [backward-kill-word (lambda (edit event) (let ([sel-start (send edit get-start-position)] [sel-end (send edit get-end-position)]) (let ([start-box (box sel-start)]) (send edit find-wordbreak start-box #f 'caret) (send edit kill 0 (unbox start-box) sel-end))))] [region-click (lambda (edit event f) (when (and (send event button-down?) (is-a? edit text%)) (let ([x-box (box (send event get-x))] [y-box (box (send event get-y))] [eol-box (box #f)]) (send edit global-to-local x-box y-box) (let ([click-pos (send edit find-position (unbox x-box) (unbox y-box) eol-box)] [start-pos (send edit get-start-position)] [end-pos (send edit get-end-position)]) (let ([eol (unbox eol-box)]) (if (< start-pos click-pos) (f click-pos eol start-pos click-pos) (f click-pos eol click-pos end-pos)))))))] [copy-click-region (lambda (edit event) (region-click edit event (lambda (click eol start end) (send edit flash-on start end) (send edit copy #f 0 start end))))] [cut-click-region (lambda (edit event) (region-click edit event (lambda (click eol start end) (send edit cut #f 0 start end))))] [paste-click-region (lambda (edit event) (region-click edit event (lambda (click eol start end) (send edit set-position click) (send edit paste 0 click))))] [mouse-copy-clipboard (lambda (edit event) (send edit copy #f (send event get-time-stamp)))] [mouse-paste-clipboard (lambda (edit event) (send edit paste (send event get-time-stamp)))] [mouse-cut-clipboard (lambda (edit event) (send edit cut #f (send event get-time-stamp)))] [select-click-word (lambda (edit event) (region-click edit event (lambda (click eol start end) (let ([start-box (box click)] [end-box (box click)]) (send edit find-wordbreak start-box end-box 'selection) (send edit set-position (unbox start-box) (unbox end-box))))))] [select-click-line (lambda (edit event) (region-click edit event (lambda (click eol start end) (let* ([line (send edit position-line click eol)] [start (send edit line-start-position line #f)] [end (send edit line-end-position line #f)]) (send edit set-position start end)))))] [goto-line (lambda (edit event) (let ([num-str (call/text-keymap-initializer (lambda () (get-text-from-user "Goto Line" "Goto Line:")))]) (when (string? num-str) (let ([line-num (inexact->exact (string->number num-str))]) (cond [(and (number? line-num) (= line-num (floor line-num)) (<= 1 line-num (+ (send edit last-line) 1))) (let ([pos (send edit line-start-position (sub1 line-num))]) (send edit set-position pos))] [else (message-box "Goto Line" (format "~a is not a valid line number. It must be an integer between 1 and ~a" num-str (+ (send edit last-line) 1)))])))) #t)] [goto-position (lambda (edit event) (let ([num-str (call/text-keymap-initializer (lambda () (get-text-from-user "Goto Position" "Goto Position:")))]) (if (string? num-str) (let ([pos (string->number num-str)]) (if pos (send edit set-position (sub1 pos)))))) #t)] [repeater (lambda (n edit) (let* ([km (send edit get-keymap)] [done (lambda () (send km set-break-sequence-callback void) (send km remove-grab-key-function))]) (send km set-grab-key-function (lambda (name local-km edit event) (if name (begin (done) (dynamic-wind (lambda () (send edit begin-edit-sequence)) (lambda () (let loop ([n n]) (unless (zero? n) (send local-km call-function name edit event) (loop (sub1 n))))) (lambda () (send edit end-edit-sequence)))) (let ([k (send event get-key-code)]) (if (and (char? k) (char<=? #\0 k #\9)) (set! n (+ (* n 10) (- (char->integer k) (char->integer #\0)))) (begin (done) (dynamic-wind (lambda () (send edit begin-edit-sequence)) (lambda () (let loop ([n n]) (unless (zero? n) (send edit on-char event) (loop (sub1 n))))) (lambda () (send edit end-edit-sequence))))))) #t)) (send km set-break-sequence-callback done) #t))] [make-make-repeater (lambda (n) (lambda (edit event) (repeater n edit)))] [current-macro '()] [building-macro #f] [build-macro-km #f] [build-protect? #f] [do-macro (lambda (edit event) ; If c:x;e during record, copy the old macro (when building-macro (set! building-macro (append (reverse current-macro) (cdr building-macro)))) (let ([bm building-macro] [km (send edit get-keymap)]) (dynamic-wind (lambda () (set! building-macro #f) (send edit begin-edit-sequence)) (lambda () (let/ec escape (for-each (lambda (f) (let ([name (car f)] [event (cdr f)]) (if name (unless (send km call-function name edit event #t) (escape #t)) (send edit on-char event)))) current-macro))) (lambda () (send edit end-edit-sequence) (set! building-macro bm)))) #t)] [start-macro (lambda (edit event) (if building-macro (send build-macro-km break-sequence) (letrec ([km (send edit get-keymap)] [done (lambda () (if build-protect? (send km set-break-sequence-callback done) (begin (set! building-macro #f) (send km set-break-sequence-callback void) (send km remove-grab-key-function))))]) (set! building-macro '()) (set! build-macro-km km) (send km set-grab-key-function (lambda (name local-km edit event) (dynamic-wind (lambda () (set! build-protect? #t)) (lambda () (if name (send local-km call-function name edit event) (send edit on-default-char event))) (lambda () (set! build-protect? #f))) (when building-macro (set! building-macro (cons (cons name event) building-macro))) #t)) (send km set-break-sequence-callback done))) #t)] [end-macro (lambda (edit event) (when building-macro (set! current-macro (reverse building-macro)) (set! build-protect? #f) (send build-macro-km break-sequence)) #t)] [delete-key (lambda (edit event) (let ([kmap (send edit get-keymap)]) (send kmap call-function (if (preferences:get 'framework:delete-forward?) "delete-next-character" "delete-previous-character") edit event #t)))] [toggle-overwrite (lambda (edit event) (send edit set-overwrite-mode (not (send edit get-overwrite-mode))))]) (lambda (kmap) (let* ([map (lambda (key func) (send kmap map-function key func))] [map-meta (lambda (key func) (send-map-function-meta kmap key func))] [add (lambda (name func) (send kmap add-function name func))] [add-m (lambda (name func) (send kmap add-function name func))]) ; Map names to keyboard functions (add "toggle-overwrite" toggle-overwrite) (add "exit" (lambda (edit event) (let ([frame (send edit get-frame)]) (if (and frame (is-a? frame frame:standard-menus<%>)) ((ivar frame file-menu:quit)) (bell))))) (add "ring-bell" ring-bell) (add "flash-paren-match" flash-paren-match) (add "left-into-editor-snip" left-into-editor-snip) (add "right-into-editor-snip" right-into-editor-snip) (add "up-out-of-editor-snip" up-out-of-editor-snip) (add "toggle-anchor" toggle-anchor) (add "center-view-on-line" center-view-on-line) (add "collapse-space" collapse-space) (add "remove-space" remove-space) (add "collapse-newline" collapse-newline) (add "open-line" open-line) (add "transpose-chars" transpose-chars) (add "transpose-words" transpose-words) (add "capitalize-word" capitalize-word) (add "upcase-word" upcase-word) (add "downcase-word" downcase-word) (add "kill-word" kill-word) (add "backward-kill-word" backward-kill-word) (let loop ([n 9]) (unless (negative? n) (let ([s (number->string n)]) (add (string-append "command-repeat-" s) (make-make-repeater n)) (loop (sub1 n))))) (add "keyboard-macro-run-saved" do-macro) (add "keyboard-macro-start-record" start-macro) (add "keyboard-macro-end-record" end-macro) (add-m "copy-clipboard" mouse-copy-clipboard) (add-m "cut-clipboard" mouse-cut-clipboard) (add-m "paste-clipboard" mouse-paste-clipboard) (add-m "copy-click-region" copy-click-region) (add-m "cut-click-region" cut-click-region) (add-m "paste-click-region" paste-click-region) (add-m "select-click-word" select-click-word) (add-m "select-click-line" select-click-line) (add "goto-line" goto-line) (add "goto-position" goto-position) (add "delete-key" delete-key) (add "mouse-popup-menu" mouse-popup-menu) ; Map keys to functions (map "c:g" "ring-bell") (map-meta "c:g" "ring-bell") (map "c:x;c:g" "ring-bell") (map "c:c;c:g" "ring-bell") (map ")" "flash-paren-match") (map "]" "flash-paren-match") (map "}" "flash-paren-match") (map "\"" "flash-paren-match") (map "c:p" "previous-line") (map "up" "previous-line") (map "s:c:p" "select-up") (map "s:up" "select-up") (map "c:n" "next-line") (map "down" "next-line") (map "s:c:n" "select-down") (map "s:down" "select-down") (map "c:e" "end-of-line") (map "d:RIGHT" "end-of-line") (map "m:RIGHT" "end-of-line") (map "END" "end-of-line") (map "d:s:RIGHT" "select-to-end-of-line") (map "m:s:RIGHT" "select-to-end-of-line") (map "s:END" "select-to-end-of-line") (map "s:c:e" "select-to-end-of-line") (map "c:a" "beginning-of-line") (map "d:LEFT" "beginning-of-line") (map "m:LEFT" "beginning-of-line") (map "HOME" "beginning-of-line") (map "d:s:LEFT" "select-to-beginning-of-line") (map "m:s:LEFT" "select-to-beginning-of-line") (map "s:HOME" "select-to-beginning-of-line") (map "s:c:a" "select-to-beginning-of-line") (map "c:f" "forward-character") (map "right" "forward-character") (map "s:c:f" "forward-select") (map "s:right" "forward-select") (map "c:b" "backward-character") (map "left" "backward-character") (map "s:c:b" "backward-select") (map "s:left" "backward-select") (map-meta "f" "forward-word") (map "a:RIGHT" "forward-word") (map "c:RIGHT" "forward-word") (map-meta "s:f" "forward-select-word") (map "a:s:RIGHT" "forward-select-word") (map "c:s:RIGHT" "forward-select-word") (map-meta "b" "backward-word") (map "a:LEFT" "backward-word") (map "c:left" "backward-word") (map-meta "s:b" "backward-select-word") (map "a:s:LEFT" "backward-select-word") (map "c:s:left" "backward-select-word") (map-meta "<" "beginning-of-file") (map "d:UP" "beginning-of-file") (map "c:HOME" "beginning-of-file") (map "s:c:home" "select-to-beginning-of-file") (map "s:d:up" "select-to-beginning-of-file") (map-meta ">" "end-of-file") (map "d:DOWN" "end-of-file") (map "c:end" "end-of-file") (map "s:c:end" "select-to-end-of-file") (map "s:d:down" "select-to-end-of-file") (map "c:v" "next-page") (map "a:DOWN" "next-page") (map "pagedown" "next-page") (map "c:DOWN" "next-page") (map "s:c:v" "select-page-down") (map "a:s:DOWN" "select-page-down") (map "s:pagedown" "select-page-down") (map "s:c:DOWN" "select-page-down") (map-meta "v" "previous-page") (map "a:up" "previous-page") (map "pageup" "previous-page") (map "c:up" "previous-page") (map-meta "s:v" "select-page-up") (map "s:a:up" "select-page-up") (map "s:pageup" "select-page-up") (map "s:c:up" "select-page-up") (map "c:h" "delete-previous-character") (map "c:d" "delete-next-character") (map "del" "delete-key") (map-meta "d" "kill-word") (map-meta "del" "backward-kill-word") (map-meta "c" "capitalize-word") (map-meta "u" "upcase-word") (map-meta "l" "downcase-word") (map "c:l" "center-view-on-line") (map "c:k" "delete-to-end-of-line") (map "c:y" "paste-clipboard") (map-meta "y" "paste-next") (map "a:v" "paste-clipboard") (map "d:v" "paste-clipboard") (map "c:_" "undo") (map "c:+" "redo") (map "a:z" "undo") (map "d:z" "undo") (map "c:x;u" "undo") (map "c:w" "cut-clipboard") (map "a:x" "cut-clipboard") (map "d:x" "cut-clipboard") (map-meta "w" "copy-clipboard") (map "a:c" "copy-clipboard") (map "d:c" "copy-clipboard") (map "s:delete" "cut-clipboard") (map "c:insert" "copy-clipboard") (map "s:insert" "paste-clipboard") (map-meta "space" "collapse-space") (map-meta "\\" "remove-space") (map "c:x;c:o" "collapse-newline") (map "c:o" "open-line") (map "c:t" "transpose-chars") (map-meta "t" "transpose-words") (map "c:space" "toggle-anchor") (map-meta "c:left" "left-into-editor-snip") (map-meta "c:right" "right-into-editor-snip") (map-meta "c:up" "up-out-of-editor-snip") (map "insert" "toggle-overwrite") (map-meta "o" "toggle-overwrite") (map-meta "g" "goto-line") (map-meta "p" "goto-position") (map "c:u" "command-repeat-0") (let loop ([n 9]) (unless (negative? n) (let ([s (number->string n)]) (map-meta s (string-append "command-repeat-" s)) (loop (sub1 n))))) (map "c:x;e" "keyboard-macro-run-saved") (map "c:x;(" "keyboard-macro-start-record") (map "c:x;)" "keyboard-macro-stop-record") (map "leftbuttontriple" "select-click-line") (map "leftbuttondouble" "select-click-word") (map "middlebutton" "paste-click-region") (map ":rightbuttonseq" "mouse-popup-menu"))))) (define setup-search (let* ([send-frame (lambda (method) (lambda (edit event) (let ([frame (cond [(is-a? edit editor<%>) (let ([canvas (send edit get-active-canvas)]) (and canvas (send canvas get-top-level-window)))] [(is-a? edit area<%>) (send edit get-top-level-window)] [else #f])]) (if frame ((ivar/proc frame method)) (bell))) #t))]) (lambda (kmap) (let* ([map (lambda (key func) (send kmap map-function key func))] [map-meta (lambda (key func) (send-map-function-meta kmap key func))] [add (lambda (name func) (send kmap add-function name func))] [add-m (lambda (name func) (send kmap add-function name func))]) (add "move-to-search-or-search" (send-frame 'move-to-search-or-search)) ;; key 1 (add "move-to-search-or-reverse-search" (send-frame 'move-to-search-or-reverse-search)) ;; key 1b, backwards (add "find-string-again" (send-frame 'search-again)) ;; key 2 (add "toggle-search-focus" (send-frame 'toggle-search-focus)) ;; key 3 (add "hide-search" (send-frame 'hide-search)) ;; key 4 (case (system-type) [(unix) (map "c:s" "move-to-search-or-search") (map-meta "%" "move-to-search-or-search") (map "c:r" "move-to-search-or-reverse-search") (map "f3" "find-string-again") (map "c:i" "toggle-search-focus") (map "c:g" "hide-search")] [(windows) (map "c:r" "move-to-search-or-reverse-search") (map "f3" "find-string-again") (map "c:g" "find-string-again") ;; covered by menu ;(map "c:f" "move-to-search-or-search") (map "c:i" "toggle-search-focus")] [(macos) (map "c:s" "move-to-search-or-search") (map "c:g" "hide-search") ;; covered by menu ;(map "d:f" "move-to-search-or-search") (map "d:r" "move-to-search-or-reverse-search") (map "d:g" "find-string-again") (map "c:i" "toggle-search-focus")]))))) (define setup-file (let* ([save-file-as (lambda (edit event) (let ([file (finder:put-file)]) (if file (send edit save-file file))) #t)] [save-file (lambda (edit event) (if (send edit get-filename) (send edit save-file) (save-file-as edit event)) #t)] [load-file (lambda (edit event) (handler:open-file) #t)]) (lambda (kmap) (let* ([map (lambda (key func) (send kmap map-function key func))] [map-meta (lambda (key func) (send-map-function-meta kmap key func))] [add (lambda (name func) (send kmap add-function name func))] [add-m (lambda (name func) (send kmap add-function name func))]) (add "save-file" save-file) (add "save-file-as" save-file-as) (add "load-file" load-file) (map "c:x;c:s" "save-file") (map "d:s" "save-file") (map "c:x;c:w" "save-file-as") (map "c:x;c:f" "load-file"))))) (define (setup-editor kmap) (let ([add/map (lambda (func op key) (send kmap add-function func (lambda (editor evt) (send editor do-edit-operation op))) (send kmap map-function (string-append (case (system-type) [(macos) "d:"] [(windows) "c:"] [(unix) "a:"] [else (error 'keymap.ss "unknown platform: ~s" (system-type))]) key) func))]) (add/map "editor-undo" 'undo "z") (add/map "editor-redo" 'redo "y") (add/map "editor-cut" 'cut "x") (add/map "editor-copy" 'copy "c") (add/map "editor-paste" 'paste "v") (add/map "editor-select-all" 'select-all "a"))) (define (generic-setup keymap) (add-editor-keymap-functions keymap) (add-pasteboard-keymap-functions keymap) (add-text-keymap-functions keymap)) (define global (make-object aug-keymap%)) (setup-global global) (generic-setup global) (define (get-global) global) (define file (make-object aug-keymap%)) (setup-file file) (generic-setup file) (define (-get-file) file) (define search (make-object aug-keymap%)) (generic-setup search) (setup-search search) (define (get-search) search) (define editor (make-object aug-keymap%)) (setup-editor editor) (define (get-editor) editor) (define (call/text-keymap-initializer thunk) (let ([ctki (current-text-keymap-initializer)]) (parameterize ([current-text-keymap-initializer (lambda (keymap) (send keymap chain-to-keymap global #t) (ctki keymap))]) (thunk)))))