Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 415 lines (381 sloc) 15.677 kB
0bd9f60 Many bug fixes in Goatee. Rewrote buffer functions in terms of a
Timothy Moore authored
1 ;;; -*- Mode: Lisp; Package: GOATEE -*-
2
3 ;;; (c) copyright 2002 by Tim Moore (moore@bricoworks.com)
4 ;;; This library is free software; you can redistribute it and/or
5 ;;; modify it under the terms of the GNU Library General Public
6 ;;; License as published by the Free Software Foundation; either
7 ;;; version 2 of the License, or (at your option) any later version.
8 ;;;
9 ;;; This library is distributed in the hope that it will be useful,
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ;;; Library General Public License for more details.
13 ;;;
14 ;;; You should have received a copy of the GNU Library General Public
15 ;;; License along with this library; if not, write to the
16 ;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 ;;; Boston, MA 02111-1307 USA.
18
19 (in-package :goatee)
20
21 ;;; Interface between input-editing-streams and Goatee areas.
22
23 (defclass editing-stream-snapshot ()
24 ((buffer :reader stream-input-buffer
25 :initform (make-array 16 :adjustable t :fill-pointer 0))
26 (insertion-pointer :accessor stream-insertion-pointer :initform 0)
27 (scan-pointer :accessor stream-scan-pointer :initform 0)))
28
29 (defclass goatee-input-editing-mixin ()
30 ((area :accessor area :initarg :area)
31 (snapshot :accessor snapshot :initarg :snapshot
32 :initform (make-instance 'editing-stream-snapshot))))
33
6f5647d Accepting-values. It's ugly and has some cursor glitches but
Timothy Moore authored
34 (defmethod cursor-visibility ((stream goatee-input-editing-mixin))
35 (cursor-visibility (area stream)))
36
37 (defmethod (setf cursor-visibility) (visibility
38 (stream goatee-input-editing-mixin))
39 (setf (cursor-visibility (area stream)) visibility))
40
9207cae The most visible change is that argument prompts are displayed in
Timothy Moore authored
41 (defclass noise-extent (extent)
42 ()
43 (:documentation "Characters within the extent are input editor noise
44 strings. Eventually these should be read-only and atomic."))
45
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
46 (defclass accept-result-extent (extent)
47 ((object :accessor object :initarg :object)
8f79488 Don't define a slot accessor named TYPE
Timothy Moore authored
48 (result-type :accessor result-type :initarg :result-type))
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
49 (:documentation "The extent is read with a single read-gesture;
50 result is returned."))
51
0bd9f60 Many bug fixes in Goatee. Rewrote buffer functions in terms of a
Timothy Moore authored
52 ;;; Stream is the encapsulated stream
53 (defmethod initialize-instance :after ((obj goatee-input-editing-mixin)
2d7e10d Some cleanup of deleted Goatee area and screen-line methods.
Timothy Moore authored
54 &rest args
6f5647d Accepting-values. It's ugly and has some cursor glitches but
Timothy Moore authored
55 &key stream (initial-contents "")
2d7e10d Some cleanup of deleted Goatee area and screen-line methods.
Timothy Moore authored
56 (cursor-visibility t)
57 (background-ink
45da96f Implemented :ALIGN-PROMPTS in ACCEPTING-VALUES. This was harder than
Timothy Moore authored
58 (medium-background stream))
59 single-line)
0bd9f60 Many bug fixes in Goatee. Rewrote buffer functions in terms of a
Timothy Moore authored
60 (multiple-value-bind (cx cy)
61 (stream-cursor-position stream)
6f5647d Accepting-values. It's ugly and has some cursor glitches but
Timothy Moore authored
62 (let ((max-width (- (stream-text-margin stream) cx)))
63 ;; XXX hack to give area a fixed size rectangle that can be highlighted
45da96f Implemented :ALIGN-PROMPTS in ACCEPTING-VALUES. This was harder than
Timothy Moore authored
64 (with-output-recording-options (stream :record t)
6f5647d Accepting-values. It's ugly and has some cursor glitches but
Timothy Moore authored
65 (draw-rectangle* stream cx cy
66 (+ cx max-width) (+ cy (stream-line-height stream))
2d7e10d Some cleanup of deleted Goatee area and screen-line methods.
Timothy Moore authored
67 :ink background-ink
68 :filled t))
45da96f Implemented :ALIGN-PROMPTS in ACCEPTING-VALUES. This was harder than
Timothy Moore authored
69 (climi::with-keywords-removed (args (:initial-contents :single-line))
2d7e10d Some cleanup of deleted Goatee area and screen-line methods.
Timothy Moore authored
70 (setf (area obj)
71 (apply #'make-instance
72 'simple-screen-area
73 :area-stream stream
74 :buffer (make-instance 'editable-buffer
45da96f Implemented :ALIGN-PROMPTS in ACCEPTING-VALUES. This was harder than
Timothy Moore authored
75 :initial-contents
76 initial-contents
77 :newline-character (if single-line
78 nil
79 #\Newline))
2d7e10d Some cleanup of deleted Goatee area and screen-line methods.
Timothy Moore authored
80 :x-position cx
81 :y-position cy
82 :cursor-visibility cursor-visibility
83 :max-width max-width
84 :allow-other-keys t
85 args)))
45da96f Implemented :ALIGN-PROMPTS in ACCEPTING-VALUES. This was harder than
Timothy Moore authored
86 ;; XXX Really add it here?
6f5647d Accepting-values. It's ugly and has some cursor glitches but
Timothy Moore authored
87 (stream-add-output-record stream (area obj))
45da96f Implemented :ALIGN-PROMPTS in ACCEPTING-VALUES. This was harder than
Timothy Moore authored
88 #+nil (redisplay-area (area obj))
6f5647d Accepting-values. It's ugly and has some cursor glitches but
Timothy Moore authored
89 ;; initialize input-editing-stream state to conform to our reality
2d7e10d Some cleanup of deleted Goatee area and screen-line methods.
Timothy Moore authored
90 (make-input-editing-stream-snapshot obj (area obj)))))
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
91
92 (defvar climi::*noise-string-start*)
93 (defvar climi::*noise-string*)
94
0bd9f60 Many bug fixes in Goatee. Rewrote buffer functions in terms of a
Timothy Moore authored
95 (defun make-input-editing-stream-snapshot (snapshot area)
9207cae The most visible change is that argument prompts are displayed in
Timothy Moore authored
96 (let ((buffer (buffer area))
97 (input-buffer (stream-input-buffer snapshot)))
98 (multiple-value-bind (point-line point-pos)
0bd9f60 Many bug fixes in Goatee. Rewrote buffer functions in terms of a
Timothy Moore authored
99 (point* buffer)
9207cae The most visible change is that argument prompts are displayed in
Timothy Moore authored
100 (setf (fill-pointer input-buffer) 0)
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
101 (map-over-region
102 #'(lambda (line pos)
103 (let ((noise nil))
104 (map-over-extents-at-location*
105 #'(lambda (extent line pos)
106 (cond ((typep extent 'noise-extent)
107 (if (and (eq line (line (bp-start extent)))
108 (eql pos (pos (bp-start extent))))
109 (setq noise climi::*noise-string-start*)
110 (setq noise climi::*noise-string*)))
111 ((typep extent 'accept-result-extent)
112 (if (and (eq line (line (bp-start extent)))
113 (eql pos (pos (bp-start extent))))
114 (setq noise extent)
115 (setq noise climi::*noise-string*)))))
116 line
117 pos
118 :start-state :closed
119 :end-state :open)
120 (vector-push-extend (or noise (char-ref line pos))
121 input-buffer)))
122 buffer
123 (buffer-start buffer)
124 (buffer-end buffer))
9207cae The most visible change is that argument prompts are displayed in
Timothy Moore authored
125 (setf (stream-insertion-pointer snapshot)
126 (offset-location* buffer point-line point-pos)))))
0bd9f60 Many bug fixes in Goatee. Rewrote buffer functions in terms of a
Timothy Moore authored
127
abee9e4 Work in progress on the OpenGL backend, but nothing's working
Timothy Moore authored
128 (defmethod update-input-editing-stream ((stream goatee-input-editing-mixin))
129 (let ((area (area stream))
130 (snapshot (snapshot stream)))
131 (make-input-editing-stream-snapshot snapshot area)
132 (let ((first-mismatch (mismatch (stream-input-buffer snapshot)
133 (stream-input-buffer stream)))
134 (snapshot-buffer (stream-input-buffer snapshot))
135 (stream-buffer (stream-input-buffer stream)))
136 (setf (stream-insertion-pointer stream)
137 (stream-insertion-pointer snapshot))
138 (when (< (car (array-dimensions stream-buffer))
139 (fill-pointer snapshot-buffer))
140 (adjust-array stream-buffer (fill-pointer snapshot-buffer)))
141 (setf (fill-pointer stream-buffer) (fill-pointer snapshot-buffer))
142 (when (and first-mismatch
143 (>= (fill-pointer snapshot-buffer) first-mismatch))
144 (replace stream-buffer snapshot-buffer
145 :start1 first-mismatch
146 :start2 first-mismatch))
147 first-mismatch)))
148
0bd9f60 Many bug fixes in Goatee. Rewrote buffer functions in terms of a
Timothy Moore authored
149 (defmethod stream-process-gesture ((stream goatee-input-editing-mixin)
150 gesture
151 type)
b759183 added ignore/special declarations
Mike McDonald authored
152 (declare (ignore type))
d015fe5 Fixed presentation highlighting to do the right thing in the
Timothy Moore authored
153 (when (activation-gesture-p gesture)
154 (setf (stream-insertion-pointer stream)
155 (fill-pointer (stream-input-buffer stream)))
156 (set-editing-stream-insertion-pointer stream
157 (stream-insertion-pointer stream))
158 (setf (climi::activation-gesture stream) gesture)
159 (rescan-if-necessary stream)
160 (return-from stream-process-gesture gesture))
0bd9f60 Many bug fixes in Goatee. Rewrote buffer functions in terms of a
Timothy Moore authored
161 (let ((area (area stream))
162 (snapshot (snapshot stream)))
163 (execute-gesture-command gesture area *simple-area-gesture-table*)
d015fe5 Fixed presentation highlighting to do the right thing in the
Timothy Moore authored
164 (make-input-editing-stream-snapshot snapshot area)
165 (let ((first-mismatch (mismatch (stream-input-buffer snapshot)
166 (stream-input-buffer stream))))
167 (unwind-protect
168 (cond ((null first-mismatch)
169 ;; No change actually took place, event though IP may have
170 ;; moved.
171 nil)
172 ((< first-mismatch (stream-scan-pointer stream))
173 ;; Throw out. Buffer is still updated by protect forms
174 (immediate-rescan stream))
175 ((and (eql first-mismatch
176 (1- (stream-insertion-pointer snapshot)))
177 (eql (aref (stream-input-buffer snapshot) first-mismatch)
178 gesture))
179 ;; As best we can tell an insertion happened: one gesture was
180 ;; entered it was inserted in the buffer. There may be other
181 ;; changes above IP, but we don't care.
182 gesture)
183 (t
184 ;; Other random changes, but we want to allow more editing
185 ;; before scanning them.
186 (queue-rescan stream)
187 nil))
188 (let ((snapshot-buffer (stream-input-buffer snapshot))
189 (stream-buffer (stream-input-buffer stream)))
190 (setf (stream-insertion-pointer stream)
191 (stream-insertion-pointer snapshot))
192 (when (< (car (array-dimensions stream-buffer))
193 (fill-pointer snapshot-buffer))
194 (adjust-array stream-buffer (fill-pointer snapshot-buffer)))
195 (setf (fill-pointer stream-buffer) (fill-pointer snapshot-buffer))
196 (when (and first-mismatch
197 (>= (fill-pointer snapshot-buffer) first-mismatch))
198 (replace stream-buffer snapshot-buffer
199 :start1 first-mismatch
200 :start2 first-mismatch)))))))
ecc7441 Fixed a bug in frame-input-context-track-pointer that was causing a
Timothy Moore authored
201
963908c Added REPOSITION-STREAM-CURSOR, which move the stream cursor somewhere
Andy Hefner authored
202 (defun reposition-stream-cursor (stream)
203 "Moves the cursor somewhere clear of Goatee's editing area."
204 (let ((max-y 0))
205 (map-over-output-records #'(lambda (r)
206 (setf max-y (max max-y (bounding-rectangle-max-y r))))
207 (stream-output-history stream))
208 (setf (stream-cursor-position stream)
209 (values 0 max-y))))
210
211
ecc7441 Fixed a bug in frame-input-context-track-pointer that was causing a
Timothy Moore authored
212 (defmethod climi::finalize ((stream goatee-input-editing-mixin)
213 input-sensitizer)
1db72d0 Moved some input-editing functions around.
Troels Henriksen authored
214 (call-next-method)
ecc7441 Fixed a bug in frame-input-context-track-pointer that was causing a
Timothy Moore authored
215 (setf (cursor-visibility (cursor (area stream))) nil)
a00f940 Fix bug where commands would disappear after entering them. Among
Timothy Moore authored
216 (let ((real-stream (encapsulating-stream-stream stream))
217 (record (area stream)))
b48fc04 Minor rearranging in finalizer for goatee-input-editing-mixin:
Andy Hefner authored
218 (when input-sensitizer
a00f940 Fix bug where commands would disappear after entering them. Among
Timothy Moore authored
219 (erase-output-record record real-stream)
ecc7441 Fixed a bug in frame-input-context-track-pointer that was causing a
Timothy Moore authored
220 (funcall input-sensitizer
221 real-stream
222 #'(lambda ()
a00f940 Fix bug where commands would disappear after entering them. Among
Timothy Moore authored
223 (stream-add-output-record real-stream record)
224 (when (stream-drawing-p real-stream)
225 #+nil (format *trace-output* "Redisplaying ~S~&" record)
226 (replay record real-stream)))))
b48fc04 Minor rearranging in finalizer for goatee-input-editing-mixin:
Andy Hefner authored
227 (reposition-stream-cursor real-stream)))
ecc7441 Fixed a bug in frame-input-context-track-pointer that was causing a
Timothy Moore authored
228
229 ;;; Hopefully only used on small buffers.
230
f8e674e Major new functionality: command processing with completion. Check
Timothy Moore authored
231 (defun location*-offset (buffer offset)
ecc7441 Fixed a bug in frame-input-context-track-pointer that was causing a
Timothy Moore authored
232 (loop for line = (dbl-head (lines buffer)) then (next line)
233 for size = (and line (size line))
234 while line
235 summing size into total-offset
236 do (when (>= total-offset offset)
237 (let ((pos (- size (- total-offset offset))))
238 (if (> pos (line-last-point line))
239 (return (values (next line) 0))
240 (return (values line pos)))))
241 finally (error 'goatee-error
242 :format-control "Offset ~S is greater than the ~
243 size of buffer ~S"
244 :format-arguments (list offset buffer))))
245
f8e674e Major new functionality: command processing with completion. Check
Timothy Moore authored
246 (defun offset-location* (buffer line pos)
247 (loop with end-line = (location* (buffer-end buffer))
248 for buf-line = (location* (buffer-start buffer)) then (next buf-line)
249 until (or (eq buf-line line) (eq buf-line end-line))
9207cae The most visible change is that argument prompts are displayed in
Timothy Moore authored
250 summing (size buf-line) into total-offset
f8e674e Major new functionality: command processing with completion. Check
Timothy Moore authored
251 finally (progn
252 (unless (eq buf-line line)
253 (error 'goatee-error
254 :format "Location line ~S pos ~S isn't in buffer ~S"
255 :format-arguments (list line pos buffer)))
256 (return (+ total-offset pos)))))
257
d015fe5 Fixed presentation highlighting to do the right thing in the
Timothy Moore authored
258 (defgeneric set-editing-stream-insertion-pointer (stream pointer))
259
260 (defmethod set-editing-stream-insertion-pointer
261 ((stream goatee-input-editing-mixin) pointer)
262 (let* ((area (area stream))
263 (buffer (buffer area)))
264 (setf (point* buffer) (location*-offset buffer pointer))
265 (redisplay-area area)))
266
b662756 When Goatee's insertion-pointer is set, actually move point.
Troels Henriksen authored
267 (defmethod (setf stream-insertion-pointer) :after
268 ((new-value integer) (stream goatee-input-editing-mixin))
269 (set-editing-stream-insertion-pointer stream new-value))
d015fe5 Fixed presentation highlighting to do the right thing in the
Timothy Moore authored
270
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
271 (defun %replace-input (stream new-input start end buffer-start
272 rescan rescan-supplied-p
273 extent-class &rest extent-args)
ecc7441 Fixed a bug in frame-input-context-track-pointer that was causing a
Timothy Moore authored
274 (let* ((scan-pointer (stream-scan-pointer stream))
275 (area (area stream))
276 (buf (buffer area))
277 (del-chars (- scan-pointer buffer-start)))
9a30878 In input-editor-format and %replace-input test for rescanning with
Timothy Moore authored
278 (when (stream-rescanning-p stream)
3bf95f4 Add support for (accept 'form) to read and
Timothy Moore authored
279 (return-from %replace-input nil))
ecc7441 Fixed a bug in frame-input-context-track-pointer that was causing a
Timothy Moore authored
280 (if (<= 0 del-chars)
f8e674e Major new functionality: command processing with completion. Check
Timothy Moore authored
281 (progn
282 (with-point (buf)
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
283 (multiple-value-bind (line pos)
284 (location*-offset buf buffer-start)
285 (when (> del-chars 0)
286 (delete-char buf del-chars :line line :pos pos))
287 ;; location should be preserved across the delete-char, but
288 ;; it would be safest to use a buffer pointer or something...
289 (let ((extent (and extent-class
290 (apply #'make-instance extent-class
291 :start-line line :start-pos pos
292 extent-args))))
293 (insert buf new-input
294 :line line :pos pos :start start :end end)
295 (when extent
296 (setf (start-state extent) :open)
297 (setf (end-state extent) :open))
298 (make-input-editing-stream-snapshot stream area)
299 ;; If not rescanning, adjust scan pointer to point after new
300 ;; input
301 (if (and rescan-supplied-p (null rescan))
302 (setf (stream-scan-pointer stream)
303 (offset-location* buf
304 (line (point buf))
305 (pos (point buf))))
306 (queue-rescan stream)))))
9207cae The most visible change is that argument prompts are displayed in
Timothy Moore authored
307 ;; XXX Redundant with make-input-editing-stream-snapshot?
f8e674e Major new functionality: command processing with completion. Check
Timothy Moore authored
308 (setf (stream-insertion-pointer stream)
309 (offset-location* buf (line (point buf)) (pos (point buf))))
310 (redisplay-area area))
ecc7441 Fixed a bug in frame-input-context-track-pointer that was causing a
Timothy Moore authored
311 (warn "replace-input stream ~S: buffer-start ~S is greater than ~
312 scan-pointer ~S. Don't know how to deal with that."
313 stream
314 buffer-start
9207cae The most visible change is that argument prompts are displayed in
Timothy Moore authored
315 scan-pointer))))
316
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
317 (defvar climi::*current-input-stream*)
318 (defvar climi::*current-input-position*)
319
320 (defmethod replace-input ((stream goatee-input-editing-mixin) new-input
321 &key
322 (start 0)
323 (end (length new-input))
324 (buffer-start nil buffer-start-supplied-p)
325 (rescan nil rescan-supplied-p))
326 (unless buffer-start-supplied-p
327 (if (eq stream climi::*current-input-stream*)
328 (setq buffer-start climi::*current-input-position*)
329 (setq buffer-start 0)))
330 (%replace-input stream new-input
331 start end buffer-start rescan rescan-supplied-p nil))
332
6c71abb Presentation histories. The new goatee command C-M y works reasonably
Timothy Moore authored
333 (defun present-acceptably-to-string
334 (object type view for-context-type)
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
335 (flet ((present-it (acceptably)
6c71abb Presentation histories. The new goatee command C-M y works reasonably
Timothy Moore authored
336 (present-to-string object type
337 :view view
338 :acceptably acceptably
339 :for-context-type for-context-type)))
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
340 (let* ((acceptably t)
341 (printed-rep nil))
342 (handler-case
343 (setq printed-rep (present-it t))
344 (error ()
345 (setq acceptably nil)
346 (setq printed-rep (present-it nil))))
6c71abb Presentation histories. The new goatee command C-M y works reasonably
Timothy Moore authored
347 (values printed-rep (if acceptably
348 nil
349 object)))))
350
351 (defmethod presentation-replace-input
352 ((stream goatee-input-editing-mixin)
353 object type view
354 &key
355 (buffer-start nil buffer-start-supplied-p)
356 (rescan nil rescan-supplied-p)
357 query-identifier
358 (for-context-type type))
359 (declare (ignore query-identifier))
360 (multiple-value-bind (printed-rep accept-object)
361 (present-acceptably-to-string object type view
362 for-context-type)
363 (unless buffer-start-supplied-p
364 (if (eq stream climi::*current-input-stream*)
365 (setq buffer-start climi::*current-input-position*)
366 (setq buffer-start 0)))
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
367 (apply #'%replace-input stream printed-rep
6c71abb Presentation histories. The new goatee command C-M y works reasonably
Timothy Moore authored
368 0 (length printed-rep) buffer-start rescan rescan-supplied-p
369 (if accept-object
370 `(accept-result-extent :object ,accept-object
d217e1e Cleaned up the frame layout code. It's now possible to change layouts
Timothy Moore authored
371 :result-type ,type)
372 '(nil)))))
6c71abb Presentation histories. The new goatee command C-M y works reasonably
Timothy Moore authored
373
f1abcc7 3 sets of additions/changes/fixes:
Timothy Moore authored
374
9207cae The most visible change is that argument prompts are displayed in
Timothy Moore authored
375 ;;; There used to be complicated logic here to support output when
376 ;;; rescanning, but it seems to be very hairy to get right in
377 ;;; combination with read-gesture's behavior upon seeing noise
378 ;;; strings, especially with respect to peek and unread-gesture. So, just
379 ;;; suppress printing the noise string unless we're at the end of the
380 ;;; buffer and can't screw anything up.
381
382 (defmethod input-editor-format ((stream goatee-input-editing-mixin)
383 format-string
384 &rest format-args)
385 (let* ((scan-pointer (stream-scan-pointer stream))
386 (area (area stream))
387 (buf (buffer area))
388 (output (apply #'format nil format-string format-args)))
9a30878 In input-editor-format and %replace-input test for rescanning with
Timothy Moore authored
389 (when (stream-rescanning-p stream)
9207cae The most visible change is that argument prompts are displayed in
Timothy Moore authored
390 (return-from input-editor-format nil))
391 (multiple-value-bind (line pos)
392 (location*-offset buf scan-pointer)
393 (let ((extent (make-instance 'noise-extent
394 :start-line line :start-pos pos)))
395 (with-point (buf)
396 (insert buf output :line line :pos pos))
397 (setf (start-state extent) :open)
398 (setf (end-state extent) :open)
399 (setf (stream-scan-pointer stream)
400 (offset-location* buf
401 (line (bp-end extent))
402 (pos (bp-end extent))))
403 (make-input-editing-stream-snapshot stream area)
404 (redisplay-area area))))
405 nil)
626d479 Added implementions of `redraw-input-buffer'. We ignore the
Troels Henriksen authored
406
407 (defmethod redraw-input-buffer ((stream goatee-input-editing-mixin) &optional (start-position 0))
408 (declare (ignore start-position))
409 (redisplay-area (area stream)))
de5f174 Implement `erase-input-buffer' (somewhat, mostly so older CLIM
Troels Henriksen authored
410
411 (defmethod erase-input-buffer ((stream goatee-input-editing-mixin)
412 &optional (start-position 0))
413 (declare (ignore start-position))
414 (clear-output-record (area stream)))
Something went wrong with that request. Please try again.