buffer-window.sl 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2. %
  3. % Buffer-Window.SL
  4. %
  5. % Author: Alan Snyder
  6. % Hewlett-Packard/CRC
  7. % Date: 18 August 1982
  8. % Revised: 24 February 1983
  9. %
  10. % Inspired by Will Galway's EMODE Virtual Screen package.
  11. %
  12. % A Buffer-Window object maintains an attachment between an editor buffer and a
  13. % virtual screen. This module is responsible for mapping the contents of the
  14. % editor buffer to an image on the virtual screen. A "window label" object
  15. % may be specified to maintain a descriptive label at the bottom of the
  16. % virtual screen (see comment for the SET-LABEL method).
  17. %
  18. % 24-Feb-83 Alan Snyder
  19. % Fixed bug: cursor positioning didn't take buffer-left into account.
  20. % 16-Feb-83 Alan Snyder
  21. % Declare -> Declare-Flavor.
  22. % 7-Feb-83 Alan Snyder
  23. % Refresh now returns a flag indicating completion (no breakout).
  24. % Add cached method for label refresh.
  25. % 31-Jan-83 Alan Snyder
  26. % Modified to use separate window-label object to write the label area.
  27. % Note: SET-SIZE height argument is now interpreted as the screen height!
  28. % 20-Jan-83 Alan Snyder
  29. % Bug fix: adjust window after changing screen size.
  30. % 28-Dec-82 Alan Snyder
  31. % Replaced call to current-display-column in REFRESH, which was incorrect
  32. % because it assumes the buffer is current. Changed to display position of
  33. % window, rather than position of buffer (meaningful only when the window
  34. % package can display multiple cursors). Added methods: CHAR-POSITION,
  35. % SET-SCREEN, and &NEW-SCREEN. Changed EXPOSE to refresh first, for more
  36. % graceful screen update when using direct writing. Change label writing to
  37. % clear-eol after writing the label, not before, also for more graceful
  38. % screen update. Changed &WRITE-LINE-TO-SCREEN to buffer its changes in a
  39. % string, for efficiency. General cleanup.
  40. % 20-Dec-82 Alan Snyder
  41. % Added declarations for buffer and screen instance variables, for
  42. % efficiency.
  43. %
  44. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  45. (BothTimes (load objects))
  46. (CompileTime (load fast-int fast-vectors fast-strings display-char))
  47. (de create-unlabeled-buffer-window (buffer virtual-screen)
  48. % Create a buffer window object that presents the specified buffer onto
  49. % the specified virtual-screen. There will be no label area.
  50. (make-instance 'buffer-window 'buffer buffer 'screen virtual-screen)
  51. )
  52. (de create-buffer-window (buffer virtual-screen)
  53. % Create a buffer window object that presents the specified buffer onto
  54. % the specified virtual-screen. There will be a one-line label.
  55. (let ((w (create-unlabeled-buffer-window buffer virtual-screen)))
  56. (=> w set-label (create-window-label w))
  57. w
  58. ))
  59. (defflavor buffer-window
  60. (height % number of rows of text (rows are 0 indexed)
  61. maxrow % highest numbered row
  62. width % number of columns of text (cols are 0 indexed)
  63. maxcol % highest numbered column
  64. (buffer-left 0) % leftmost buffer column displayed
  65. (buffer-top 0) % topmost buffer line displayed
  66. (overflow-marker #/!) % display character used to mark overlong lines
  67. (saved-position NIL) % buffer position saved here while not selected
  68. (label NIL) % the optional label-maintaining object
  69. (label-height 0) % number of lines occupied by the label
  70. (label-refresh-method NIL) % cached method for refreshing the label
  71. (text-enhancement (dc-make-enhancement-mask))
  72. % display enhancement used in text area
  73. line-buffer % string of characters used to write line
  74. buffer % the buffer being displayed
  75. screen % the virtual screen used for display
  76. buffer-lines % vector of buffer lines currently displayed
  77. % % NIL used for EQable empty string
  78. )
  79. ()
  80. (gettable-instance-variables
  81. height
  82. width
  83. screen
  84. buffer
  85. buffer-left
  86. buffer-top
  87. text-enhancement
  88. )
  89. (initable-instance-variables
  90. screen
  91. buffer
  92. text-enhancement
  93. )
  94. )
  95. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  96. (declare-flavor text-buffer buffer)
  97. (declare-flavor virtual-screen screen)
  98. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  99. % Public methods:
  100. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  101. (defmethod (buffer-window select) ()
  102. % This method is invoked when the window is selected. It restores the saved
  103. % buffer pointer, if any. It will not scroll the window: instead, it will
  104. % adjust the buffer position, if necessary, to keep the buffer pointer within
  105. % the window.
  106. (when saved-position
  107. (=> buffer set-position saved-position)
  108. (setf saved-position NIL)
  109. )
  110. (=> self adjust-buffer)
  111. )
  112. (defmethod (buffer-window deselect) ()
  113. % This method is invoked when the window is deselected. It saves the current
  114. % buffer pointer, which will be restored when the window is again selected.
  115. % It adjusts the window to ensure that the window shows the saved position.
  116. (setf saved-position (=> buffer position))
  117. (=> self adjust-window)
  118. )
  119. (defmethod (buffer-window expose) ()
  120. % Expose the window, putting it "on top" (expose the attached virtual screen).
  121. (=> self refresh nil)
  122. (=> screen expose)
  123. )
  124. (defmethod (buffer-window deexpose) ()
  125. % De-expose the window (de-expose the attached virtual screen).
  126. (=> screen deexpose)
  127. )
  128. (defmethod (buffer-window exposed?) ()
  129. (=> screen exposed?)
  130. )
  131. (defmethod (buffer-window set-screen) (new-screen)
  132. (when (not (eq screen new-screen))
  133. (let ((exposed? (=> screen exposed?))
  134. (old-screen screen)
  135. )
  136. (setf screen new-screen)
  137. (=> self &new-screen)
  138. (when exposed? (=> self expose) (=> old-screen deexpose))
  139. )))
  140. (defmethod (buffer-window set-label) (new-label)
  141. % Specify a "label" object to write a label at the bottom of the screen. NIL
  142. % implies that no label area is wanted. If an object is specified, it
  143. % must support the following operations:
  144. % (=> label height)
  145. % Return the number of lines occupied by the label area at the bottom
  146. % of the buffer-window's virtual screen.
  147. % (=> label resize)
  148. % Tell the label that the window has changed size. This may cause
  149. % the label to change its height, but should not cause a refresh.
  150. % (=> label refresh)
  151. % This instructs the label object to refresh the label area. The label
  152. % area is assumed to be the bottom-most <height> lines on the
  153. % buffer-window's virtual screen, although it could be on a totally
  154. % different virtual screen, if desired (in which case the "height"
  155. % operation should return 0).
  156. % This operation may change the number of lines available for text, which
  157. % may require adjusting the window position. A refresh is not done
  158. % immediately.
  159. (setf label new-label)
  160. (setf label-refresh-method (if label (object-get-handler label 'refresh)))
  161. (=> self &new-size)
  162. )
  163. (defmethod (buffer-window position) ()
  164. % If the window is selected, return the position of the buffer. Otherwise,
  165. % return the "saved position".
  166. (or saved-position (=> buffer position)))
  167. (defmethod (buffer-window line-position) ()
  168. (if saved-position
  169. (buffer-position-line saved-position)
  170. (=> buffer line-pos)
  171. ))
  172. (defmethod (buffer-window char-position) ()
  173. (if saved-position
  174. (buffer-position-column saved-position)
  175. (=> buffer char-pos)
  176. ))
  177. (defmethod (buffer-window set-position) (bp)
  178. % If the window is selected, set the buffer position. Otherwise, set the
  179. % "saved position".
  180. (if saved-position
  181. (setf saved-position bp)
  182. (=> buffer set-position bp)
  183. ))
  184. (defmethod (buffer-window set-line-position) (line)
  185. % If the window is selected, set the buffer position.
  186. % Otherwise, set the "saved position".
  187. (if saved-position
  188. (setf saved-position (buffer-position-create line 0))
  189. (=> buffer set-line-pos line)
  190. ))
  191. (defmethod (buffer-window adjust-window) ()
  192. % Adjust the window position, if necessary, to ensure that the current
  193. % buffer location (if the window is selected) or the saved buffer location
  194. % (if the window is not selected) is within the window.
  195. (let ((line (=> self line-position)))
  196. (if (or (< line buffer-top) (>= line (+ buffer-top height)))
  197. % The desired line doesn't show in the window.
  198. (=> self readjust-window)
  199. )))
  200. (defmethod (buffer-window readjust-window) ()
  201. % Adjust the window position to nicely show the current location.
  202. (let ((line (=> self line-position))
  203. (one-third-screen (/ height 3))
  204. )
  205. (=> self set-buffer-top
  206. (if (>= line (- (=> buffer size) one-third-screen))
  207. (- line (* 2 one-third-screen))
  208. (- line one-third-screen)
  209. ))))
  210. (defmethod (buffer-window adjust-buffer) ()
  211. % Adjust the buffer position, if necessary, to ensure that the current
  212. % buffer location is visible on the screen. If the window position is
  213. % past the end of the buffer, it will be changed.
  214. (let ((size (=> buffer size)))
  215. (cond ((>= buffer-top size)
  216. % The window is past the end of the buffer.
  217. (=> self set-buffer-top (- size (/ height 3)))
  218. )))
  219. (let ((line (=> buffer line-pos)))
  220. (cond ((or (< line buffer-top) (>= line (+ buffer-top height)))
  221. % The current line doesn't show in the window.
  222. (=> buffer set-line-pos (+ buffer-top (/ height 3)))
  223. ))))
  224. (defmethod (buffer-window set-buffer) (new-buffer)
  225. (setf buffer new-buffer)
  226. (setf buffer-left 0)
  227. (setf buffer-top 0)
  228. (if saved-position (setf saved-position (=> buffer position)))
  229. (=> self adjust-window)
  230. (=> self &reset)
  231. )
  232. (defmethod (buffer-window set-buffer-top) (new-top)
  233. (cond ((<= new-top 0) (setf new-top 0))
  234. ((>= new-top (=> buffer visible-size))
  235. (setf new-top (- (=> buffer visible-size) 1)))
  236. )
  237. (setf buffer-top new-top)
  238. )
  239. (defmethod (buffer-window set-buffer-left) (new-left)
  240. (when (~= new-left buffer-left)
  241. (if (< new-left 0) (setf new-left 0))
  242. (when (~= new-left buffer-left)
  243. (setf buffer-left new-left)
  244. (=> self &reset)
  245. )))
  246. (defmethod (buffer-window set-size) (new-height new-width)
  247. % Change the size of the screen to have the specified height and width.
  248. % The size is adjusted to ensure that there is at least one row of text.
  249. (setf new-height (max new-height (+ label-height 1)))
  250. (setf new-width (max new-width 1))
  251. (when (or (~= new-height (=> screen height))
  252. (~= new-width (=> screen width)))
  253. (=> screen set-size new-height new-width)
  254. (=> self &new-size)
  255. ))
  256. (defmethod (buffer-window set-text-enhancement) (e-mask)
  257. (when (~= text-enhancement e-mask)
  258. (setf text-enhancement e-mask)
  259. (=> screen set-default-enhancement e-mask)
  260. (=> self &reset)
  261. ))
  262. (defmethod (buffer-window refresh) (breakout-allowed)
  263. % Update the virtual screen (including the label) to correspond to the
  264. % current state of the attached buffer. Return true if the refresh
  265. % was completed (no breakout occurred).
  266. (if (not (and breakout-allowed (input-available?)))
  267. (let ((buffer-end (=> buffer visible-size)))
  268. (for (from row 0 maxrow)
  269. (for line-number buffer-top (+ line-number 1))
  270. (do
  271. % NIL is used to represent all EMPTY lines, so that EQ will work.
  272. (let ((line (and (< line-number buffer-end)
  273. (=> buffer fetch-line line-number))))
  274. (if (and line (string-empty? line)) (setf line NIL))
  275. (when (not (eq line (vector-fetch buffer-lines row)))
  276. (vector-store buffer-lines row line)
  277. (=> self &write-line-to-screen line row)
  278. )))
  279. )
  280. (if (and label label-refresh-method)
  281. (apply label-refresh-method (list label)))
  282. (let* ((linepos (=> self line-position))
  283. (charpos (=> self char-position))
  284. (row (- linepos buffer-top))
  285. (line (vector-fetch buffer-lines row))
  286. (column (- (map-char-to-column line charpos) buffer-left))
  287. )
  288. (=> screen set-cursor-position row column)
  289. )
  290. T % refresh completed
  291. )))
  292. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  293. % Private methods:
  294. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  295. (defmethod (buffer-window init) (init-plist)
  296. (=> self &new-screen)
  297. )
  298. (defmethod (buffer-window &new-screen) ()
  299. (=> screen set-default-enhancement text-enhancement)
  300. (=> self &new-size)
  301. )
  302. (defmethod (buffer-window &new-size) ()
  303. % The size of the screen and/or label may have changed. Adjust
  304. % the internal state of the buffer-window accordingly.
  305. (if label (=> label resize)) % may change label height
  306. (setf label-height (if label (max 0 (=> label height)) 0))
  307. (setf height (- (=> screen height) label-height))
  308. (setf width (=> screen width))
  309. (setf maxrow (- height 1))
  310. (setf maxcol (- width 1))
  311. (setf buffer-lines (make-vector maxrow 'UNKNOWN))
  312. (setf line-buffer (make-string (+ maxcol 10) #\space))
  313. (=> self adjust-window) % ensure that cursor is still visible
  314. )
  315. (defmethod (buffer-window &reset) ()
  316. % "Forget" information about displayed lines.
  317. (for (from i 0 maxrow)
  318. (do (vector-store buffer-lines i 'UNKNOWN))))
  319. (defmethod (buffer-window &write-line-to-screen) (line row)
  320. (if (null line)
  321. (=> screen clear-to-eol row 0)
  322. % else
  323. (let ((count (=> self &compute-screen-line line)))
  324. (cond
  325. ((> count width)
  326. (=> screen write-string row 0 line-buffer maxcol)
  327. (=> screen write overflow-marker row maxcol)
  328. )
  329. (t
  330. (=> screen write-string row 0 line-buffer count)
  331. (=> screen clear-to-eol row count)
  332. )))))
  333. (defmacro &write-char (ch)
  334. % Used by &COMPUTE-SCREEN-LINE.
  335. `(progn
  336. (if (>= line-index 0)
  337. (string-store line-buf line-index ,ch))
  338. (setf line-index (+ line-index 1))
  339. (setf line-column (+ line-column 1))
  340. ))
  341. (defmethod (buffer-window &compute-screen-line) (line)
  342. % Internal method used by &WRITE-LINE-TO-SCREEN. It fills the line buffer
  343. % with the appropriate characters and returns the number of characters in
  344. % the line buffer.
  345. (let ((line-buf line-buffer) % local variables are more efficient
  346. (line-column 0)
  347. (line-index (- buffer-left))
  348. (the-width width) % local variables are more efficient
  349. )
  350. (for (from i 0 (string-upper-bound line))
  351. (until (> line-index the-width)) % have written past the right edge
  352. (do (let ((ch (string-fetch line i)))
  353. (cond
  354. ((= ch #\TAB) % TABs are converted to spaces.
  355. (let ((tabcol (& (+ line-column 8) (~ 7))))
  356. (while (< line-column tabcol)
  357. (&write-char #\space)
  358. )))
  359. ((or (< ch #\space) (= ch #\rubout))
  360. % Control characters are converted to "uparrow" form.
  361. (&write-char #/^)
  362. (&write-char (^ ch 8#100))
  363. )
  364. (t (&write-char ch))
  365. ))))
  366. line-index
  367. ))
  368. (de map-char-to-column (line n)
  369. % Map character position N to the corresponding display column index with
  370. % respect to the specified LINE. Handle funny mapping of TABs and control
  371. % characters.
  372. (setf n (- n 1))
  373. (let ((upper-bound (string-upper-bound line)))
  374. (if (> n upper-bound) (setf n upper-bound)))
  375. (for* (from i 0 n)
  376. (with (col 0))
  377. (do (let ((ch (string-fetch line i)))
  378. (cond
  379. ((= ch #\TAB)
  380. % TABs are converted to an appropriate number of spaces.
  381. (setf col (& (+ col 8) (~ 7)))
  382. )
  383. ((or (< ch #\space) (= ch #\rubout))
  384. % Control characters are converted to "uparrow" form.
  385. (setf col (+ col 2))
  386. )
  387. (t
  388. (setf col (+ col 1))
  389. ))))
  390. (returns col)))
  391. (de map-column-to-char (line n)
  392. % Map display column index N to the corresponding character position with
  393. % respect to the specified LINE. Handle funny mapping of TABs and control
  394. % characters.
  395. (for* (from i 0 (string-upper-bound line))
  396. (with (col 0))
  397. (until (>= col n))
  398. (do (let ((ch (string-fetch line i)))
  399. (cond
  400. ((= ch #\TAB)
  401. % TABs are converted to an appropriate number of spaces.
  402. (setf col (& (+ col 8) (~ 7)))
  403. )
  404. ((or (< ch #\space) (= ch #\rubout))
  405. % Control characters are converted to "uparrow" form.
  406. (setf col (+ col 2))
  407. )
  408. (t
  409. (setf col (+ col 1))
  410. ))))
  411. (returns i)
  412. ))
  413. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  414. (undeclare-flavor buffer screen)