448 lines
14 KiB
Scheme
448 lines
14 KiB
Scheme
;;This is an application of the "scsh-ncurses" lib.
|
||
;;It consists of a buffer with input.
|
||
;;
|
||
;;At the beginning we only have an empty buffer for example.
|
||
;;You pass this buffer an char after char to the input-function,
|
||
;;which modifies the content of the buffer. In the standard-case
|
||
;;you get the characters from "wgetch".
|
||
;;
|
||
;;One brief example:
|
||
; ;
|
||
; ;
|
||
; ; (begin
|
||
; ; (define my-buffer (make-buffer-welcome 20 20 "test"))
|
||
; ; (init-screen)
|
||
; ; (define win (newwin 20 20 5 5))
|
||
; ; (box win (ascii->char 0) (ascii->char 0))
|
||
; ; (noecho)
|
||
; ; (keypad win #t)
|
||
; ; (set! my-buffer (print-command-buffer win my-buffer))
|
||
; ; (let loop ((ch (wgetch win)))
|
||
; ; (begin
|
||
; ; (wclear win)
|
||
; ; (box win (ascii->char 0) (ascii->char 0))
|
||
; ; (set! my-buffer (input my-buffer ch))
|
||
; ; (set! my-buffer (print-command-buffer win my-buffer))
|
||
; ; (if (= ch key-f1)
|
||
; ; (begin
|
||
; ; (echo)
|
||
; ; (endwin))
|
||
; ; (begin
|
||
; ; (set! my-buffer (cursor-right-pos win my-buffer))
|
||
; ; (loop (wgetch win)))))))
|
||
|
||
;;It is very esential, that you set keypad to true und call noecho!!!
|
||
|
||
|
||
(define first-column 2)
|
||
|
||
;;record-type buffer
|
||
(define-record-type buffer :buffer
|
||
(make-buffer text
|
||
pos-line ;; Cursor-Position auf text bezogen
|
||
pos-col
|
||
pos-fin-ln ;; ???
|
||
pos-y ;; Cursor relativ zum Fenster
|
||
pos-x
|
||
num-lines
|
||
num-cols
|
||
can-write
|
||
history-pos)
|
||
(text buffer-text set-buffer-text!)
|
||
(pos-line buffer-pos-line set-buffer-pos-line!)
|
||
(pos-col buffer-pos-col set-buffer-pos-col!)
|
||
(pos-fin-ln buffer-pos-fin-ln set-buffer-pos-fin-ln!)
|
||
(pos-y buffer-pos-y set-buffer-pos-y!)
|
||
(pos-x buffer-pos-x set-buffer-pos-x!)
|
||
(num-lines buffer-num-lines set-buffer-num-lines!)
|
||
(num-cols buffer-num-cols set-buffer-num-cols!)
|
||
(can-write buffer-can-write set-buffer-can-write!)
|
||
(history-pos buffer-history-pos set-buffer-history-pos!))
|
||
|
||
(define-record-discloser :buffer
|
||
(lambda (rec)
|
||
`(buffer ,@(map (lambda (p)
|
||
(cons (car p) ((cdr p) rec)))
|
||
`((pos-line . ,buffer-pos-line)
|
||
(pos-col . ,buffer-pos-col)
|
||
(pos-fin-ln . ,buffer-pos-fin-ln)
|
||
(pos-y . ,buffer-pos-y)
|
||
(pos-x . ,buffer-pos-x)
|
||
(history-pos . ,buffer-history-pos)
|
||
(lines . ,(lambda (b) (length (buffer-text b)))))))))
|
||
|
||
(define (buffer-text-current-line buffer)
|
||
(list-ref (buffer-text buffer)
|
||
(- (buffer-pos-line buffer) 1)))
|
||
|
||
(define (buffer-text-append-new-line! buffer)
|
||
(set-buffer-text! buffer
|
||
(append (buffer-text buffer)
|
||
(list ""))))
|
||
|
||
;;handle input
|
||
(define (input buffer ch)
|
||
(cond
|
||
|
||
;; enter key
|
||
((= ch 10)
|
||
(set-buffer-text! buffer
|
||
(append (buffer-text buffer) (list "")))
|
||
(set-buffer-pos-line! buffer
|
||
(+ (buffer-pos-line buffer) 1))
|
||
(set-buffer-pos-col! buffer first-column)
|
||
(set-buffer-history-pos! buffer
|
||
(- (length (buffer-text buffer)) 1)))
|
||
;; backspace
|
||
((and (= ch key-backspace)
|
||
(buffer-can-write buffer)
|
||
(not (< (buffer-pos-col buffer) 3)))
|
||
(set-buffer-text! buffer
|
||
(remove-from-command-buffer
|
||
(buffer-text buffer)
|
||
(buffer-pos-col buffer)))
|
||
(regress-buffer-cursor-column! buffer))
|
||
|
||
;; FIXME
|
||
;; move cursor to previous line Ctrl-p, keycode 16
|
||
; ((= ch 16)
|
||
; (if (< pos-fin-ln 2)
|
||
; (values)
|
||
; (let ((length-prev-line
|
||
; (string-length
|
||
; (list-ref text (- pos-line 2)))))
|
||
; (set! can-write #f)
|
||
; (set! pos-line (- pos-line 1))
|
||
; (set! pos-col (+ length-prev-line 2)))))
|
||
|
||
;; FIXME
|
||
;; move cursor to next line Ctrl-n, keycode 141
|
||
; ((= ch 141)
|
||
; (let ((last-pos (length text)))
|
||
; (if (>= pos-line last-pos)
|
||
; (values)
|
||
; (let ((length-next-line
|
||
; (string-length
|
||
; (list-ref text pos-line))))
|
||
; (begin
|
||
; (set! pos-col (+ length-next-line 2))
|
||
; (set! pos-line (+ pos-line 1))
|
||
; (if (= pos-line last-pos)
|
||
; (set! can-write #t)))))))
|
||
|
||
;; CursorLeft
|
||
((and (= ch key-left)
|
||
(> (buffer-pos-col buffer) first-column))
|
||
(regress-buffer-cursor-column! buffer))
|
||
|
||
;; CursorRight
|
||
((= ch key-right)
|
||
(let ((line-length (string-length
|
||
(buffer-text-current-line buffer))))
|
||
(if (< (buffer-pos-col buffer) (+ line-length first-column))
|
||
(advance-buffer-cursor-column! buffer))))
|
||
|
||
;;Ctrl+a -> Pos 1
|
||
((= ch 1)
|
||
(set-buffer-pos-col! buffer first-column))
|
||
|
||
;;Ctrl-e -> End
|
||
((= ch 5)
|
||
(let ((line-length (string-length
|
||
(buffer-text-current-line buffer))))
|
||
(set-buffer-pos-col! buffer
|
||
(+ line-length first-column))))
|
||
|
||
;; Ctrl+k -> Zeile l<>schen
|
||
((and (= ch 11)
|
||
(buffer-can-write buffer))
|
||
(let ((text-front (sublist (buffer-text buffer)
|
||
0 (- (length (buffer-text buffer)) 1))))
|
||
(set-buffer-text! buffer (append text-front '("")))
|
||
(set-buffer-pos-col! buffer first-column)))
|
||
|
||
;; forward in command history -- CursorDown
|
||
((and (= ch key-down)
|
||
(buffer-can-write buffer))
|
||
(if (< (buffer-history-pos buffer)
|
||
(- (length (buffer-text buffer)) 1))
|
||
(set-buffer-history-pos! buffer
|
||
(+ (buffer-history-pos buffer) 1)))
|
||
(let ((rest (sublist (buffer-text buffer) 0
|
||
(- (length (buffer-text buffer)) 1)))
|
||
(hist (if (= (buffer-history-pos buffer)
|
||
(- (length (buffer-text buffer)) 1))
|
||
""
|
||
(list-ref (buffer-text buffer)
|
||
(buffer-history-pos buffer)))))
|
||
(set-buffer-text! buffer (append rest (list hist)))
|
||
(let ((line-length
|
||
(string-length
|
||
(list-ref (buffer-text buffer)
|
||
(- (length (buffer-text buffer)) 1)))))
|
||
(set-buffer-pos-col! buffer (+ line-length first-column)))))
|
||
|
||
;; back in command history -- CursorUp
|
||
((and (= ch key-up)
|
||
(buffer-can-write buffer))
|
||
(if (> (buffer-history-pos buffer) 0)
|
||
(set-buffer-history-pos! buffer
|
||
(- (buffer-history-pos buffer) 1)))
|
||
(let ((rest (sublist (buffer-text buffer) 0
|
||
(- (length (buffer-text buffer)) 1)))
|
||
(hist (list-ref (buffer-text buffer)
|
||
(buffer-history-pos buffer))))
|
||
(set-buffer-text! buffer
|
||
(append rest (list hist))))
|
||
(let ((line-length (string-length
|
||
(list-ref (buffer-text buffer)
|
||
(- (length (buffer-text buffer)) 1)))))
|
||
(set-buffer-pos-col! buffer (+ line-length first-column))))
|
||
|
||
((and (buffer-can-write buffer) (<= ch 255))
|
||
(append-char-to-buffer! ch buffer)
|
||
(advance-buffer-cursor-column! buffer))
|
||
|
||
(values)))
|
||
|
||
;;print content of the buffer into the specified window
|
||
(define (print-command-buffer win buffer)
|
||
(call-with-values
|
||
(lambda ()
|
||
(get-right-command-lines buffer))
|
||
(lambda (lines new-pos-fin-ln)
|
||
(set-buffer-pos-fin-ln! buffer new-pos-fin-ln)
|
||
(let lp ((lines lines) (line-count 0))
|
||
(if (or (null? lines)
|
||
(> line-count (buffer-num-lines buffer)))
|
||
(values)
|
||
(begin
|
||
(mvwaddstr win line-count 1 (car lines))
|
||
(lp (cdr lines) (+ line-count 1))))))))
|
||
|
||
;;compute the visible lines
|
||
(define (get-right-command-lines buffer)
|
||
(call-with-values
|
||
(lambda ()
|
||
(all-commands-seperated buffer))
|
||
(lambda (all-lines-seperated pos-fin-ln)
|
||
(let ((num-all-lines (length all-lines-seperated))
|
||
(num-lines (buffer-num-lines buffer)))
|
||
(if (>= pos-fin-ln num-lines)
|
||
;;aktive Zeile ist die unterste
|
||
(values (sublist all-lines-seperated
|
||
(- pos-fin-ln num-lines)
|
||
num-lines)
|
||
pos-fin-ln)
|
||
(if (<= num-all-lines num-lines)
|
||
;;noch keine ganze Seite im Buffer
|
||
(values (prepare-lines all-lines-seperated
|
||
num-lines (- pos-fin-ln 1))
|
||
pos-fin-ln)
|
||
;;scrollen auf der ersten Seite
|
||
(values (sublist all-lines-seperated 0 num-lines)
|
||
pos-fin-ln)))))))
|
||
|
||
;;seperate all statements
|
||
(define (all-commands-seperated buffer)
|
||
(let ((num-cols (buffer-num-cols buffer))
|
||
(pos-fin-ln (buffer-pos-fin-ln buffer))
|
||
(commands (buffer-text buffer)))
|
||
(let loop ((act-pos 1) (new '()))
|
||
(if (= act-pos (buffer-pos-line buffer))
|
||
(let* ((length-new (length new))
|
||
(first-el-old (list-ref commands (- act-pos 1)))
|
||
(seperated-act (seperate-line-com
|
||
first-el-old num-cols))
|
||
(length-act (length seperated-act)))
|
||
(set! pos-fin-ln (+ length-new length-act))))
|
||
|
||
(if (> act-pos (length commands))
|
||
(values (reverse new) pos-fin-ln)
|
||
(let* ((first-el-old (list-ref commands (- act-pos 1)))
|
||
(seperated-fst-el-old
|
||
(seperate-line-com first-el-old num-cols)))
|
||
(loop (+ act-pos 1) (append seperated-fst-el-old new)))))))
|
||
|
||
;;seperate one statement
|
||
(define (seperate-line-com line width)
|
||
(let loop ((new '())
|
||
(old line))
|
||
(if (> width (string-length old))
|
||
(if (= 0 (string-length old))
|
||
(if (equal? new '())
|
||
(add-prompts '(""))
|
||
(add-prompts new))
|
||
;new
|
||
(add-prompts (append (list old) new)))
|
||
;(append (list old) new))
|
||
(let ((next-line (substring old 0 width))
|
||
(rest-old (substring old width (string-length old))))
|
||
(loop (cons next-line new) rest-old)))))
|
||
|
||
;;add ">"
|
||
(define (add-prompts l)
|
||
(let* ((lr (reverse l))
|
||
(old-first-el (list-ref lr 0))
|
||
(new-first-el (string-append ">" old-first-el))
|
||
(old-rest (list-tail lr 1)))
|
||
(let loop ((old old-rest)
|
||
(new (list new-first-el)))
|
||
(if (> (length old) 0)
|
||
(let* ((old-first-el (list-ref old 0))
|
||
(new-first-el (string-append " " old-first-el)))
|
||
(loop (list-tail old 1) (append new (list new-first-el))))
|
||
(reverse new)))))
|
||
|
||
;;Find the lines to print
|
||
(define (prepare-lines l height pos)
|
||
(if (< (length l) height)
|
||
;; Liste zu kurz -> ""s hinzuf<75>gen
|
||
(let loop ((tmp-list l))
|
||
(if (= height (length tmp-list))
|
||
tmp-list
|
||
(loop (append tmp-list (list "")))))
|
||
;; Teilliste holen
|
||
(if (< pos height)
|
||
;;pos nicht ganz unten
|
||
(sublist l 0 height)
|
||
;;standard-Fall
|
||
(sublist l (- pos height) height))))
|
||
|
||
;;Cursor
|
||
;;Put Cursor to the right position
|
||
(define (cursor-right-pos win buffer)
|
||
(let ((text-index (- (buffer-pos-col buffer) first-column))
|
||
(pos-fin-ln (buffer-pos-fin-ln buffer))
|
||
(pos-x (buffer-pos-x buffer))
|
||
(num-lines (buffer-num-lines buffer))
|
||
(num-cols (buffer-num-cols buffer)))
|
||
|
||
;; y position
|
||
(let* ((item-length (string-length (buffer-text-current-line buffer)))
|
||
(no-wrapped-lines (quotient item-length num-cols))
|
||
(first-line-offset (quotient text-index num-cols))
|
||
(new-y (- (+ (- pos-fin-ln no-wrapped-lines)
|
||
first-line-offset)
|
||
1)))
|
||
; (debug-message "num-cols "
|
||
; num-cols
|
||
; " no-wrapped-lines "
|
||
; no-wrapped-lines
|
||
; " first-line-offset "
|
||
; first-line-offset
|
||
; " new-y " new-y
|
||
; " length "
|
||
; item-length
|
||
; " pos-fin-ln "
|
||
; pos-fin-ln
|
||
; " text-index "
|
||
; text-index
|
||
; " pos-line "
|
||
; (buffer-pos-line buffer)
|
||
; " num-lines "
|
||
; num-lines)
|
||
|
||
; (if (and (not (zero? text-index))
|
||
; (zero? (remainder text-index num-cols))
|
||
; (= text-index item-length))
|
||
; (set-buffer-pos-y! buffer (+ new-y 1))
|
||
; (set-buffer-pos-y! buffer new-y))
|
||
|
||
|
||
(cond
|
||
((and (>= new-y num-lines)
|
||
(not (zero? text-index))
|
||
(zero? (remainder text-index num-cols))
|
||
(= text-index item-length))
|
||
(set-buffer-pos-y! buffer num-lines))
|
||
((and (>= new-y num-lines)
|
||
(< text-index item-length))
|
||
(set-buffer-pos-y! buffer
|
||
(- num-lines
|
||
(- (+ no-wrapped-lines 1)
|
||
first-line-offset))))
|
||
((>= new-y num-lines)
|
||
(set-buffer-pos-y! buffer (- num-lines 1)))
|
||
((and (not (zero? text-index))
|
||
(zero? (remainder text-index num-cols))
|
||
(= text-index item-length))
|
||
(set-buffer-pos-y! buffer (+ new-y 1)))
|
||
(else
|
||
(set-buffer-pos-y! buffer new-y)))
|
||
|
||
;; x position
|
||
(set-buffer-pos-x! buffer (+ first-column
|
||
(modulo text-index num-cols)))
|
||
|
||
; (debug-message "cursor position "
|
||
; (buffer-pos-x buffer)
|
||
; " "
|
||
; (buffer-pos-y buffer))
|
||
; (debug-message "num-cols " num-cols
|
||
; " pos-col " pos-col
|
||
; " text '" (buffer-text-current-line buffer) "'")
|
||
|
||
(wmove win (buffer-pos-y buffer) (buffer-pos-x buffer)))))
|
||
|
||
(define (advance-buffer-cursor-column! buffer)
|
||
(set-buffer-pos-col! buffer
|
||
(+ 1 (buffer-pos-col buffer))))
|
||
|
||
(define (regress-buffer-cursor-column! buffer)
|
||
(set-buffer-pos-col! buffer
|
||
(- (buffer-pos-col buffer) 1)))
|
||
|
||
;; add one character to the buffer
|
||
(define (append-char-to-buffer! ch buffer)
|
||
(let* ((text (buffer-text buffer))
|
||
(pos-col (buffer-pos-col buffer))
|
||
(last-pos (- (length text) 1))
|
||
(old-last-el (list-ref text last-pos))
|
||
(old-rest (sublist text 0 last-pos))
|
||
(before-ch (substring old-last-el 0
|
||
(max 0 (- pos-col 2))))
|
||
(after-ch (substring old-last-el
|
||
(max 0 (- pos-col 2))
|
||
(string-length old-last-el)))
|
||
(new-last-el (string-append before-ch
|
||
(string (ascii->char ch))
|
||
after-ch)))
|
||
(set-buffer-text! buffer
|
||
(append old-rest (list new-last-el)))))
|
||
|
||
;;Remove one character from the line (backspace)
|
||
(define remove-from-command-buffer
|
||
(lambda (text pos-col)
|
||
(let* ((last-pos (- (length text) 1))
|
||
(old-last-el (list-ref text last-pos))
|
||
(old-rest (sublist text 0 last-pos))
|
||
(before-ch (substring old-last-el 0
|
||
(max 0 (- pos-col 3))))
|
||
(after-ch (if (= pos-col
|
||
(+ (string-length old-last-el) 2))
|
||
""
|
||
(substring old-last-el
|
||
(max 0 (- pos-col 2))
|
||
(string-length old-last-el))))
|
||
(new-last-el (if (= pos-col
|
||
(+ (string-length old-last-el) 2))
|
||
before-ch
|
||
(string-append before-ch after-ch))))
|
||
(append old-rest (list new-last-el)))))
|
||
|
||
(define (sublist l pos k)
|
||
(let ((tmp (list-tail l pos)))
|
||
(reverse (list-tail (reverse tmp)
|
||
(- (length tmp) k)))))
|
||
|
||
;;Create a fitting buffer for a window with box and a welcome-Message
|
||
;;If the message is "", the buffer starts in line one
|
||
(define (make-buffer-welcome height width welcome-message)
|
||
(if (string=? "" welcome-message)
|
||
(make-buffer (list "") 1 2 1 1 2 (- height 2) (- width 3) #t 1)
|
||
(make-buffer (list welcome-message "") 2 2 2 2 2
|
||
(- height 2) (- width 3) #t 1) ))
|
||
|
||
|