;;;; ;;;; Entries bindings and procs ;;;; ;;;; Copyright © 1993-1996 Erick Gallesio - I3S-CNRS/ESSI ;;;; ;;;; Permission to use, copy, and/or distribute this software and its ;;;; documentation for any purpose and without fee is hereby granted, provided ;;;; that both the above copyright notice and this permission notice appear in ;;;; all copies and derived works. Fees for distribution or use of this ;;;; software or derived works may only be charged with express written ;;;; permission of the copyright holder. ;;;; This software is provided ``as is'' without express or implied warranty. ;;;; ;;;; This software is a derivative work of other copyrighted softwares; the ;;;; copyright notices of these softwares are placed in the file COPYRIGHTS ;;;; ;;;; ;;;; Author: Erick Gallesio [eg@unice.fr] ;;;; Creation date: 17-May-1993 12:35 ;;;; Last file update: 2-Jul-1996 13:00 ;;;; ;; ---------------------------------------------------------------------- ;; Class bindings for entry widgets. ;; ---------------------------------------------------------------------- ;;------------------------------------------------------------------------- ;; Elements of tkPriv that are used in this file: ;; ;; after-id - If non-null, it means that auto-scanning is underway ;; and it gives the "after" id for the next auto-scan ;; command to be executed. ;; mouse-moved - Non-zero means the mouse has moved a significant ;; amount since the button went down (so, for example, ;; start dragging out a selection). ;; press-x - X-coordinate at which the mouse button was pressed. ;; select-mode - The style of selection currently underway: ;; char, word, or line. ;; x, y - Last known mouse coordinates for scanning ;; and auto-scanning. ;;------------------------------------------------------------------------- (let() ;; ;; Utilities ;; (define (Tk:word-character c) (or (char-alphabetic? c) (char-numeric? c) (char=? c #\-) (char=? c #\*))) (define (Tk:word-separator c) (not (Tk:word-character c))) (define (Tk:end-of-word str index . separator) (let ((len (string-length str)) (sep (if (null? separator) Tk:word-separator separator))) (let loop ((i index)) (cond ((= len 0) 0) ((>= i len) len) ((sep (string-ref str i)) (if (= index i) (+ i 1) i)) (ELSE (loop (+ i 1))))))) (define (Tk:beginning-of-word str index . separator) (let ((len (string-length str)) (sep (if (null? separator) Tk:word-separator separator))) (let loop ((i index)) (cond ((= len 0) 0) ((= i -1) 0) ((>= i len) (loop (- len 1))) ((sep (string-ref str i)) (if (= index i) i (+ i 1))) (ELSE (loop (- i 1))))))) ;; Tk:entry-clipboard-keysyms ;; This procedure is invoked to identify the keys that correspond to ;; the "copy", "cut", and "paste" functions for the clipboard. ;; ;; Arguments: ;; copy - Name of the key (keysym name plus modifiers, if any, ;; such as "Meta-y") used for the copy operation. ;; cut - Name of the key used for the cut operation. ;; paste - Name of the key used for the paste operation. (define (Tk:entry-clipboard-keysyms copy cut paste) (define-binding "Entry" copy (|W|) (when (equal? [selection 'own :displayof |W|] (widget->string |W|)) (clipboard 'clear :displayof |W|) (catch (clipboard 'append :displayof |W| (selection 'get :displayof |W|))))) (define-binding "Entry" cut (|W|) (when (equal? [selection 'own :displayof |W|] (widget->string |W|)) (clipboard 'clear :displayof |W|) (catch (clipboard 'append :displayof |W| (selection 'get :displayof |W|)) (|W| 'delete 'sel.first 'sel.last)))) (define-binding "Entry" paste (|W|) (catch (|W| 'insert 'insert (selection 'get :displayof |W| :selection "CLIPBOARD")))) ) ;; Tk:entry-button-1 -- ;; This procedure is invoked to handle button-1 presses in "Entry" ;; widgets. It moves the insertion cursor, sets the selection anchor, ;; and claims the input focus. ;; ;; Arguments: ;; w - The "Entry" window in which the button was pressed. ;; x - The x-coordinate of the button press. (define (Tk:entry-button-1 w x) (let ((pos (format #f "@~A" x))) (set! tk::select-mode "char") (set! tk::mouse-moved #f) (set! tk::press-x x) (w 'icursor (Tk:entry-closest-gap w x)) (w 'selection 'from 'insert) (if (equal? (tk-get w :state) "normal") (focus w)))) ;; Tk:entry-mouse-select -- ;; This procedure is invoked when dragging out a selection with ;; the mouse. Depending on the selection mode (character, word, ;; line) it selects in different-sized units. This procedure ;; ignores mouse motions initially until the mouse has moved from ;; one character to another or until there have been multiple clicks. ;; ;; Arguments: ;; w - The "Entry" window in which the button was pressed. ;; x - The x-coordinate of the mouse. (define (Tk:entry-mouse-select w x) (let* ((cur (Tk:entry-closest-gap w x)) (anchor (w 'index 'anchor))) (if (or (equal? cur anchor) (>= (abs (- tk::press-x x)) 3)) (set! tk::mouse-moved #t)) (cond ((string=? tk::select-mode "char") (if tk::mouse-moved (cond ((< cur anchor) (w 'selection 'range cur anchor)) ((> cur anchor) (w 'selection 'range anchor cur)) (ELSE (w 'selection 'clear))))) ((string=? tk::select-mode "word") (if (< cur (w 'index 'anchor)) (w 'selection 'range (Tk:beginning-of-word (w 'get) cur) (Tk:end-of-word (w 'get) (- anchor 1))) (w 'selection 'range (Tk:beginning-of-word (w 'get) anchor) (Tk:end-of-word (w 'get) (- cur 1))))) ((string=? tk::select-mode "line") (w 'selection 'range 0 'end))) (update 'idletasks))) ;; Tk:entry-auto-scan -- ;; This procedure is invoked when the mouse leaves an "Entry" window ;; with button 1 down. It scrolls the window left or right, ;; depending on where the mouse is, and reschedules itself as an ;; "after" command so that the window continues to scroll until the ;; mouse moves back into the window or the mouse button is released. ;; ;; Arguments: ;; w - The "Entry" window. (define (Tk:entry-auto-scan w) (when (winfo 'exists w) (let ((x tk::x)) (if (>= x (winfo 'width w)) (begin (w 'xview 'scroll 2 'units) (Tk:entry-mouse-select w x)) (if (< x 0) (w 'xview 'scroll -2 'units) (Tk:entry-mouse-select w x))) (set! tk::after-id (after 50 (lambda () (Tk:entry-auto-scan w))))))) ;; Tk:entry-key-select -- ;; This procedure is invoked when stroking out selections using the ;; keyboard. It moves the cursor to a new position, then extends ;; the selection to that position. ;; ;; Arguments: ;; w - The "Entry" window. ;; new - A new position for the insertion cursor (the cursor hasn't ;; actually been moved to this position yet). (define (Tk:entry-key-select w new) (if (w 'selection 'present) (w 'selection 'adjust new) (begin (w 'selection 'from 'insert) (w 'selection 'to new))) (w 'icursor new)) ;; Tk:entry-Insert -- ;; Insert a string into an "Entry" at the point of the insertion cursor. ;; If there is a selection in the "Entry", and it covers the point of the ;; insertion cursor, then delete the selection before inserting. ;; ;; Arguments: ;; w - The "Entry" window in which to insert the string ;; s - The string to insert (usually just a single character) (define (Tk:entry-insert w s) (unless (equal? s "") (let ((insert (w 'index 'insert))) (catch (if (and (<= (w 'index 'sel.first) insert) (>= (w 'index 'sel.last) insert)) (w 'delete 'sel.first 'sel.last)))) (w 'insert 'insert s) (Tk:entry-see-insert w))) ;; Tk:entry-backspace -- ;; Backspace over the character just before the insertion cursor. ;; If backspacing would move the cursor off the left edge of the ;; window, reposition the cursor at about the middle of the window. ;; ;; Arguments: ;; w - The "Entry" window in which to backspace. (define (Tk:entry-backspace w) (if (w 'selection 'present) (w 'delete 'sel.first 'sel.last) (let ((x (- (w 'index 'insert) 1))) (if (>= x 0) (w 'delete x)) (when (>= (w 'index "@0") (w 'index 'insert)) (let* ((range (w 'xview)) (left (car range)) (right (cadr range))) (w 'xview 'moveto (- left (/ (- right left) 2.0)))))))) ;; Tk:entry-see-insert -- ;; Make sure that the insertion cursor is visible in the "Entry" window. ;; If not, adjust the view so that it is. ;; ;; Arguments: ;; w - The "Entry" window. (define (Tk:entry-see-insert w) (let ((c (w 'index 'insert)) (left (w 'index "@0"))) (if (> left c) (w 'xview c) (let ((x (winfo 'width w))) (while (and (<= (w 'index (format #f "@~A" x)) c) (< left c)) (set! left (+ left 1)) (w 'xview left)))))) ;; Tk:entry-set-cursor - ;; Move the insertion cursor to a given position in an "Entry". Also ;; clears the selection, if there is one in the "Entry", and makes sure ;; that the insertion cursor is visible. ;; ;; Arguments: ;; w - The "Entry" window. ;; pos - The desired new position for the cursor in the window. (define (Tk:entry-set-cursor w pos) (w 'icursor pos) (w 'selection 'clear) (Tk:entry-see-insert w)) ;; Tk:entry-Transpose - ;; This procedure implements the "transpose" function for "Entry" widgets. ;; It tranposes the characters on either side of the insertion cursor, ;; unless the cursor is at the end of the line. In this case it ;; transposes the two characters to the left of the cursor. In either ;; case, the cursor ends up to the right of the transposed characters. ;; ;; w - The "Entry" window. (define (Tk:entry-transpose w) (let ((i (w 'index 'insert))) (if (< i (w 'index 'end)) (set! i (+ i 1))) (let ((first (- i 2))) (if (>= first 0) (let* ((str (w 'get)) (new (string (string-ref str (- i 1)) (string-ref str first)))) (w 'delete first i) (w 'insert 'insert new) (Tk:entry-see-insert w)))))) ;; Tk:entry-closest-gap -- ;; Given x and y coordinates, this procedure finds the closest boundary ;; between characters to the given coordinates and returns the index ;; of the character just after the boundary. ;; ;; w - The entry window. ;; x - X-coordinate within the window. (define (Tk:entry-closest-gap w x) (let* ((pos (w 'index (format #f "@~A" x))) (bbox (w 'bbox pos))) (if (< [- x (list-ref bbox 0)] (/ (list-ref bbox 2) 2)) pos (+ pos 1)))) ;; Tk:entry-paste -- ;; This procedure sets the insertion cursor to the current mouse position, ;; pastes the selection there, and sets the focus to the window. ;; ;; w - The entry window. ;; x - X position of the mouse. (define (Tk:entry-paste w x) (w 'icursor (Tk:entry-closest-gap w x)) (catch (w 'insert 'insert (selection 'get :displayof w))) (if (string=? (tk-get w :state) "normal") (focus w))) ;;------------------------------------------------------------------------- ;; The code below creates the default class bindings for entries. ;;------------------------------------------------------------------------- ;; Standard Motif bindings: ;;------------------------------------------------------------------------- ;; The code below creates the default class bindings for entries. ;;------------------------------------------------------------------------- ;; Standard Motif bindings: (define-binding "Entry" "<1>" (|W| x) (Tk:entry-button-1 |W| x) (|W| 'selection 'clear)) (define-binding "Entry" "" (|W| x) (set! tk::x x) (Tk:entry-mouse-select |W| x)) (define-binding "Entry" "" (|W| x) (set! tk::select-mode "word") (Tk:entry-mouse-select |W| x) (catch (|W| 'icursor 'sel.first))) (define-binding "Entry" "" (|W| x) (set! tk::select-mode "line") (Tk:entry-mouse-select |W| x) (|W| 'icursor 0)) (define-binding "Entry" "" (|W| x) (set! tk::select-mode "char") (|W| 'selection 'adjust (format #f "@~A" x))) (define-binding "Entry" "" (|W| x) (set! tk::select-mode "word") (Tk:entry-mouse-select |W| x)) (define-binding "Entry" "" (|W| x) (set! tk::select-mode "line") (Tk:entry-mouse-select |W| x)) (define-binding "Entry" "" (|W| x) (set! tk::x x) (Tk:entry-auto-scan |W|)) (define-binding "Entry" "" () (Tk:cancel-repeat)) (define-binding "Entry" "" () (Tk:cancel-repeat)) (define-binding "Entry" "" (|W| x) (|W| 'icursor (format #f "@~A" x))) (define-binding "Entry" "" (|W|) (Tk:entry-set-cursor |W| (- (|W| 'index 'insert) 1))) (define-binding "Entry" "" (|W|) (Tk:entry-set-cursor |W| (+ (|W| 'index 'insert) 1))) (define-binding "Entry" "" (|W|) (Tk:entry-key-select |W| (- (|W| 'index 'insert) 1)) (Tk:entry-see-insert |W|)) (define-binding "Entry" "" (|W|) (Tk:entry-key-select |W| (+ (|W| 'index 'insert) 1)) (Tk:entry-see-insert |W|)) (define-binding "Entry" "" (|W|) (Tk:entry-set-cursor |W| (Tk:beginning-of-word (|W| 'get) (- (|W| 'index 'insert) 1)))) (define-binding "Entry" "" (|W|) (Tk:entry-set-cursor |W| (Tk:end-of-word (|W| 'get) (|W| 'index 'insert)))) (define-binding "Entry" "" (|W|) (Tk:entry-key-select |W| (Tk:beginning-of-word (|W| 'get) (- (|W| 'index 'insert) 1))) (Tk:entry-see-insert |W|)) (define-binding "Entry" "" (|W|) (Tk:entry-key-select |W| (Tk:end-of-word (|W| 'get) (|W| 'index 'insert))) (Tk:entry-see-insert |W|)) (define-binding "Entry" "" (|W|) (Tk:entry-set-cursor |W| 0)) (define-binding "Entry" "" (|W|) (Tk:entry-key-select |W| 0) (Tk:entry-see-insert |W|)) (define-binding "Entry" "" (|W|) (Tk:entry-set-cursor |W| 'end)) (define-binding "Entry" "" (|W|) (Tk:entry-key-select |W| 'end) (Tk:entry-see-insert |W|)) (define-binding "Entry" "" (|W|) (if (|W| 'selection 'present) (|W| 'delete 'sel.first 'sel.last) (|W| 'delete 'insert))) (define-binding "Entry" "" (|W|) (Tk:entry-backspace |W|)) (define-binding "Entry" "" (|W|) (|W| 'selection 'from 'insert)) (define-binding "Entry" "