[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


My thanks to Richard Lynch, Dale Fish, Stephen Davis, Danny Brewer et al.
for all their suggestions on how to do passwords. My final (so far) 
version is based on ideas from all, but primarily the idea of having
one real dialog-item that isn't shown and a display-only one that just
has the blobs. By capturing all the keystrokes and mouse clicks and 
applying them to both, I get an acceptable dialog-item which doesn't
suffer from refresh problems, allows almost all the flexibility of 
normal editable-text-dialog-items (complex commands don't work too 
well - but who needs them for passwords), and doesn't require 
special fonts or anything.

Here are the definitions I'm going to use...

Hope it's of interest, and of course if anyone improves on this, please
let me know.


;; The definition of a class of editable-text-dialog-item that doesn't
;; echo the characters entered.

(defclass password-text-dialog-item (editable-text-dialog-item)
  ;; the password-text-dialog-item has two extra attributes:
  ;; - the alter-ego is a regular editable-text-dialog-item which holds
  ;;   the true text
  ;; - the echo-char holds the character to be used for echoing. The
  ;;   default is the bullet character.
  ((alter-ego :initform nil 
              :initarg :alter-ego
              :accessor password-text-alter-ego)
   (echo-char :initform #\245
              :initarg :echo-char
              :accessor password-text-echo-char)))

(defmethod initialize-instance ((item password-text-dialog-item) &rest args)
  ;; this method creates a regular editable-text-dialog-item 
  ;; and stores it in the alter-ego slot.
  (declare (ignore args))
  (setf (password-text-alter-ego item)
        (make-instance 'editable-text-dialog-item))
(defmethod keystroke-function :before ((item password-text-dialog-item) 
                                       keystroke &optional comtab)
  ;; this is the clever bit!
  ;; whenever a keystroke is received for the password-text-dialog-item
  ;; it is sent to the alter-ego editable-text-dialog-item and then the
  ;; current-keystroke and current-character are changed to be the echo
  ;; character before proceeding. This has the result that the alter-ego
  ;; dialog-item has the correct text and the visible dialog-item has 
  ;; just the echo characters.

  ;; Note: there are problems with complicated keystrokes like meta-y
  ;; but I don't suppose anybody really wants meta-y in a password...
  ;; it may be because I set *current-keystroke* regardless of whether 
  ;; this is a self insert character or not...

  (declare (ignore comtab))
  (let* ((alter-ego (password-text-alter-ego item))
         (echo-char (password-text-echo-char item))
         (func (keystroke-function alter-ego keystroke)))
    (apply func (list alter-ego)))
  (setf *current-keystroke* echo-char)
  (setf *current-character* echo-char))

(defmethod view-click-event-handler :after ((item password-text-dialog-item) 

  ;; To handle the mouse, we have to see if the user has marked a region
  ;; or moved the insertion point. Fortunately, the functions 
  ;; selection-range and set-selection-range do both for us, so, whenever
  ;; the user uses the mouse, update the selection range and cursor 
  ;; position. This ensures that the user can delete a whole range etc.

  (declare (ignore where))
  (let ((alter-ego (password-text-alter-ego item)))
    (multiple-value-bind (position cursorpos)
                         (selection-range item)
      (set-selection-range alter-ego position cursorpos))))

(defmethod dialog-item-text ((item password-text-dialog-item))

  ;; this allows transparent access to the text - call this just
  ;; like for any dialog item, but it returns the correct text
  ;; from the alter-ego.
  (dialog-item-text (password-text-alter-ego item)))


(defun get-password ()
  ;; This is a simple example of the use of the password-text-dialog-item

  (let ((win (make-instance 'dialog
                            :window-type :double-edge-box 
                            :view-position :centered
                            :view-size #@(200 100)
                            :close-box-p nil
                            :view-font '("Chicago" 12 :SRCOR :PLAIN)))
        (password (make-dialog-item 'password-text-dialog-item
                                       #@(20 44)
                                       #@(133 16)
                                       :allow-returns nil)))

    (add-subviews win
                  (make-dialog-item 'static-text-dialog-item
                                    #@(16 14)
                                    #@(141 16)
                                    "Enter the password:"
                  (make-dialog-item 'button-dialog-item
                                    #@(91 81)
                                    #@(62 16)
                                         (dialog-item-text password)))
                                    :default-button t))
    (modal-dialog win)))