browser.sl 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2. %
  3. % Browser.SL - Browser object definition
  4. %
  5. % Author: Alan Snyder
  6. % Hewlett-Packard/CRC
  7. % Date: 4 February 1983
  8. % Revised: 14 February 1983
  9. %
  10. % This file implements browser objects. These objects form the basis of
  11. % a general browser support mechanism. See Browser-Support.SL for additional
  12. % support functions and Buffer-Browser.SL for an example of a browser
  13. % using this mechanism.
  14. %
  15. % 14-Feb-83 Alan Snyder
  16. % Fix bug in filter application (was trying to apply a macro).
  17. % 11-Feb-83 Alan Snyder
  18. % Fix &remove-current-item to reset the display buffer's modified flag.
  19. % Improve comments.
  20. %
  21. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  22. (compiletime (load fast-vectors fast-int))
  23. (load gsort)
  24. (de create-browser (display-buffer view-buffer header-text items current-sorter)
  25. % Create a brower. DISPLAY-BUFFER is the buffer to use for displaying the
  26. % items. VIEW-BUFFER is the buffer to use for viewing an item; if NIL, the
  27. % item is expected to provide its own buffer. HEADER-TEXT is a vector of
  28. % strings to display at the top of the display buffer; it may be NIL. ITEMS
  29. % is a list or vector containing the set of items to display (this data
  30. % structure will not be modified). CURRENT-SORTER may be NIL or a function
  31. % ID. If non-NIL, the function will be used to sort the initial set of
  32. % items.
  33. (make-instance 'browser
  34. 'display-buffer display-buffer
  35. 'view-buffer view-buffer
  36. 'header-text header-text
  37. 'items items
  38. 'current-sorter current-sorter
  39. ))
  40. (defflavor browser
  41. (
  42. (display-buffer NIL) % buffer used to display items
  43. (view-buffer NIL) % buffer used to view items (NIL => ask item)
  44. (viewed-item NIL) % the item most recently viewed
  45. (header-text NIL) % text displayed at top of buffer
  46. items % vector of visible items (may have junk at end)
  47. first-item-linepos % line number of first item in display
  48. last-item-index % index of last item in ITEMS vector
  49. (filtered-items ()) % list of lists of items removed by filtering
  50. (current-sorter NIL) % sorter used if items are un-filtered
  51. )
  52. ()
  53. (initable-instance-variables display-buffer view-buffer header-text items
  54. current-sorter)
  55. )
  56. % Methods provided by items:
  57. %
  58. % (=> item display-text)
  59. % Return string used to display the item.
  60. %
  61. % (=> item delete)
  62. % Mark the item as deleted. May do nothing if deletion is not supported.
  63. % May change the display-text. This method need not be provided if no
  64. % delete commands are provided in the particular browser.
  65. %
  66. % (=> item undelete)
  67. % Mark the item as not deleted. May do nothing if deletion is not
  68. % supported. May change the display-text. This method need not be provided
  69. % if no delete commands are provided in the particular browser.
  70. %
  71. % (=> item deleted?)
  72. % Return T if the item has been marked for deletion. This method need not
  73. % be provided if no delete commands are provided in the particular browser.
  74. %
  75. % (=> item kill)
  76. % Kill the real item. (Instead of just marking the item for deletion, this
  77. % should actually dispose of the item, if that action is supported.) May do
  78. % nothing if killing is not supported. Return T if the item is actually
  79. % killed, NIL otherwise. This method need not be provided if no delete
  80. % commands are provided in the particular browser.
  81. %
  82. % (=> item view-buffer buffer)
  83. % Return a buffer containing the item for viewing. If the buffer argument
  84. % is non-NIL, then that buffer should be used for viewing. Otherwise, the
  85. % item must provide its own buffer.
  86. %
  87. % (=> item cleanup)
  88. % Throw away any unneeded stuff, such as a buffer created for viewing. This
  89. % method is invoked when an item is no longer being viewed, or when the item
  90. % is being filtered out, or when the browser is being exited.
  91. %
  92. % (=> item apply-filter filter)
  93. % The item should apply the filter to itself and return T if the filter
  94. % matches the item and NIL otherwise.
  95. (defmethod (browser current-item) ()
  96. % Return the current item, which is the item that is displayed on the
  97. % display-buffer's current line, or NIL, if there is no such item.
  98. (let ((index (- (=> display-buffer line-pos) first-item-linepos)))
  99. (when (and (>= index 0) (<= index last-item-index))
  100. (vector-fetch items index)
  101. )))
  102. (defmethod (browser current-item-index) ()
  103. % Return the index of the current item, which is the item that is displayed
  104. % on the display-buffer's current line, or NIL, if there is no such item.
  105. (let ((index (- (=> display-buffer line-pos) first-item-linepos)))
  106. (when (and (>= index 0) (<= index last-item-index))
  107. index
  108. )))
  109. (defmethod (browser kill-item) ()
  110. % Kill the current item, if any. Return T if the item is killed,
  111. % NIL otherwise.
  112. (let ((item (=> self current-item)))
  113. (when (=> item kill)
  114. (=> self &remove-current-item)
  115. )))
  116. (defmethod (browser kill-deleted-items) ()
  117. % Attempts to KILL all items that have been marked for deletion.
  118. % Returns a list of the items actually killed.
  119. (=> self &keep-items '&browser-item-not-killed ())
  120. )
  121. (defmethod (browser delete-item) ()
  122. % Mark the current item as deleted, if any. Return T if the item exists,
  123. % NIL otherwise.
  124. (let ((item (=> self current-item)))
  125. (when item
  126. (=> item delete)
  127. (=> self &update-current-item)
  128. T
  129. )))
  130. (defmethod (browser undelete-item) ()
  131. % Mark the current item as not deleted, if any. Return T if the item exists,
  132. % NIL otherwise.
  133. (let ((item (=> self current-item)))
  134. (when item
  135. (=> item undelete)
  136. (=> self &update-current-item)
  137. T
  138. )))
  139. (defmethod (browser view-item) ()
  140. % View the current item, if any, in a separate buffer.
  141. % Return the buffer if the item exists, NIL otherwise.
  142. (let ((item (=> self current-item)))
  143. (when item
  144. (when viewed-item
  145. (=> viewed-item cleanup))
  146. (setf viewed-item item)
  147. (=> item view-buffer view-buffer) % return the buffer
  148. )))
  149. (defmethod (browser ignore-item) ()
  150. % Ignore the current item, if any. Return T if the item exists.
  151. % Ignoring an item is like running a filter that accepts every item
  152. % except the current one, except that multiple successive ignores
  153. % coalesce into one filtered-item-set for undoing purposes.
  154. (let ((item (=> self &remove-current-item)))
  155. (when item
  156. (cond ((and filtered-items (eqcar (car filtered-items) 'IGNORE-COMMAND))
  157. % add this item to the previous list of ignored items
  158. (let ((filter-set (car filtered-items)))
  159. (setf (cdr filter-set) (cons item (cdr filter-set)))
  160. ))
  161. (t (setf filtered-items
  162. (cons (list 'IGNORE-COMMAND item) filtered-items))
  163. )))))
  164. (defmethod (browser filter-items) (filter)
  165. % Remove those items that do not match the specified filter.
  166. % If some items are removed, then they are added as a set to the
  167. % list of filtered items, so that this step can be undone, and T
  168. % is returned. Otherwise, no new set is created, and NIL is returned.
  169. (let ((filtered-list (=> self &keep-items 'ev-send
  170. (list 'apply-filter (list filter)))))
  171. (when filtered-list
  172. (setf filtered-list (cons filter filtered-list))
  173. (setf filtered-items (cons filtered-list filtered-items))
  174. T
  175. )))
  176. (defmethod (browser undo-filter) ()
  177. % Undo the effect of the most recent active filtering step.
  178. % Return the filter or NIL if there are no active filtering steps.
  179. (when filtered-items
  180. (let ((filter (car (car filtered-items)))
  181. (the-items (cdr (car filtered-items)))
  182. (current-item (=> self current-item))
  183. )
  184. (setf filtered-items (cdr filtered-items))
  185. (while the-items
  186. (let ((item (car the-items)))
  187. (setf the-items (cdr the-items))
  188. (setf last-item-index (+ last-item-index 1))
  189. (vector-store items last-item-index item)
  190. ))
  191. (=> self &sort-items)
  192. (=> self &update-display)
  193. (=> self select-item current-item)
  194. filter
  195. )))
  196. (defmethod (browser exit) ()
  197. (setf viewed-item NIL)
  198. (for (from i 0 last-item-index)
  199. (do (=> (vector-fetch items i) cleanup)))
  200. )
  201. (defmethod (browser items) ()
  202. % Return a list of the items.
  203. (for (from i 0 last-item-index)
  204. (collect (vector-fetch items i)))
  205. )
  206. (defmethod (browser sort) (sorter)
  207. (let ((current-item (=> self current-item)))
  208. (setf current-sorter sorter)
  209. (=> self &sort-items)
  210. (=> self &update-display)
  211. (=> self select-item current-item)
  212. ))
  213. (defmethod (browser send-item) (msg args)
  214. % Send the current item, if any, the specified message with the specified
  215. % arguments. Return NIL if there is no current item; otherwise, return
  216. % the result of sending the message to the item.
  217. (let ((item (=> self current-item)))
  218. (when item
  219. (prog1
  220. (lexpr-send item msg args)
  221. (=> self &update-current-item)
  222. ))))
  223. (defmethod (browser select-item) (item)
  224. % If ITEM is not NIL, then adjust the buffer pointer to point to
  225. % that item.
  226. (for (from i 0 last-item-index)
  227. (do (when (eq item (vector-fetch items i))
  228. (=> display-buffer goto (+ i first-item-linepos) 0)
  229. (exit)
  230. ))))
  231. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  232. % Private methods:
  233. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  234. (defmethod (browser init) (init-plist)
  235. (=> display-buffer put 'browser self)
  236. (setf items (cond ((ListP items) (List2Vector items))
  237. ((VectorP items) (CopyVector items))
  238. (t (List2Vector ()))
  239. ))
  240. (setf last-item-index (vector-upper-bound items))
  241. (=> self &sort-items)
  242. (=> self &update-display)
  243. )
  244. (defmethod (browser &update-display) ()
  245. % Update the display. The cursor is moved to the first item.
  246. (=> display-buffer reset)
  247. (when header-text
  248. (=> display-buffer insert-text header-text)
  249. (=> display-buffer insert-eol)
  250. )
  251. (setf first-item-linepos (=> display-buffer line-pos))
  252. (for (from i 0 last-item-index)
  253. (do (let ((item (vector-fetch items i)))
  254. (=> display-buffer insert-line (=> item display-text))
  255. )))
  256. (=> display-buffer set-modified? NIL)
  257. (=> display-buffer goto first-item-linepos 0)
  258. )
  259. (defmethod (browser &sort-items) ()
  260. % Sort the items according to the current sorter, if any.
  261. % Do not update the display buffer.
  262. (when current-sorter
  263. (let ((list ()))
  264. (for (from i 0 last-item-index)
  265. (do (setf list (cons (vector-fetch items i) list)))
  266. )
  267. (setf list (GSort list current-sorter))
  268. (for (from i 0 last-item-index)
  269. (do (vector-store items i (car list))
  270. (setf list (cdr list))
  271. ))
  272. )))
  273. (defmethod (browser &remove-current-item) ()
  274. % Remove the current item from ITEMS and the display.
  275. % Return the item or NIL if there is no current item.
  276. (let ((index (=> self current-item-index)))
  277. (when index
  278. (let ((item (vector-fetch items index)))
  279. (for (from i (+ index 1) last-item-index)
  280. (do (vector-store items (- i 1) (vector-fetch items i))
  281. ))
  282. (vector-store items last-item-index NIL)
  283. (setf last-item-index (- last-item-index 1))
  284. (=> display-buffer move-to-start-of-line)
  285. (let ((start-pos (=> display-buffer position)))
  286. (=> display-buffer move-to-next-line)
  287. (=> display-buffer extract-region T start-pos
  288. (=> display-buffer position))
  289. (=> display-buffer set-modified? NIL)
  290. )
  291. item
  292. ))))
  293. (defmethod (browser &update-current-item) ()
  294. % Update the display for the current item.
  295. (let ((index (=> self current-item-index)))
  296. (when index
  297. (let ((item (vector-fetch items index)))
  298. (=> display-buffer store-line (+ index first-item-linepos)
  299. (=> item display-text))
  300. (=> display-buffer set-modified? NIL)
  301. ))))
  302. (defmethod (browser &keep-items) (fcn args)
  303. % Apply the function FCN once for each item. The first argument to FCN
  304. % is the item; the remaining items are ARGS (a list).
  305. % Remove those items for which FCN returns NIL and return them
  306. % in a list of items.
  307. (let ((removed-items ())
  308. (ptr 0)
  309. (current-item-index (=> self current-item-index))
  310. (new-current-item-index 0)
  311. )
  312. (for (from i 0 last-item-index)
  313. (do (let ((item (vector-fetch items i))
  314. (this-ptr ptr)
  315. )
  316. (cond ((apply fcn (cons item args)) % keep it
  317. (vector-store items ptr item)
  318. (setf ptr (+ ptr 1))
  319. )
  320. (t % remove it
  321. (setf removed-items (cons item removed-items))
  322. (=> item cleanup)
  323. ))
  324. (when (and current-item-index (= i current-item-index))
  325. (setf new-current-item-index this-ptr))
  326. )))
  327. (setf last-item-index (- ptr 1))
  328. (=> self &update-display)
  329. (=> display-buffer goto (+ new-current-item-index first-item-linepos) 0)
  330. removed-items
  331. ))
  332. (de &browser-item-not-killed (item)
  333. (or (not (=> item deleted?))
  334. (not (=> item kill))
  335. ))