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

Re: Passwords



>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.

By specializing VIEW-KEY-EVENT-HANDLER instead of KEYSTROKE-FUNCTION,
you can make all the "complex" commands work as well. I also
changed your class to inherit directly from FRED-DIALOG-ITEM
instead of EDITABLE-TEXT-DIALOG-ITEM since EDITABLE-TEXT-DIALOG-ITEM
is intended to be redefineable to inherit from TEXT-EDIT-DIALOG-ITEM
(defined in an example file).

-------------------------------------------------------------------------

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


(defclass password-text-dialog-item (fred-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))
  (call-next-method))

(defmethod view-key-event-handler ((item password-text-dialog-item) char)
  ;; the secret here is to realize that the only user-distinguishable
  ;; properties of the visible part of the password-dialog-item are
  ;; the number of bullets and the selection. We actually process
  ;; the keystroke in the alter-ego, then fix up the visible version
  ;; to have the same number of characters and the same selection.
  ;; Note that there are some security problems with this: c-s works,
  ;; as do cut and paste (you can cut from the password dialog and
  ;; paste somewhere else).
  (let* ((alter-ego (password-text-alter-ego item))
         (echo-char (password-text-echo-char item)))
    (view-key-event-handler alter-ego char)
    (let* ((alter-length (buffer-size (fred-buffer alter-ego)))
           (buf (fred-buffer item))
           (length (buffer-size buf)))
      (if (> alter-length length)
        (dotimes (i (- alter-length length))
          (buffer-insert buf echo-char))
        (buffer-delete buf alter-length length))
      (multiple-value-bind (start end) (selection-range alter-ego)
        (set-selection-range item start end)))))
          

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

  ;; 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)
                                       ""
                                       nil
                                       :allow-returns nil)))

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