PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/mouseedit.sc

https://bitbucket.org/bunny351/ezd
Scala | 440 lines | 395 code | 45 blank | 0 comment | 6 complexity | b1a70719d4e43b880473c66bfbecb88b MD5 | raw file
  1. ;;; ezd - easy drawing for X11 displays.
  2. ;;;
  3. ;;; The procedures in this module implement a mouse based editor for use inside
  4. ;;; a TEXT-DRAWING. In order to test the utility of attributes, all
  5. ;;; communication with the text drawing is via attributes.
  6. ;* Copyright 1990-1993 Digital Equipment Corporation
  7. ;* All Rights Reserved
  8. ;*
  9. ;* Permission to use, copy, and modify this software and its documentation is
  10. ;* hereby granted only under the following terms and conditions. Both the
  11. ;* above copyright notice and this permission notice must appear in all copies
  12. ;* of the software, derivative works or modified versions, and any portions
  13. ;* thereof, and both notices must appear in supporting documentation.
  14. ;*
  15. ;* Users of this software agree to the terms and conditions set forth herein,
  16. ;* and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free
  17. ;* right and license under any changes, enhancements or extensions made to the
  18. ;* core functions of the software, including but not limited to those affording
  19. ;* compatibility with other hardware or software environments, but excluding
  20. ;* applications which incorporate this software. Users further agree to use
  21. ;* their best efforts to return to Digital any such changes, enhancements or
  22. ;* extensions that they make and inform Digital of noteworthy uses of this
  23. ;* software. Correspondence should be provided to Digital at:
  24. ;*
  25. ;* Director of Licensing
  26. ;* Western Research Laboratory
  27. ;* Digital Equipment Corporation
  28. ;* 250 University Avenue
  29. ;* Palo Alto, California 94301
  30. ;*
  31. ;* This software may be distributed (but not offered for sale or transferred
  32. ;* for compensation) to third parties, provided such third parties agree to
  33. ;* abide by the terms and conditions of this notice.
  34. ;*
  35. ;* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
  36. ;* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
  37. ;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
  38. ;* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  39. ;* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  40. ;* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  41. ;* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  42. ;* SOFTWARE.
  43. (module mouseedit (with ezd))
  44. (include "struct.sch")
  45. (include "display.sch")
  46. (include "ezd.sch")
  47. (include "events.sch")
  48. (include "interact.sch")
  49. (include "xternal.sch")
  50. ;;; When a TEXT-DRAWING is created, the following procedure is called to
  51. ;;; install mouse based editting.
  52. (define (MOUSE-EDIT-INIT drawing object options)
  53. (let* ((read-only (memq 'read-only options))
  54. (current-window #f)
  55. (first-line #f)
  56. (last-line #f)
  57. (slider #f)
  58. (button1 #f)
  59. (button1down-line #f)
  60. (button1down-char #f)
  61. (cursor-line 0)
  62. (cursor-char 0)
  63. (selection #f)
  64. (begin-line 0)
  65. (begin-char 0)
  66. (end-line 0)
  67. (end-char 0)
  68. (undo-cursor #f)
  69. (undo-text #f))
  70. ;;; Change cursor on drawing entry and exit. Find out viewed lines
  71. ;;; and slider name on entry.
  72. (define (ENTER)
  73. (when (not current-window)
  74. (set! current-window *user-event-window*)
  75. (let ((view (get-attribute drawing object
  76. `(view ,current-window))))
  77. (set! first-line (car view))
  78. (set! last-line (cadr view))
  79. (set! slider (caddr view)))
  80. (set! button1 #f)
  81. (ezd `(save-cursor ,current-window)
  82. `(set-cursor ,current-window xc_xterm))))
  83. (define (EXIT)
  84. (when (and current-window
  85. (or (not (eq? (car *user-event-misc*)
  86. current-window))
  87. (not (eq? (cadr *user-event-misc*) drawing))))
  88. (ezd `(restore-cursor ,current-window))
  89. (set! current-window #f)))
  90. ;;; Covert the mouse position in the current event to the cursor
  91. ;;; position stored here. Note that the screen cursor is not
  92. ;;; updated at this time.
  93. (define (MOUSE->CURSOR)
  94. (let* ((line-char-text (get-attribute drawing object
  95. `(xy->line-char-text
  96. ,*user-event-x*
  97. ,*user-event-y*)))
  98. (line (car line-char-text))
  99. (char (cadr line-char-text))
  100. (text (caddr line-char-text)))
  101. (set! cursor-line line)
  102. (set! cursor-char
  103. (if (and (positive? char)
  104. (eq? char (string-length text))
  105. (eq? (string-ref text (- char 1))
  106. #\newline))
  107. (- char 1)
  108. char))))
  109. ;;; Query the drawing for the current cursor position. If the
  110. ;;; cursor is not visibile in the current window, then scroll it to
  111. ;;; make it visible.
  112. (define (READ-CURSOR)
  113. (let ((line-char (get-attribute drawing object 'cursor)))
  114. (set! cursor-line (car line-char))
  115. (set! cursor-char (cadr line-char))
  116. (if (not (<= first-line cursor-line last-line))
  117. (let ((newfirst (if (< cursor-line first-line)
  118. cursor-line
  119. (+ first-line
  120. (- cursor-line last-line)))))
  121. (set! last-line
  122. (+ last-line (- newfirst first-line)))
  123. (set! first-line newfirst)
  124. (if slider
  125. (set-attributes slider 'slider
  126. `(value ,newfirst)))
  127. (set-attributes drawing object
  128. '(mouse-edit)
  129. `(scroll ,current-window ,newfirst))))))
  130. ;;; Mouse button 1 going down sets the cursor and clears any
  131. ;;; current selection.
  132. (define (BUTTON1DOWN)
  133. (mouse->cursor)
  134. (set! button1down-line cursor-line)
  135. (set! button1down-char cursor-char)
  136. (set! selection #f)
  137. (set! button1 #t)
  138. (set-attributes drawing object
  139. '(mouse-edit)
  140. `(cursor ,cursor-line ,cursor-char) '(highlight)))
  141. ;;; Motion with mouse button 1 down causes the cursor to move and
  142. ;;; starts/extends the current selection. Completion of the
  143. ;;; selection causes the selection to be copied to the X cut buffer
  144. ;;; when in READ-ONLY mode.
  145. (define (MOTION-BUTTON1UP)
  146. (if button1
  147. (let ((event *user-event-xevent*))
  148. (mouse->cursor)
  149. (set! button1 *mouse-button1*)
  150. (extend-selection cursor-line cursor-char)
  151. (if (and read-only (not button1))
  152. (cut/copy #f (xevent-xbutton-time event))))))
  153. ;;; The current selection is extended by the following function. The
  154. ;;; cursor is placed at the end of the selection. If the selection
  155. ;;; turns out to be null when the button comes up, it disappears.
  156. (define (EXTEND-SELECTION line char)
  157. (if (lc<? button1down-line button1down-char line char)
  158. (let* ((lc (inc-line-char drawing object line char #f
  159. -1))
  160. (new-line (car lc))
  161. (new-char (cadr lc)))
  162. (set! begin-line button1down-line)
  163. (set! begin-char button1down-char)
  164. (set! end-line new-line)
  165. (set! end-char new-char))
  166. (let* ((lc (inc-line-char drawing object button1down-line
  167. button1down-char #f -1))
  168. (new-line (car lc))
  169. (new-char (cadr lc)))
  170. (set! begin-line line)
  171. (set! begin-char char)
  172. (set! end-line new-line)
  173. (set! end-char new-char)))
  174. (if (lc=? line char button1down-line button1down-char)
  175. (begin (set! selection #f)
  176. (set! cursor-line line)
  177. (set! cursor-char char)
  178. (set-attributes drawing object
  179. '(mouse-edit)
  180. `(cursor ,line ,char) '(highlight)))
  181. (let ((lc (inc-line-char drawing object end-line end-char
  182. #f 1)))
  183. (set! cursor-line (car lc))
  184. (set! cursor-char (cadr lc))
  185. (set! selection #t)
  186. (set-attributes drawing object
  187. '(mouse-edit)
  188. `(cursor ,cursor-line ,cursor-char)
  189. `(highlight ,begin-line ,begin-char
  190. ,end-line ,end-char)))))
  191. ;;; Keyboard input is handled here.
  192. (define (KEYPRESS)
  193. (let* ((key (car *user-event-misc*))
  194. (keysym (cadr *user-event-misc*))
  195. (ascii-code (if (equal? key "") 0
  196. (char->integer (string-ref key 0))))
  197. (key-state (xevent-xkey-state *user-event-xevent*))
  198. (time (xevent-xkey-time *user-event-xevent*)))
  199. (cond (read-only (ezd '(bell)))
  200. ((or (eq? ascii-code 8) ;;; control-h
  201. (eq? ascii-code 127));;; backspace
  202. (if selection
  203. (delete-selection)
  204. (delete-before-cursor)))
  205. ((eq? ascii-code 13) ;;; return
  206. (delete-selection)
  207. (set-attributes drawing object
  208. '(mouse-edit)
  209. `(insert ,cursor-line ,cursor-char
  210. ,(list->string '(#\newline))))
  211. (read-cursor)
  212. (unless (zero? cursor-char)
  213. (set! cursor-line (+ cursor-line 1))
  214. (set! cursor-char 0)
  215. (set-attributes drawing object
  216. '(mouse-edit)
  217. `(cursor ,cursor-line ,cursor-char))
  218. (read-cursor)))
  219. ((not (zero? (bit-and key-state mod1mask)))
  220. (cond ((equal? key "z") (undo))
  221. ((equal? key "x") (cut/copy #t time))
  222. ((equal? key "c") (cut/copy #f time))
  223. ((equal? key "v") (paste))
  224. (else (ezd '(bell)))))
  225. ((or (and (string<=? " " key)
  226. (string<=? key "~"))
  227. (eq? ascii-code 9)) ;;; tab
  228. (delete-selection)
  229. (set-attributes drawing object
  230. '(mouse-edit)
  231. `(insert ,cursor-line ,cursor-char ,key))
  232. (read-cursor))
  233. ((<= xk_left keysym xk_down)
  234. (cursor-motion keysym))
  235. ((and (not (<= xk_shift_l keysym xk_hyper_r))
  236. (not (= keysym xk_multi_key)))
  237. (ezd '(bell))))))
  238. ;;; Delete the currently selected text.
  239. (define (DELETE-SELECTION)
  240. (when selection
  241. (set! selection #f)
  242. (set! undo-cursor (list begin-line begin-char))
  243. (set! undo-text (selection->string))
  244. (set-attributes drawing object
  245. '(mouse-edit)
  246. '(highlight)
  247. `(delete ,begin-line ,begin-char
  248. ,end-line ,end-char))
  249. (read-cursor)))
  250. ;;; Delete the character behind the cursor.
  251. (define (DELETE-BEFORE-CURSOR)
  252. (when (lc>? cursor-line cursor-char 0 0)
  253. (if (zero? cursor-char) (cursor-motion xk_left))
  254. (cursor-motion xk_left)
  255. (set-attributes drawing object
  256. '(mouse-edit)
  257. `(delete ,cursor-line ,cursor-char
  258. ,cursor-line ,cursor-char))
  259. (read-cursor)
  260. (if (zero? cursor-char) (cursor-motion xk_left))))
  261. ;;; Undo the last edit command.
  262. (define (UNDO)
  263. (when undo-cursor
  264. (set-attributes drawing object
  265. '(mouse-edit)
  266. `(cursor ,@undo-cursor)
  267. `(insert ,@undo-cursor ,undo-text)
  268. '(highlight))
  269. (set! undo-cursor #f)
  270. (set! undo-text #f)))
  271. ;;; Return a string containing the current selection.
  272. (define (SELECTION->STRING)
  273. (define CUT-BUFFER #f)
  274. (let loop ((i begin-line) (len 0))
  275. (if (<= i end-line)
  276. (let* ((whole-line (get-attribute drawing object
  277. `(text-line ,i)))
  278. (line (if (or (< begin-line i end-line)
  279. (eq? whole-line ""))
  280. whole-line
  281. (substring whole-line
  282. (if (eq? i begin-line)
  283. begin-char
  284. 0)
  285. (if (eq? i end-line)
  286. (min (+ end-char 1)
  287. (string-length
  288. whole-line))
  289. (string-length
  290. whole-line)))))
  291. (line-len (string-length line)))
  292. (case (and (< i end-line)
  293. (positive? line-len)
  294. (string-ref line (- line-len 1)))
  295. ((#f #\tab #\space #\newline)
  296. (loop (+ i 1) (+ line-len len)))
  297. ((#\.)
  298. (loop (+ i 1) (+ line-len len 2)))
  299. (else
  300. (loop (+ i 1) (+ line-len len 1))))
  301. (do ((j 0 (+ j 1)))
  302. ((= j line-len))
  303. (string-set! cut-buffer (+ j len)
  304. (string-ref line j))))
  305. (set! cut-buffer (make-string len #\space))))
  306. cut-buffer)
  307. ;;; Cut or copy the current selection to the X selection.
  308. (define (CUT/COPY cut time)
  309. (let ((cut-buffer (selection->string)))
  310. (xsetselectionowner *dpy* xa_primary none time)
  311. (xstorebytes *dpy*
  312. (type/value->pointer 'charap cut-buffer)
  313. (string-length cut-buffer))
  314. (if cut (delete-selection))))
  315. ;;; Paste the current X selection into the document.
  316. (define (PASTE)
  317. (let* ((ptr-cnt (xfetchbytes *dpy*))
  318. (ptr (pointer-value (car ptr-cnt)))
  319. (cnt (cadr ptr-cnt))
  320. (buffer (make-string cnt)))
  321. (delete-selection)
  322. (do ((i 0 (+ i 1)))
  323. ((= i cnt))
  324. (string-set! buffer i
  325. (integer->char (c-byte-ref ptr i))))
  326. (if (not (zero? ptr)) (xfree ptr))
  327. (set-attributes drawing object
  328. '(mouse-edit)
  329. `(insert ,cursor-line ,cursor-char ,buffer))
  330. (read-cursor)))
  331. ;;; Handle a cursor character.
  332. (define (CURSOR-MOTION keysym)
  333. (let ((line-char (inc-line-char drawing object
  334. cursor-line cursor-char
  335. (cond ((eq? keysym xk_up) -1)
  336. ((eq? keysym xk_down) 1)
  337. (else #f))
  338. (cond ((eq? keysym xk_left) -1)
  339. ((eq? keysym xk_right) 1)
  340. (else #f)))))
  341. (set! cursor-line (car line-char))
  342. (set! cursor-char (cadr line-char))
  343. (set! selection #f)
  344. (set-attributes drawing object
  345. '(mouse-edit)
  346. `(cursor ,cursor-line ,cursor-char)
  347. '(highlight))
  348. (read-cursor)))
  349. ;;; Get current cursor position and highlight information from
  350. ;;; the drawing.
  351. (let ((cursor-line-char (get-attribute drawing object 'cursor))
  352. (l-c-l-c (get-attribute drawing object 'highlight)))
  353. (set! cursor-line (car cursor-line-char))
  354. (set! cursor-char (cadr cursor-line-char))
  355. (when (not (negative? (cadr l-c-l-c)))
  356. (set! selection #t)
  357. (set! begin-line (car l-c-l-c))
  358. (set! begin-char (cadr l-c-l-c))
  359. (set! end-line (caddr l-c-l-c))
  360. (set! end-char (cadddr l-c-l-c))))
  361. ;;; Install event handlers.
  362. (ezd '(save-drawing)
  363. `(set-drawing ,drawing)
  364. `(when * enter ,enter)
  365. `(when * exit ,exit)
  366. `(when * button1down ,button1down)
  367. `(when * motion ,motion-button1up)
  368. `(when * button1up ,motion-button1up)
  369. `(when * keypress ,keypress)
  370. '(restore-drawing))))
  371. ;;; Booleans for comparing line/character positions.
  372. (define (LC=? l0 c0 l1 c1) (and (eq? l0 l1) (eq? c0 c1)))
  373. (define (LC<? l0 c0 l1 c1) (or (< l0 l1) (and (eq? l0 l1) (< c0 c1))))
  374. (define (LC>? l0 c0 l1 c1) (or (> l0 l1) (and (eq? l0 l1) (> c0 c1))))
  375. (define (LC<=? l0 c0 l1 c1) (not (lc>? l0 c0 l1 c1)))
  376. (define (LC>=? l0 c0 l1 c1) (not (lc<? l0 c0 l1 c1)))
  377. ;;; Procedure to move a line-character position either a number of lines or a
  378. ;;; number of characters. A list consisting of the new line and character
  379. ;;; positions is returned.
  380. (define (INC-LINE-CHAR drawing object line char delta-line delta-char)
  381. (if delta-line
  382. (let* ((line (min (get-attribute drawing object 'lines)
  383. (max 0 (+ line delta-line))))
  384. (text (get-attribute drawing object `(text-line ,line)))
  385. (text-len (string-length text))
  386. (char (min char text-len)))
  387. (list line char))
  388. (let* ((text (get-attribute drawing object `(text-line ,line)))
  389. (text-len (string-length text))
  390. (char (+ delta-char char)))
  391. (cond ((negative? char)
  392. (inc-line-char drawing object line 1000000 -1 #f))
  393. ((<= char text-len)
  394. (list line char))
  395. (else (inc-line-char drawing object line 0 1 #f))))))