PageRenderTime 56ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/src/jtextree.sc

https://bitbucket.org/bunny351/ezd
Scala | 485 lines | 443 code | 42 blank | 0 comment | 6 complexity | e113d4720d04a358ee7c1ba6dbedf93f MD5 | raw file
  1. ;;; ezd - easy drawing for X11 displays.
  2. ;;;
  3. ;;; Procedures in this module are responsible for managing text stored in
  4. ;;; a justified text tree.
  5. ;* Copyright 1990-1993 Digital Equipment Corporation
  6. ;* All Rights Reserved
  7. ;*
  8. ;* Permission to use, copy, and modify this software and its documentation is
  9. ;* hereby granted only under the following terms and conditions. Both the
  10. ;* above copyright notice and this permission notice must appear in all copies
  11. ;* of the software, derivative works or modified versions, and any portions
  12. ;* thereof, and both notices must appear in supporting documentation.
  13. ;*
  14. ;* Users of this software agree to the terms and conditions set forth herein,
  15. ;* and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free
  16. ;* right and license under any changes, enhancements or extensions made to the
  17. ;* core functions of the software, including but not limited to those affording
  18. ;* compatibility with other hardware or software environments, but excluding
  19. ;* applications which incorporate this software. Users further agree to use
  20. ;* their best efforts to return to Digital any such changes, enhancements or
  21. ;* extensions that they make and inform Digital of noteworthy uses of this
  22. ;* software. Correspondence should be provided to Digital at:
  23. ;*
  24. ;* Director of Licensing
  25. ;* Western Research Laboratory
  26. ;* Digital Equipment Corporation
  27. ;* 250 University Avenue
  28. ;* Palo Alto, California 94301
  29. ;*
  30. ;* This software may be distributed (but not offered for sale or transferred
  31. ;* for compensation) to third parties, provided such third parties agree to
  32. ;* abide by the terms and conditions of this notice.
  33. ;*
  34. ;* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
  35. ;* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
  36. ;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
  37. ;* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  38. ;* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  39. ;* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  40. ;* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  41. ;* SOFTWARE.
  42. (module jtextree (with xlib))
  43. (include "struct.sch")
  44. (include "textree.sch")
  45. (include "display.sch")
  46. (include "xternal.sch")
  47. ;;; A justified text tree is represented by an instance of the JTEXTREE
  48. ;;; structure which contains the following fields.
  49. ;;;
  50. ;;; TEXTREE textree representing the text. Besides visible
  51. ;;; characters, a line of text may also contain spaces,
  52. ;;; tabs, newline characters and markers (char code = #x1-)
  53. ;;; LINES # of lines in the textree
  54. ;;; FIRST-CHANGED index of first changed line
  55. ;;; LAST-CHANGED index of last changed line
  56. ;;; MARKERS list of markers describing points in the text.
  57. ;;; WIDTH width to justify the text (in pixels). If this is
  58. ;;; false, then no justification is done.
  59. ;;; FONT X font structure for the font
  60. (define-structure JTEXTREE
  61. (textree '())
  62. (lines 0)
  63. (first-changed #f)
  64. (last-changed #f)
  65. (markers '())
  66. (width #f)
  67. (font #f))
  68. (define-in-line-structure-access JTEXTREE textree lines first-changed
  69. last-changed markers width font)
  70. ;;; A marker in the text is defined by a MARKER entry of the following
  71. ;;; form:
  72. (define-structure MARKER
  73. name
  74. line
  75. char
  76. (changed #f))
  77. (define-in-line-structure-access MARKER name line char changed)
  78. ;;; The change record in a JTEXTREE is cleared by calling the following
  79. ;;; procedure.
  80. (define (CLEAR-CHANGES-JTEXTREE jt)
  81. (jtextree-first-changed! jt #f)
  82. (jtextree-last-changed! jt #f)
  83. (for-each
  84. (lambda (marker) (marker-changed! marker #f))
  85. (jtextree-markers jt)))
  86. ;;; Changes are logged in the JTEXTREE structure by calling the following
  87. ;;; procedure.
  88. (define (CHANGED-LINE-JTEXTREE jt line)
  89. (cond ((not (jtextree-first-changed jt))
  90. (jtextree-first-changed! jt line)
  91. (jtextree-last-changed! jt line))
  92. ((< line (jtextree-first-changed jt))
  93. (jtextree-first-changed! jt line))
  94. ((> line (jtextree-last-changed jt))
  95. (jtextree-last-changed! jt line))))
  96. ;;; A portion of a JTEXTREE can be justified by calling the following
  97. ;;; procedure. Lines changed will be reflected in the JTEXTREE structure.
  98. (define (JUSTIFY-JTEXTREE jt line line-count)
  99. (if (and (positive? line-count) (< line (jtextree-lines jt))
  100. (jtextree-width jt))
  101. (case (justify-jtextree-line jt line)
  102. ((-1) (justify-jtextree jt line line-count))
  103. ((0) (justify-jtextree jt (+ line 1) (- line-count 1)))
  104. ((1) (justify-jtextree jt (+ line 1) line-count)))))
  105. ;;; Justify a single line in a JTEXTREE. It will return with the number of
  106. ;;; lines that it changed the JTEXTREE structure by. Changed lines will be
  107. ;;; noted in the structure as needed.
  108. (define (JUSTIFY-JTEXTREE-LINE jt line)
  109. (let* ((width (jtextree-width jt))
  110. (font (jtextree-font jt))
  111. (old-text (jtextree-text jt line))
  112. (old-text-len (string-length old-text))
  113. (space-width (xtextwidth font " " 1)))
  114. ;;; Delete the next line and append it to the existing line when at
  115. ;;; least one token will fit. One or two spaces of padding will be
  116. ;;; added as needed.
  117. (define (JOIN-LINE left)
  118. (let* ((next-text (jtextree-text jt (+ line 1)))
  119. (next-len (string-length next-text))
  120. (next-width (car (token-size next-text next-len width
  121. 0 0)))
  122. (pad (if (or (eq? old-text "") (eq? next-text ""))
  123. 0
  124. (let ((last-char (string-ref old-text
  125. (- old-text-len 1))))
  126. (cond ((or (char-whitespace? last-char)
  127. (char-whitespace?
  128. (string-ref next-text
  129. 0)))
  130. 0)
  131. ((eq? last-char #\.)
  132. 2)
  133. (else 1))))))
  134. (if (> (+ next-width (* pad space-width)) left)
  135. 0
  136. (let loop ((markers (jtextree-markers jt)))
  137. (if (pair? markers)
  138. (let* ((m (car markers))
  139. (ml (marker-line m))
  140. (mc (marker-char m)))
  141. (loop (cdr markers))
  142. (cond ((eq? ml line)
  143. (marker-char! m mc))
  144. ((eq? ml (+ line 1))
  145. (marker-line! m line)
  146. (marker-char! m
  147. (+ old-text-len mc pad))
  148. (marker-changed! m #t)))
  149. -1)
  150. (begin (delete-jtextree jt (+ line 1) 0
  151. (+ line 1)
  152. (string-length next-text) #f)
  153. (insert-jtextree jt line
  154. old-text-len
  155. (case pad
  156. ((0) next-text)
  157. ((1) (string-append " "
  158. next-text))
  159. ((2) (string-append " "
  160. next-text)))
  161. #f)
  162. -1))))))
  163. ;;; Break the existing line at the character index and insert the
  164. ;;; rest of the text in a new line that follows. Trailing and
  165. ;;; leading spaces in the middle of a line are deleted.
  166. (define (BREAK-LINE charx)
  167. (let ((last (let loop ((x (- charx 1)))
  168. (if (and (positive? x)
  169. (eq? (string-ref old-text x)
  170. #\space))
  171. (loop (- x 1))
  172. x)))
  173. (first (let loop ((x charx))
  174. (if (and (< x old-text-len)
  175. (eq? (string-ref old-text x)
  176. #\space))
  177. (loop (+ x 1))
  178. x))))
  179. (define (FIX-MARKER m)
  180. (cond ((and (eq? (marker-line m) line)
  181. (>= (marker-char m) first))
  182. (marker-changed! m #t)
  183. (marker-line! m (+ line 1))
  184. (marker-char! m
  185. (- (marker-char m) first)))
  186. ((and (eq? (marker-line m) line)
  187. (> (marker-char m) last))
  188. (marker-changed! m #t)
  189. (marker-char! m (+ last 1)))
  190. ((> (marker-line m) line)
  191. (marker-changed! m #t)
  192. (marker-line! m (+ (marker-line m) 1)))))
  193. (delete-jtextree jt line (+ last 1)
  194. line old-text-len #f)
  195. (jtextree-textree! jt
  196. (insert-textree (jtextree-textree jt) (+ line 1)
  197. (if (eq? old-text-len first)
  198. ""
  199. (substring old-text first old-text-len))))
  200. (changed-line-jtextree jt (+ line 1))
  201. (jtextree-lines! jt
  202. (+ (jtextree-lines jt) 1))
  203. (for-each fix-marker (jtextree-markers jt))
  204. 1))
  205. ;;; Compute the size of a token and return the width in pixels, and
  206. ;;; the incrments to charx and tabx.
  207. (define (TOKEN-SIZE text text-len line-width charx tabx)
  208. (case (and (< charx text-len) (string-ref text charx))
  209. ((#\space)
  210. (list space-width 1 1))
  211. ((#\tab)
  212. (list (xtextwidth font " "
  213. (- 8 (remainder tabx 8)))
  214. 1 (- 8 (remainder tabx 8))))
  215. ((#\newline)
  216. (list (- width line-width) 1 1))
  217. ((#f)
  218. (list 0 0 0))
  219. (else (let loop ((x charx))
  220. (if (and (< x text-len)
  221. (not (char-whitespace?
  222. (string-ref text x))))
  223. (loop (+ x 1))
  224. (list (xtextwidth font
  225. (substring text charx x)
  226. (- x charx))
  227. (- x charx) (- x charx)))))))
  228. ;;; Step across the line looking to break or join as needed.
  229. (let loop
  230. ((line-width
  231. (if (eq? line "")
  232. 0
  233. (let ((dim (cadddr (xtextextents font old-text 1))))
  234. (- (xcharstruct-lbearing dim)))))
  235. (charx 0)
  236. (tabx 0))
  237. (let* ((cwidth-charx-tabx (token-size old-text old-text-len
  238. line-width charx tabx))
  239. (cwidth (car cwidth-charx-tabx))
  240. (delta-charx (cadr cwidth-charx-tabx))
  241. (delta-tabx (caddr cwidth-charx-tabx)))
  242. (cond ((and (< cwidth (- width line-width))
  243. (< charx old-text-len))
  244. ;;; Step to the next token
  245. (loop (+ line-width cwidth) (+ charx delta-charx)
  246. (+ tabx delta-tabx)))
  247. ((eq? charx old-text-len)
  248. ;;; Out of text...
  249. (if (or (eq? cwidth (- width line-width))
  250. (eq? line (- (jtextree-lines jt) 1)))
  251. ;;; Exact match or end of text buffer.
  252. 0
  253. ;;; Try to join with the next line.
  254. (join-line (- width line-width))))
  255. ((zero? charx)
  256. ;;; First token too large, break after it.
  257. (break-line delta-charx))
  258. ((eq? cwidth (- width line-width))
  259. ;;; Exact fit if we take this one.
  260. (if (eq? (+ charx delta-charx) old-text-len)
  261. 0
  262. (break-line (+ charx delta-charx))))
  263. ;;; Next token won't fit.
  264. (else (break-line charx)))))))
  265. ;;; Before a line can be printed, all tabs must be expanded and newline and
  266. ;;; mark characters deleted. This is done by the following procedure.
  267. (define (JTEXTREE-EXPANDED-TEXT jt linex)
  268. (let* ((line (textree-text (jtextree-textree jt) linex))
  269. (buffer #f)
  270. (len (string-length line)))
  271. (let loop ((i 0) (j 0))
  272. (if (eq? i len)
  273. (set! buffer (make-string j #\space))
  274. (let ((char (string-ref line i)))
  275. (cond ((char>=? char #\space)
  276. (loop (+ i 1) (+ j 1))
  277. (string-set! buffer j char))
  278. ((eq? char #\tab)
  279. (loop (+ i 1) (+ j (- 8 (remainder j 8)))))
  280. (else (loop (+ i 1) j))))))
  281. buffer))
  282. ;;; A character index in the JTEXTREE text is converted to an X pixel offset
  283. ;;; in the displayed text by the following procedure.
  284. (define (TEXTI->PIXEL-JTEXTREE jt linex charx)
  285. (let* ((line (textree-text (jtextree-textree jt) linex))
  286. (font (jtextree-font jt))
  287. (len (string-length line))
  288. (buffer (make-string 1)))
  289. (let loop ((i 0) (j 0) (pixels 0)
  290. (lb (if (eq? line "")
  291. 0
  292. (let ((dim (cadddr (xtextextents font line 1))))
  293. (- (xcharstruct-lbearing dim))))))
  294. (if (or (eq? i charx) (eq? i len))
  295. pixels
  296. (let ((char (string-ref line i)))
  297. (cond ((char>=? char #\space)
  298. (string-set! buffer 0 char)
  299. (loop (+ i 1) (+ j 1)
  300. (+ pixels lb (xtextwidth font buffer 1))
  301. 0))
  302. ((eq? char #\tab)
  303. (loop (+ i 1)
  304. (+ j (- 8 (remainder j 8)))
  305. (+ pixels lb (xtextwidth font " "
  306. (- 8 (remainder j 8))))
  307. 0))
  308. (else (loop (+ i 1) j pixels lb))))))))
  309. ;;; A pixel index into a displayed line is converted to a character index in
  310. ;;; the JTEXTREE text line by the following procedure.
  311. (define (PIXEL->TEXTI-JTEXTREE jt linex pixel)
  312. (let* ((line (textree-text (jtextree-textree jt) linex))
  313. (font (jtextree-font jt))
  314. (len (string-length line))
  315. (buffer (make-string 1)))
  316. (let loop ((i 0) (j 0)
  317. (width (+ pixel
  318. (if (eq? line "")
  319. 0
  320. (let ((dim (cadddr (xtextextents font line
  321. 1))))
  322. (xcharstruct-lbearing dim))))))
  323. (if (eq? i len)
  324. i
  325. (let ((char (string-ref line i))
  326. (cwidth 0)
  327. (j j))
  328. (cond ((char>=? char #\space)
  329. (string-set! buffer 0 char)
  330. (set! j (+ j 1))
  331. (set! cwidth (xtextwidth font buffer 1)))
  332. ((eq? char #\tab)
  333. (set! j (+ j (- 8 (remainder j 8))))
  334. (set! cwidth (xtextwidth font " "
  335. (- 8 (remainder j 8))))))
  336. (if (< width (quotient cwidth 2))
  337. i
  338. (loop (+ i 1) j (- width cwidth))))))))
  339. ;;; Text is inserted into a JTEXTREE before a given line and character by
  340. ;;; calling the following procedure. Markers will be corrected as required.
  341. ;;; Justification is an option as this procedure is called from inside the
  342. ;;; justifier.
  343. (define (INSERT-JTEXTREE jt line char text justify)
  344. (let ((lines (jtextree-lines jt))
  345. (textree (jtextree-textree jt)))
  346. (if (>= line lines)
  347. (begin (jtextree-lines! jt (+ 1 lines))
  348. (jtextree-textree! jt (insert-textree textree line text)))
  349. (let* ((old-text (textree-text textree line))
  350. (old-len (string-length old-text)))
  351. (jtextree-textree! jt
  352. (textree-text! textree line
  353. (string-append
  354. (substring old-text 0 char)
  355. text
  356. (if (>= char old-len)
  357. ""
  358. (substring old-text char old-len)))))))
  359. (for-each
  360. (lambda (marker)
  361. (when (and (eq? (marker-line marker)
  362. line)
  363. (>= (marker-char marker)
  364. char))
  365. (marker-char! marker
  366. (+ (marker-char marker)
  367. (string-length text)))
  368. (marker-changed! marker #t)))
  369. (jtextree-markers jt))
  370. (changed-line-jtextree jt line)
  371. (when justify
  372. (if (positive? line)
  373. (justify-jtextree jt (- line 1) 2)
  374. (justify-jtextree jt line 1))
  375. (if (> (jtextree-lines jt) lines)
  376. (jtextree-last-changed! jt (- (jtextree-lines jt) 1))))))
  377. ;;; All text from one character position through another is deleted from a
  378. ;;; JTEXTREE by the following procedure. Justification is an option as this
  379. ;;; procedure is called from inside the justifier. Markers outside the range
  380. ;;; are corrected. Markers inside the range are left unchanged, but the change
  381. ;;; flag is set.
  382. (define (DELETE-JTEXTREE jt line0 char0 line1 char1 justify)
  383. (let* ((textree (jtextree-textree jt))
  384. (lines (jtextree-lines jt))
  385. (deleted-lines 0)
  386. (text0 (textree-text textree line0))
  387. (text1 (textree-text textree line1))
  388. (len1 (string-length text1))
  389. (new-text (string-append (substring text0 0 char0)
  390. (if (>= char1 (- len1 1))
  391. ""
  392. (substring text1 (+ 1 char1) len1)))))
  393. (define (DELETE-LINE line)
  394. (when (< line (- lines deleted-lines))
  395. (set! textree (delete-textree textree line))
  396. (set! deleted-lines (+ 1 deleted-lines))))
  397. (do ((i (+ line0 1) (+ i 1)))
  398. ((> i line1))
  399. (delete-line (+ 1 line0)))
  400. (if (eq? new-text "")
  401. (delete-line line0)
  402. (set! textree (textree-text! textree line0 new-text)))
  403. (for-each
  404. (lambda (marker)
  405. (let ((mline (marker-line marker))
  406. (mchar (marker-char marker)))
  407. (cond ((or (< mline line0)
  408. (and (eq? mline line0) (< mchar char0)))
  409. #t)
  410. ((> mline line1)
  411. (marker-line! marker (- mline deleted-lines))
  412. (marker-changed! marker #t))
  413. ((and (eq? mline line1) (> mchar char1))
  414. (if (and (eq? new-text "") (> line0 0))
  415. (let ((prev-text (textree-text textree
  416. (- line0 1))))
  417. (marker-line! marker (- line0 1))
  418. (marker-char! marker
  419. (string-length prev-text)))
  420. (begin (marker-char! marker
  421. (- mchar char1 (- char0) 1))
  422. (marker-line! marker
  423. (max 0 (- mline
  424. deleted-lines)))))
  425. (marker-changed! marker #t))
  426. (else (marker-changed! marker #t)))))
  427. (jtextree-markers jt))
  428. (jtextree-lines! jt (- lines deleted-lines))
  429. (jtextree-textree! jt textree)
  430. (changed-line-jtextree jt line0)
  431. (when justify
  432. (if (positive? line0)
  433. (justify-jtextree jt (- line0 1) 2)
  434. (justify-jtextree jt line0 1))
  435. (if (< (jtextree-lines jt) lines)
  436. (jtextree-last-changed! jt (- (jtextree-lines jt) 1))))))
  437. ;;; Lines of text in a JTEXTREE may be accessed by the following two
  438. ;;; procedures. Changed lines will be noted in the JTEXTREE structure.
  439. (define (JTEXTREE-TEXT jt line)
  440. (textree-text (jtextree-textree jt) line))
  441. (define (JTEXTREE-TEXT! jt line text)
  442. (jtextree-textree! jt (textree-text! (jtextree-textree jt) line text))
  443. (changed-line-jtextree jt line))