Convert consecutive FSF copyright years to ranges.
[bpt/emacs.git] / lisp / textmodes / picture.el
CommitLineData
55535639 1;;; picture.el --- "Picture mode" -- editing using quarter-plane screen model
6594deb0 2
73b0cd50 3;; Copyright (C) 1985, 1994, 2001-2011 Free Software Foundation, Inc.
3a801d0c 4
e5167999
ER
5;; Author: K. Shane Hartman
6;; Maintainer: FSF
82168689 7;; Keywords: convenience wp
695d13c7
BP
8
9;; This file is part of GNU Emacs.
10
1fecc8fe 11;; GNU Emacs is free software: you can redistribute it and/or modify
695d13c7 12;; it under the terms of the GNU General Public License as published by
1fecc8fe
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
695d13c7
BP
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
1fecc8fe 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
695d13c7 23
edbd2f74
ER
24;;; Commentary:
25
eaae8106 26;; This code provides the picture-mode commands documented in the Emacs
edbd2f74
ER
27;; manual. The screen is treated as a semi-infinite quarter-plane with
28;; support for rectangle operations and `etch-a-sketch' character
29;; insertion in any of eight directions.
30
e5167999 31;;; Code:
695d13c7 32
d1ebc62e 33(defgroup picture nil
381194d0 34 "Picture mode --- editing using quarter-plane screen model."
d1ebc62e 35 :prefix "picture-"
eba5b4dd 36 :group 'wp)
d1ebc62e
SE
37
38(defcustom picture-rectangle-ctl ?+
1fc7dabf 39 "Character `picture-draw-rectangle' uses for top left corners."
d1ebc62e
SE
40 :type 'character
41 :group 'picture)
42(defcustom picture-rectangle-ctr ?+
1fc7dabf 43 "Character `picture-draw-rectangle' uses for top right corners."
d1ebc62e
SE
44 :type 'character
45 :group 'picture)
46(defcustom picture-rectangle-cbr ?+
1fc7dabf 47 "Character `picture-draw-rectangle' uses for bottom right corners."
d1ebc62e
SE
48 :type 'character
49 :group 'picture)
50(defcustom picture-rectangle-cbl ?+
1fc7dabf 51 "Character `picture-draw-rectangle' uses for bottom left corners."
d1ebc62e
SE
52 :type 'character
53 :group 'picture)
54(defcustom picture-rectangle-v ?|
1fc7dabf 55 "Character `picture-draw-rectangle' uses for vertical lines."
d1ebc62e
SE
56 :type 'character
57 :group 'picture)
58(defcustom picture-rectangle-h ?-
1fc7dabf 59 "Character `picture-draw-rectangle' uses for horizontal lines."
d1ebc62e
SE
60 :type 'character
61 :group 'picture)
62
695d13c7 63
695d13c7
BP
64;; Picture Movement Commands
65
2697c1f3
KH
66;; When a cursor is on a wide-column character (e.g. Chinese,
67;; Japanese, Korean), this variable tells the desired current column
68;; which may be different from (current-column).
69(defvar picture-desired-column 0)
70
71;; If the value of picture-desired-column is far from the current
72;; column, or if the arg ADJUST-TO-CURRENT is non-nil, set it to the
73;; current column. Return the current column.
74(defun picture-update-desired-column (adjust-to-current)
75 (let ((current-column (current-column)))
76 (if (or adjust-to-current
77 (< picture-desired-column (1- current-column))
78 (> picture-desired-column (1+ current-column)))
79 (setq picture-desired-column current-column))
80 current-column))
81
ca9c7579
ER
82(defun picture-beginning-of-line (&optional arg)
83 "Position point at the beginning of the line.
84With ARG not nil, move forward ARG - 1 lines first.
85If scan reaches end of buffer, stop there without error."
86 (interactive "P")
87 (if arg (forward-line (1- (prefix-numeric-value arg))))
88 (beginning-of-line)
5f6a0375 89 (setq picture-desired-column 0))
ca9c7579 90
695d13c7
BP
91(defun picture-end-of-line (&optional arg)
92 "Position point after last non-blank character on current line.
93With ARG not nil, move forward ARG - 1 lines first.
94If scan reaches end of buffer, stop there without error."
95 (interactive "P")
96 (if arg (forward-line (1- (prefix-numeric-value arg))))
97 (beginning-of-line)
ca9c7579 98 (skip-chars-backward " \t" (prog1 (point) (end-of-line)))
5f6a0375 99 (setq picture-desired-column (current-column)))
695d13c7 100
bac8c2e7 101(defun picture-forward-column (arg &optional interactive)
695d13c7
BP
102 "Move cursor right, making whitespace if necessary.
103With argument, move that many columns."
bac8c2e7 104 (interactive "p\nd")
4855897e
RS
105 (let (deactivate-mark)
106 (picture-update-desired-column interactive)
107 (setq picture-desired-column (max 0 (+ picture-desired-column arg)))
108 (let ((current-column (move-to-column picture-desired-column t)))
109 (if (and (> current-column picture-desired-column)
110 (< arg 0))
111 ;; It seems that we have just tried to move to the right
112 ;; column of a multi-column character.
113 (forward-char -1)))))
695d13c7 114
bac8c2e7 115(defun picture-backward-column (arg &optional interactive)
695d13c7
BP
116 "Move cursor left, making whitespace if necessary.
117With argument, move that many columns."
bac8c2e7
RS
118 (interactive "p\nd")
119 (picture-update-desired-column interactive)
ad8fb8ae 120 (picture-forward-column (- arg)))
695d13c7
BP
121
122(defun picture-move-down (arg)
123 "Move vertically down, making whitespace if necessary.
124With argument, move that many lines."
125 (interactive "p")
4855897e
RS
126 (let (deactivate-mark)
127 (picture-update-desired-column nil)
128 (picture-newline arg)
129 (let ((current-column (move-to-column picture-desired-column t)))
130 (if (> current-column picture-desired-column)
131 (forward-char -1)))))
695d13c7 132
99b3bc61 133(defvar picture-vertical-step 0
695d13c7
BP
134 "Amount to move vertically after text character in Picture mode.")
135
99b3bc61 136(defvar picture-horizontal-step 1
695d13c7
BP
137 "Amount to move horizontally after text character in Picture mode.")
138
139(defun picture-move-up (arg)
140 "Move vertically up, making whitespace if necessary.
141With argument, move that many lines."
142 (interactive "p")
2697c1f3 143 (picture-update-desired-column nil)
695d13c7
BP
144 (picture-move-down (- arg)))
145
146(defun picture-movement-right ()
147 "Move right after self-inserting character in Picture mode."
148 (interactive)
149 (picture-set-motion 0 1))
150
151(defun picture-movement-left ()
152 "Move left after self-inserting character in Picture mode."
153 (interactive)
154 (picture-set-motion 0 -1))
155
156(defun picture-movement-up ()
157 "Move up after self-inserting character in Picture mode."
158 (interactive)
159 (picture-set-motion -1 0))
160
161(defun picture-movement-down ()
162 "Move down after self-inserting character in Picture mode."
163 (interactive)
164 (picture-set-motion 1 0))
165
2697c1f3
KH
166(defun picture-movement-nw (&optional arg)
167 "Move up and left after self-inserting character in Picture mode.
168With prefix argument, move up and two-column left."
169 (interactive "P")
170 (picture-set-motion -1 (if arg -2 -1)))
695d13c7 171
2697c1f3
KH
172(defun picture-movement-ne (&optional arg)
173 "Move up and right after self-inserting character in Picture mode.
174With prefix argument, move up and two-column right."
175 (interactive "P")
176 (picture-set-motion -1 (if arg 2 1)))
695d13c7 177
2697c1f3
KH
178(defun picture-movement-sw (&optional arg)
179 "Move down and left after self-inserting character in Picture mode.
180With prefix argument, move down and two-column left."
181 (interactive "P")
182 (picture-set-motion 1 (if arg -2 -1)))
695d13c7 183
2697c1f3
KH
184(defun picture-movement-se (&optional arg)
185 "Move down and right after self-inserting character in Picture mode.
186With prefix argument, move down and two-column right."
187 (interactive "P")
188 (picture-set-motion 1 (if arg 2 1)))
695d13c7
BP
189
190(defun picture-set-motion (vert horiz)
191 "Set VERTICAL and HORIZONTAL increments for movement in Picture mode.
192The mode line is updated to reflect the current direction."
193 (setq picture-vertical-step vert
194 picture-horizontal-step horiz)
195 (setq mode-name
196 (format "Picture:%s"
2697c1f3
KH
197 (nth (+ 2 (% horiz 3) (* 5 (1+ (% vert 2))))
198 '(wnw nw up ne ene Left left none right Right
199 wsw sw down se ese))))
00dbaf0a 200 (force-mode-line-update)
695d13c7
BP
201 (message ""))
202
203(defun picture-move ()
204 "Move in direction of `picture-vertical-step' and `picture-horizontal-step'."
2697c1f3
KH
205 (if (/= picture-vertical-step 0)
206 (picture-move-down picture-vertical-step))
207 (if (/= picture-horizontal-step 0)
208 (picture-forward-column picture-horizontal-step)))
695d13c7
BP
209
210(defun picture-motion (arg)
211 "Move point in direction of current picture motion in Picture mode.
212With ARG do it that many times. Useful for delineating rectangles in
213conjunction with diagonal picture motion.
214Do \\[command-apropos] picture-movement to see commands which control motion."
215 (interactive "p")
216 (picture-move-down (* arg picture-vertical-step))
217 (picture-forward-column (* arg picture-horizontal-step)))
218
219(defun picture-motion-reverse (arg)
220 "Move point in direction opposite of current picture motion in Picture mode.
221With ARG do it that many times. Useful for delineating rectangles in
222conjunction with diagonal picture motion.
15693bc3 223Do \\[command-apropos] picture-movement to see commands which control motion."
695d13c7
BP
224 (interactive "p")
225 (picture-motion (- arg)))
226
15693bc3 227(defun picture-mouse-set-point (event)
de02effd 228 "Move point to the position of EVENT, making whitespace if necessary."
15693bc3 229 (interactive "e")
de02effd
CY
230 (let ((position (event-start event)))
231 (unless (posn-area position) ; Ignore EVENT unless in text area
232 (let* ((window (posn-window position))
233 (frame (if (framep window) window (window-frame window)))
234 (pair (posn-x-y position))
235 (start-pos (window-start window))
236 (start-pair (posn-x-y (posn-at-point start-pos)))
237 (dx (- (car pair) (car start-pair)))
238 (dy (- (cdr pair) (cdr start-pair)))
239 (char-ht (frame-char-height frame))
240 (spacing (when (display-graphic-p frame)
241 (or (with-current-buffer (window-buffer window)
242 line-spacing)
243 (frame-parameter frame 'line-spacing))))
244 rows cols)
245 (cond ((floatp spacing)
246 (setq spacing (truncate (* spacing char-ht))))
247 ((null spacing)
248 (setq spacing 0)))
249 (goto-char start-pos)
250 (picture-move-down (/ dy (+ char-ht spacing)))
251 (picture-forward-column (/ dx (frame-char-width frame)))))))
15693bc3 252
695d13c7
BP
253\f
254;; Picture insertion and deletion.
255
d792910f 256(defun picture-insert (ch arg)
2697c1f3
KH
257 (let* ((width (char-width ch))
258 ;; We must be sure that the succeeding insertion won't delete
259 ;; the just inserted character.
260 (picture-horizontal-step
261 (if (and (= picture-vertical-step 0)
262 (> width 1)
263 (< (abs picture-horizontal-step) 2))
264 (* picture-horizontal-step 2)
265 picture-horizontal-step)))
266 (while (> arg 0)
267 (setq arg (1- arg))
268 (if (/= picture-desired-column (current-column))
5ed5b2c2 269 (move-to-column picture-desired-column t))
2697c1f3
KH
270 (let ((col (+ picture-desired-column width)))
271 (or (eolp)
272 (let ((pos (point)))
5ed5b2c2 273 (move-to-column col t)
2697c1f3
KH
274 (delete-region pos (point)))))
275 (insert ch)
276 (forward-char -1)
277 (picture-move))))
d792910f 278
695d13c7
BP
279(defun picture-self-insert (arg)
280 "Insert this character in place of character previously at the cursor.
281The cursor then moves in the direction you previously specified
282with the commands `picture-movement-right', `picture-movement-up', etc.
283Do \\[command-apropos] `picture-movement' to see those commands."
284 (interactive "p")
2697c1f3 285 (picture-update-desired-column (not (eq this-command last-command)))
d792910f 286 (picture-insert last-command-event arg)) ; Always a character in this case.
695d13c7
BP
287
288(defun picture-clear-column (arg)
289 "Clear out ARG columns after point without moving."
290 (interactive "p")
2697c1f3
KH
291 (let* ((original-col (current-column))
292 (target-col (max 0 (+ original-col arg)))
293 pos)
5ed5b2c2 294 (move-to-column target-col t)
2697c1f3
KH
295 (setq pos (point))
296 (move-to-column original-col)
297 (delete-region pos (point))
695d13c7 298 (save-excursion
2697c1f3
KH
299 (indent-to (max target-col original-col))))
300 (setq picture-desired-column (current-column)))
695d13c7
BP
301
302(defun picture-backward-clear-column (arg)
303 "Clear out ARG columns before point, moving back over them."
304 (interactive "p")
305 (picture-clear-column (- arg)))
306
307(defun picture-clear-line (arg)
308 "Clear out rest of line; if at end of line, advance to next line.
309Cleared-out line text goes into the kill ring, as do newlines that are
310advanced over. With argument, clear out (and save in kill ring) that
311many lines."
312 (interactive "P")
313 (if arg
314 (progn
315 (setq arg (prefix-numeric-value arg))
316 (kill-line arg)
317 (newline (if (> arg 0) arg (- arg))))
318 (if (looking-at "[ \t]*$")
319 (kill-ring-save (point) (progn (forward-line 1) (point)))
320 (kill-region (point) (progn (end-of-line) (point))))))
321
322(defun picture-newline (arg)
323 "Move to the beginning of the following line.
324With argument, moves that many lines (up, if negative argument);
325always moves to the beginning of a line."
326 (interactive "p")
327 (if (< arg 0)
328 (forward-line arg)
329 (while (> arg 0)
330 (end-of-line)
331 (if (eobp) (newline) (forward-char 1))
5f6a0375 332 (setq arg (1- arg)))))
695d13c7
BP
333
334(defun picture-open-line (arg)
335 "Insert an empty line after the current line.
336With positive argument insert that many lines."
337 (interactive "p")
338 (save-excursion
339 (end-of-line)
5f6a0375 340 (open-line arg)))
695d13c7
BP
341
342(defun picture-duplicate-line ()
343 "Insert a duplicate of the current line, below it."
344 (interactive)
345 (save-excursion
346 (let ((contents
347 (buffer-substring
348 (progn (beginning-of-line) (point))
349 (progn (picture-newline 1) (point)))))
350 (forward-line -1)
351 (insert contents))))
352
5c927015
RS
353;; Like replace-match, but overwrites.
354(defun picture-replace-match (newtext fixedcase literal)
355 (let (ocolumn change pos)
356 (goto-char (setq pos (match-end 0)))
357 (setq ocolumn (current-column))
358 ;; Make the replacement and undo it, to see how it changes the length.
359 (let ((buffer-undo-list nil)
360 list1)
361 (replace-match newtext fixedcase literal)
362 (setq change (- (current-column) ocolumn))
363 (setq list1 buffer-undo-list)
364 (while list1
365 (setq list1 (primitive-undo 1 list1))))
366 (goto-char pos)
367 (if (> change 0)
368 (delete-region (point)
369 (progn
d792910f 370 (move-to-column (+ change (current-column)) t)
5c927015
RS
371 (point))))
372 (replace-match newtext fixedcase literal)
373 (if (< change 0)
a64dc04f 374 (insert-char ?\s (- change)))))
695d13c7
BP
375\f
376;; Picture Tabs
377
d1ebc62e 378(defcustom picture-tab-chars "!-~"
1fc7dabf 379 "A character set which controls behavior of commands.
695d13c7
BP
380\\[picture-set-tab-stops] and \\[picture-tab-search]. It is NOT a
381regular expression, any regexp special characters will be quoted.
382It defines a set of \"interesting characters\" to look for when setting
383\(or searching for) tab stops, initially \"!-~\" (all printing characters).
384For example, suppose that you are editing a table which is formatted thus:
385| foo | bar + baz | 23 *
386| bubbles | and + etc | 97 *
387and that `picture-tab-chars' is \"|+*\". Then invoking
388\\[picture-set-tab-stops] on either of the previous lines would result
389in the following tab stops
390 : : : :
391Another example - \"A-Za-z0-9\" would produce the tab stops
392 : : : :
393
394Note that if you want the character `-' to be in the set, it must be
395included in a range or else appear in a context where it cannot be
396taken for indicating a range (e.g. \"-A-Z\" declares the set to be the
397letters `A' through `Z' and the character `-'). If you want the
398character `\\' in the set it must be preceded by itself: \"\\\\\".
399
400The command \\[picture-tab-search] is defined to move beneath (or to) a
d1ebc62e
SE
401character belonging to this set independent of the tab stops list."
402 :type 'string
403 :group 'picture)
695d13c7
BP
404
405(defun picture-set-tab-stops (&optional arg)
406 "Set value of `tab-stop-list' according to context of this line.
407This controls the behavior of \\[picture-tab]. A tab stop is set at
408every column occupied by an \"interesting character\" that is preceded
409by whitespace. Interesting characters are defined by the variable
410`picture-tab-chars', see its documentation for an example of usage.
411With ARG, just (re)set `tab-stop-list' to its default value. The tab
412stops computed are displayed in the minibuffer with `:' at each stop."
413 (interactive "P")
414 (save-excursion
415 (let (tabs)
416 (if arg
417 (setq tabs (default-value 'tab-stop-list))
418 (let ((regexp (concat "[ \t]+[" (regexp-quote picture-tab-chars) "]")))
419 (beginning-of-line)
420 (let ((bol (point)))
421 (end-of-line)
422 (while (re-search-backward regexp bol t)
423 (skip-chars-forward " \t")
424 (setq tabs (cons (current-column) tabs)))
425 (if (null tabs)
55535639 426 (error "No characters in set %s on this line"
695d13c7
BP
427 (regexp-quote picture-tab-chars))))))
428 (setq tab-stop-list tabs)
429 (let ((blurb (make-string (1+ (nth (1- (length tabs)) tabs)) ?\ )))
430 (while tabs
431 (aset blurb (car tabs) ?:)
432 (setq tabs (cdr tabs)))
433 (message blurb)))))
434
435(defun picture-tab-search (&optional arg)
436 "Move to column beneath next interesting char in previous line.
437With ARG move to column occupied by next interesting character in this
438line. The character must be preceded by whitespace.
439\"interesting characters\" are defined by variable `picture-tab-chars'.
440If no such character is found, move to beginning of line."
441 (interactive "P")
442 (let ((target (current-column)))
443 (save-excursion
444 (if (and (not arg)
445 (progn
446 (beginning-of-line)
447 (skip-chars-backward
448 (concat "^" (regexp-quote picture-tab-chars))
449 (point-min))
450 (not (bobp))))
451 (move-to-column target))
452 (if (re-search-forward
453 (concat "[ \t]+[" (regexp-quote picture-tab-chars) "]")
5ed619e0 454 (line-end-position)
695d13c7
BP
455 'move)
456 (setq target (1- (current-column)))
457 (setq target nil)))
458 (if target
d792910f 459 (move-to-column target t)
695d13c7
BP
460 (beginning-of-line))))
461
462(defun picture-tab (&optional arg)
463 "Tab transparently (just move point) to next tab stop.
464With prefix arg, overwrite the traversed text with spaces. The tab stop
465list can be changed by \\[picture-set-tab-stops] and \\[edit-tab-stops].
466See also documentation for variable `picture-tab-chars'."
467 (interactive "P")
468 (let* ((opoint (point)))
469 (move-to-tab-stop)
470 (if arg
471 (let (indent-tabs-mode
472 (column (current-column)))
473 (delete-region opoint (point))
474 (indent-to column)))))
475\f
476;; Picture Rectangles
477
238b647a 478(defvar picture-killed-rectangle nil
695d13c7
BP
479 "Rectangle killed or copied by \\[picture-clear-rectangle] in Picture mode.
480The contents can be retrieved by \\[picture-yank-rectangle]")
481
482(defun picture-clear-rectangle (start end &optional killp)
483 "Clear and save rectangle delineated by point and mark.
484The rectangle is saved for yanking by \\[picture-yank-rectangle] and replaced
485with whitespace. The previously saved rectangle, if any, is lost. With
486prefix argument, the rectangle is actually killed, shifting remaining text."
487 (interactive "r\nP")
488 (setq picture-killed-rectangle (picture-snarf-rectangle start end killp)))
489
490(defun picture-clear-rectangle-to-register (start end register &optional killp)
491 "Clear rectangle delineated by point and mark into REGISTER.
492The rectangle is saved in REGISTER and replaced with whitespace. With
493prefix argument, the rectangle is actually killed, shifting remaining text."
494 (interactive "r\ncRectangle to register: \nP")
495 (set-register register (picture-snarf-rectangle start end killp)))
496
497(defun picture-snarf-rectangle (start end &optional killp)
498 (let ((column (current-column))
499 (indent-tabs-mode nil))
500 (prog1 (save-excursion
501 (if killp
502 (delete-extract-rectangle start end)
503 (prog1 (extract-rectangle start end)
504 (clear-rectangle start end))))
d792910f 505 (move-to-column column t))))
695d13c7
BP
506
507(defun picture-yank-rectangle (&optional insertp)
508 "Overlay rectangle saved by \\[picture-clear-rectangle]
509The rectangle is positioned with upper left corner at point, overwriting
510existing text. With prefix argument, the rectangle is inserted instead,
511shifting existing text. Leaves mark at one corner of rectangle and
512point at the other (diagonally opposed) corner."
513 (interactive "P")
514 (if (not (consp picture-killed-rectangle))
55535639 515 (error "No rectangle saved")
695d13c7
BP
516 (picture-insert-rectangle picture-killed-rectangle insertp)))
517
2ee658c3
RS
518(defun picture-yank-at-click (click arg)
519 "Insert the last killed rectangle at the position clicked on.
520Also move point to one end of the text thus inserted (normally the end).
521Prefix arguments are interpreted as with \\[yank].
522If `mouse-yank-at-point' is non-nil, insert at point
523regardless of where you click."
524 (interactive "e\nP")
525 (or mouse-yank-at-point (mouse-set-point click))
526 (picture-yank-rectangle arg))
527
695d13c7
BP
528(defun picture-yank-rectangle-from-register (register &optional insertp)
529 "Overlay rectangle saved in REGISTER.
530The rectangle is positioned with upper left corner at point, overwriting
531existing text. With prefix argument, the rectangle is
532inserted instead, shifting existing text. Leaves mark at one corner
533of rectangle and point at the other (diagonally opposed) corner."
534 (interactive "cRectangle from register: \nP")
535 (let ((rectangle (get-register register)))
536 (if (not (consp rectangle))
55535639 537 (error "Register %c does not contain a rectangle" register)
695d13c7
BP
538 (picture-insert-rectangle rectangle insertp))))
539
540(defun picture-insert-rectangle (rectangle &optional insertp)
541 "Overlay RECTANGLE with upper left corner at point.
542Optional argument INSERTP, if non-nil causes RECTANGLE to be inserted.
543Leaves the region surrounding the rectangle."
544 (let ((indent-tabs-mode nil))
545 (if (not insertp)
546 (save-excursion
547 (delete-rectangle (point)
548 (progn
549 (picture-forward-column (length (car rectangle)))
550 (picture-move-down (1- (length rectangle)))
551 (point)))))
552 (push-mark)
553 (insert-rectangle rectangle)))
554
d792910f
RS
555(defun picture-current-line ()
556 "Return the vertical position of point. Top line is 1."
557 (+ (count-lines (point-min) (point))
558 (if (= (current-column) 0) 1 0)))
559
560(defun picture-draw-rectangle (start end)
561 "Draw a rectangle around region."
562 (interactive "*r") ; start will be less than end
563 (let* ((sl (picture-current-line))
564 (sc (current-column))
565 (pvs picture-vertical-step)
566 (phs picture-horizontal-step)
567 (c1 (progn (goto-char start) (current-column)))
568 (r1 (picture-current-line))
569 (c2 (progn (goto-char end) (current-column)))
570 (r2 (picture-current-line))
571 (right (max c1 c2))
572 (left (min c1 c2))
573 (top (min r1 r2))
574 (bottom (max r1 r2)))
e6ce8c42
GM
575 (goto-char (point-min))
576 (forward-line (1- top))
5ed5b2c2 577 (move-to-column left t)
2697c1f3 578 (picture-update-desired-column t)
d792910f
RS
579
580 (picture-movement-right)
581 (picture-insert picture-rectangle-ctl 1)
2697c1f3 582 (picture-insert picture-rectangle-h (- right picture-desired-column))
d792910f
RS
583
584 (picture-movement-down)
585 (picture-insert picture-rectangle-ctr 1)
586 (picture-insert picture-rectangle-v (- bottom (picture-current-line)))
587
588 (picture-movement-left)
589 (picture-insert picture-rectangle-cbr 1)
2697c1f3 590 (picture-insert picture-rectangle-h (- picture-desired-column left))
d792910f
RS
591
592 (picture-movement-up)
593 (picture-insert picture-rectangle-cbl 1)
594 (picture-insert picture-rectangle-v (- (picture-current-line) top))
595
596 (picture-set-motion pvs phs)
e6ce8c42
GM
597 (goto-char (point-min))
598 (forward-line (1- sl))
d792910f
RS
599 (move-to-column sc t)))
600
695d13c7
BP
601\f
602;; Picture Keymap, entry and exit points.
603
2137e978 604(defvar picture-mode-map nil)
695d13c7 605
ca9c7579 606(defun picture-substitute (oldfun newfun)
e70cccc0 607 (define-key picture-mode-map (vector 'remap oldfun) newfun))
ca9c7579 608
695d13c7 609(if (not picture-mode-map)
0ad0f28f 610 (progn
2697c1f3 611 (setq picture-mode-map (make-keymap))
0ad0f28f 612 (picture-substitute 'self-insert-command 'picture-self-insert)
2ae00b00
RS
613 (picture-substitute 'completion-separator-self-insert-command
614 'picture-self-insert)
615 (picture-substitute 'completion-separator-self-insert-autofilling
616 'picture-self-insert)
ca9c7579
ER
617 (picture-substitute 'forward-char 'picture-forward-column)
618 (picture-substitute 'backward-char 'picture-backward-column)
619 (picture-substitute 'delete-char 'picture-clear-column)
47bad81c 620 ;; There are two possibilities for what is normally on DEL.
ca9c7579 621 (picture-substitute 'backward-delete-char-untabify 'picture-backward-clear-column)
47bad81c 622 (picture-substitute 'delete-backward-char 'picture-backward-clear-column)
ca9c7579
ER
623 (picture-substitute 'kill-line 'picture-clear-line)
624 (picture-substitute 'open-line 'picture-open-line)
625 (picture-substitute 'newline 'picture-newline)
0ad0f28f 626 (picture-substitute 'newline-and-indent 'picture-duplicate-line)
ca9c7579
ER
627 (picture-substitute 'next-line 'picture-move-down)
628 (picture-substitute 'previous-line 'picture-move-up)
629 (picture-substitute 'beginning-of-line 'picture-beginning-of-line)
630 (picture-substitute 'end-of-line 'picture-end-of-line)
15693bc3 631 (picture-substitute 'mouse-set-point 'picture-mouse-set-point)
ca9c7579 632
695d13c7 633 (define-key picture-mode-map "\C-c\C-d" 'delete-char)
695d13c7
BP
634 (define-key picture-mode-map "\e\t" 'picture-toggle-tab-state)
635 (define-key picture-mode-map "\t" 'picture-tab)
636 (define-key picture-mode-map "\e\t" 'picture-tab-search)
637 (define-key picture-mode-map "\C-c\t" 'picture-set-tab-stops)
638 (define-key picture-mode-map "\C-c\C-k" 'picture-clear-rectangle)
639 (define-key picture-mode-map "\C-c\C-w" 'picture-clear-rectangle-to-register)
640 (define-key picture-mode-map "\C-c\C-y" 'picture-yank-rectangle)
641 (define-key picture-mode-map "\C-c\C-x" 'picture-yank-rectangle-from-register)
d792910f 642 (define-key picture-mode-map "\C-c\C-r" 'picture-draw-rectangle)
695d13c7
BP
643 (define-key picture-mode-map "\C-c\C-c" 'picture-mode-exit)
644 (define-key picture-mode-map "\C-c\C-f" 'picture-motion)
645 (define-key picture-mode-map "\C-c\C-b" 'picture-motion-reverse)
646 (define-key picture-mode-map "\C-c<" 'picture-movement-left)
647 (define-key picture-mode-map "\C-c>" 'picture-movement-right)
648 (define-key picture-mode-map "\C-c^" 'picture-movement-up)
649 (define-key picture-mode-map "\C-c." 'picture-movement-down)
650 (define-key picture-mode-map "\C-c`" 'picture-movement-nw)
651 (define-key picture-mode-map "\C-c'" 'picture-movement-ne)
652 (define-key picture-mode-map "\C-c/" 'picture-movement-sw)
ffe2923b
JL
653 (define-key picture-mode-map "\C-c\\" 'picture-movement-se)
654 (define-key picture-mode-map [(control ?c) left] 'picture-movement-left)
655 (define-key picture-mode-map [(control ?c) right] 'picture-movement-right)
656 (define-key picture-mode-map [(control ?c) up] 'picture-movement-up)
657 (define-key picture-mode-map [(control ?c) down] 'picture-movement-down)
658 (define-key picture-mode-map [(control ?c) home] 'picture-movement-nw)
659 (define-key picture-mode-map [(control ?c) prior] 'picture-movement-ne)
660 (define-key picture-mode-map [(control ?c) end] 'picture-movement-sw)
661 (define-key picture-mode-map [(control ?c) next] 'picture-movement-se)))
695d13c7 662
d1ebc62e 663(defcustom picture-mode-hook nil
ca9c7579 664 "If non-nil, its value is called on entry to Picture mode.
d1ebc62e
SE
665Picture mode is invoked by the command \\[picture-mode]."
666 :type 'hook
667 :group 'picture)
695d13c7 668
c4fc49b6
RS
669(defvar picture-mode-old-local-map)
670(defvar picture-mode-old-mode-name)
671(defvar picture-mode-old-major-mode)
b9911289 672(defvar picture-mode-old-truncate-lines)
c4fc49b6 673
7229064d 674;;;###autoload
ca9c7579 675(defun picture-mode ()
695d13c7 676 "Switch to Picture mode, in which a quarter-plane screen model is used.
1aa545c1 677\\<picture-mode-map>
695d13c7
BP
678Printing characters replace instead of inserting themselves with motion
679afterwards settable by these commands:
1aa545c1
CY
680
681 Move left after insertion: \\[picture-movement-left]
682 Move right after insertion: \\[picture-movement-right]
683 Move up after insertion: \\[picture-movement-up]
684 Move down after insertion: \\[picture-movement-down]
685
686 Move northwest (nw) after insertion: \\[picture-movement-nw]
687 Move northeast (ne) after insertion: \\[picture-movement-ne]
688 Move southwest (sw) after insertion: \\[picture-movement-sw]
689 Move southeast (se) after insertion: \\[picture-movement-se]
690
691 Move westnorthwest (wnw) after insertion: C-u \\[picture-movement-nw]
692 Move eastnortheast (ene) after insertion: C-u \\[picture-movement-ne]
693 Move westsouthwest (wsw) after insertion: C-u \\[picture-movement-sw]
694 Move eastsoutheast (ese) after insertion: C-u \\[picture-movement-se]
695
695d13c7
BP
696The current direction is displayed in the mode line. The initial
697direction is right. Whitespace is inserted and tabs are changed to
698spaces when required by movement. You can move around in the buffer
699with these commands:
1aa545c1
CY
700
701 Move vertically to SAME column in previous line: \\[picture-move-down]
702 Move vertically to SAME column in next line: \\[picture-move-up]
703 Move to column following last
704 non-whitespace character: \\[picture-end-of-line]
705 Move right, inserting spaces if required: \\[picture-forward-column]
706 Move left changing tabs to spaces if required: \\[picture-backward-column]
707 Move in direction of current picture motion: \\[picture-motion]
708 Move opposite to current picture motion: \\[picture-motion-reverse]
709 Move to beginning of next line: \\[next-line]
710
695d13c7 711You can edit tabular text with these commands:
1aa545c1
CY
712
713 Move to column beneath (or at) next interesting
714 character (see variable `picture-tab-chars'): \\[picture-tab-search]
715 Move to next stop in tab stop list: \\[picture-tab]
716 Set tab stops according to context of this line: \\[picture-set-tab-stops]
717 (With ARG, resets tab stops to default value.)
718 Change the tab stop list: \\[edit-tab-stops]
719
695d13c7 720You can manipulate text with these commands:
1aa545c1
CY
721 Clear ARG columns after point without moving: \\[picture-clear-column]
722 Delete char at point: \\[delete-char]
723 Clear ARG columns backward: \\[picture-backward-clear-column]
724 Clear ARG lines, advancing over them: \\[picture-clear-line]
725 (the cleared text is saved in the kill ring)
726 Open blank line(s) beneath current line: \\[picture-open-line]
727
695d13c7 728You can manipulate rectangles with these commands:
1aa545c1
CY
729 Clear a rectangle and save it: \\[picture-clear-rectangle]
730 Clear a rectangle, saving in a named register: \\[picture-clear-rectangle-to-register]
731 Insert currently saved rectangle at point: \\[picture-yank-rectangle]
732 Insert rectangle from named register: \\[picture-yank-rectangle-from-register]
733 Draw a rectangular box around mark and point: \\[picture-draw-rectangle]
734 Copies a rectangle to a register: \\[copy-rectangle-to-register]
8cb95edf 735 Undo effects of rectangle overlay commands: \\[undo]
1aa545c1
CY
736
737You can return to the previous mode with \\[picture-mode-exit], which
738also strips trailing whitespace from every line. Stripping is suppressed
739by supplying an argument.
695d13c7 740
e444e0d6 741Entry to this mode calls the value of `picture-mode-hook' if non-nil.
695d13c7
BP
742
743Note that Picture mode commands will work outside of Picture mode, but
744they are not defaultly assigned to keys."
745 (interactive)
ca9c7579 746 (if (eq major-mode 'picture-mode)
55535639 747 (error "You are already editing a picture")
e444e0d6 748 (set (make-local-variable 'picture-mode-old-local-map) (current-local-map))
695d13c7 749 (use-local-map picture-mode-map)
e444e0d6
SM
750 (set (make-local-variable 'picture-mode-old-mode-name) mode-name)
751 (set (make-local-variable 'picture-mode-old-major-mode) major-mode)
ca9c7579 752 (setq major-mode 'picture-mode)
e444e0d6
SM
753 (set (make-local-variable 'picture-killed-rectangle) nil)
754 (set (make-local-variable 'tab-stop-list) (default-value 'tab-stop-list))
755 (set (make-local-variable 'picture-tab-chars)
756 (default-value 'picture-tab-chars))
695d13c7
BP
757 (make-local-variable 'picture-vertical-step)
758 (make-local-variable 'picture-horizontal-step)
e444e0d6 759 (set (make-local-variable 'picture-mode-old-truncate-lines) truncate-lines)
ca9c7579 760 (setq truncate-lines t)
695d13c7 761 (picture-set-motion 0 1)
ca9c7579 762
e0dad66e
RS
763 ;; edit-picture-hook is what we used to run, picture-mode-hook is in doc.
764 (run-hooks 'edit-picture-hook 'picture-mode-hook)
d55235bb
KH
765 (message "Type %s in this buffer to return it to %s mode."
766 (substitute-command-keys "\\[picture-mode-exit]")
767 picture-mode-old-mode-name)))
695d13c7 768
6503cec3 769;;;###autoload
ca9c7579 770(defalias 'edit-picture 'picture-mode)
695d13c7
BP
771
772(defun picture-mode-exit (&optional nostrip)
2137e978 773 "Undo `picture-mode' and return to previous major mode.
8803c4f4
RS
774With no argument, strip whitespace from end of every line in Picture buffer;
775 otherwise, just return to previous mode.
776Runs `picture-mode-exit-hook' at the end."
695d13c7 777 (interactive "P")
ca9c7579 778 (if (not (eq major-mode 'picture-mode))
55535639 779 (error "You aren't editing a Picture")
eaae8106 780 (if (not nostrip) (delete-trailing-whitespace))
695d13c7
BP
781 (setq mode-name picture-mode-old-mode-name)
782 (use-local-map picture-mode-old-local-map)
783 (setq major-mode picture-mode-old-major-mode)
784 (kill-local-variable 'tab-stop-list)
b9911289 785 (setq truncate-lines picture-mode-old-truncate-lines)
8803c4f4
RS
786 (force-mode-line-update)
787 (run-hooks 'picture-mode-exit-hook)))
695d13c7 788
49116ac0
JB
789(provide 'picture)
790
6594deb0 791;;; picture.el ends here