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

One's own RUBOUT

    Date: Thu, 13 Jun 1991 00:12 EDT
    From: kddlab!atr-la.atr.co.jp!myers@uunet.UU.NET (John K. Myers)

    I'm implementing a simple real-time interactor on top of (1) a Listener,
    and (2) my own graphics window (a la MacWrite) on the Symbolics.  I need 
    a Rubout.  The obvious method is to print a #\backspace and then do
    (send window :clear-char).  Clear-char does not seem to do anything--
    why not?  Other solutions, such as :clear-rest-of-line, mess with the
    graphics in the graphics window.  Why doesn't clear-char work?  
    Is there an easy way to get backspace to linewrap on a Listener,
    without having to record pixel locations of the previous line and
    do cursor controls? [I'm using (read-char-no-hang) for input, and
    format for output.]

Especially if you're only using Symbolics systems, and perhaps even if
you're not, why are you implementing your own rubout handling?  We
provide tools which allow you to insulate yourself from rubout handling
by letting you define your parser in an input-editing-independent fashion
and then call into an input editor as a separate mechanism.  e.g.,

  (defun read-pair () 
    (labels ((fail (&optional (msg "Bad syntax") &rest args)
	       (apply #'sys:parse-ferror msg args))
	     (parse-number (ch)
	       (let ((l '()) (n 1))
		 (when (member (peek-char nil) '(#\- #\+)) 
		   (incf n) (push (read-char) l))
		 (loop (push (read-char) l)
		       (unless (digit-char-p (car l))
			 (unless (char= (car l) ch) (fail "Missing ~A" ch))
			 (unless (nthcdr n l) (fail "Missing digits"))
			 (return (values (parse-integer 
					   (coerce (nreverse (cdr l)) 'string))
					 (car l))))))))
      (unless (char= (read-char) #\[) (fail "Missing ["))
      (list (parse-number #\,) (parse-number #\]))))
  ;; Look, Ma, no rubout handling here.  Better type your input
  ;; right the first time here or you'll be in the debugger.
 => (34 5)
  ;; But, voila, if you just wrap this in WITH-INPUT-EDITING,
  ;; you can magically get rubout processing and other simple 
  ;; input editing without any hassle. e.g.,
  (with-input-editing () (read-pair))[34,5<RUBOUT><RUBOUT>6,5]
 => (36 5)

If you're writing something big like a text editor, this doesn't work,
but if the nature of your interaction is small, this paradigm is ideal.

If your problem -really- calls for something home-grown, here's the workaround
you need.

The actual bug is that the code which computes the bounds of a presentation
box assumes it can do a minds-eye retracing of the output, and that the place
where the cursor last was is the max edge of the box.  When you write Backspace,
this moves the cursor back and :clear-char has an optimization that tricks it
into believing that there is no char there because it thinks it's at the right
edge and it doesn't see any way there could be anything farther out.  (e.g.,
try (present "foo" 'string) and then (accept 'string) and wave the mouse
over the string.  Notice that the box it draws around the presented string is not
as wide as the string's max width.  I'm pretty sure this is a manifestation of the
same bug.)

I'll report this as a bug, although since it's pretty rare I can't say how easy
it will be to get resources to fix it. Nevertheless, even though this:

  (macrolet ((slowly (form) `(progn (sleep 1) (beep) ,form)))
    (slowly (write-char #\x))
    (slowly (write-char #\y))
    (slowly (write-char #\Backspace))
    (slowly (send *standard-output* :clear-char #\y))
    (slowly (write-char #\z)))

(which I presume characterizes your problem) does not work, the following
should work just fine as a workaround.

  (macrolet ((slowly (form) `(progn (sleep 1) (beep) ,form)))
    (slowly (write-char #\x))
    (multiple-value-bind (x1 y1)
	(send *standard-output* :read-cursorpos)
      (slowly (write-char #\y))
      (slowly (send *standard-output* :set-cursorpos x1 y1))
      (slowly (send *standard-output* :clear-char #\y))
      (slowly (write-char #\z))))

Also, regarding the issue of wrapping back up a line, there's nothing to
do it all automatically, but it's not necessarily as bad as recording
the output cursorpos at every line break.  If you're buffering the input
and know where either the next line or the full input started from, you 
can either search back in the input for the previous line break, or work
from the beginning of the input, and then compute the cursor motion that
would have had to have been done for that input by using the :string-length
message to the window.  It's a pretty cool message and returns all kinds of
amazingly interesting stuff--much more than just the string's length.
That way, the cursorpos info (other than the initial position) doesn't
have to be explicitly stored.

Hope this helps.