Refill some copyright headers.
[bpt/emacs.git] / lisp / emulation / cua-rect.el
CommitLineData
72cc582e
KS
1;;; cua-rect.el --- CUA unified rectangle support
2
5fd6d89f 3;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
5df4f04c 4;; 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
72cc582e
KS
5
6;; Author: Kim F. Storm <storm@cua.dk>
7;; Keywords: keyboard emulations convenience CUA
bd78fa1d 8;; Package: cua-base
72cc582e
KS
9
10;; This file is part of GNU Emacs.
11
ed0f493f 12;; GNU Emacs is free software: you can redistribute it and/or modify
72cc582e 13;; it under the terms of the GNU General Public License as published by
ed0f493f
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
72cc582e
KS
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
ed0f493f 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
72cc582e
KS
24
25;;; Acknowledgements
26
27;; The rectangle handling and display code borrows from the standard
aa87aafc 28;; GNU emacs rect.el package and the rect-mark.el package by Rick
72cc582e
KS
29;; Sladkey <jrs@world.std.com>.
30
30764597
PJ
31;;; Commentary:
32
33;;; Code:
34
72cc582e 35(eval-when-compile
2c0f8564 36 (require 'cua-base))
72cc582e
KS
37
38;;; Rectangle support
39
40(require 'rect)
41
42;; If non-nil, restrict current region to this rectangle.
72797108 43;; Value is a vector [top bot left right corner ins virt select].
72cc582e
KS
44;; CORNER specifies currently active corner 0=t/l 1=t/r 2=b/l 3=b/r.
45;; INS specifies whether to insert on left(nil) or right(t) side.
72797108 46;; If VIRT is non-nil, virtual straight edges are enabled.
72cc582e
KS
47;; If SELECT is a regexp, only lines starting with that regexp are affected.")
48(defvar cua--rectangle nil)
49(make-variable-buffer-local 'cua--rectangle)
50
51;; Most recent rectangle geometry. Note: car is buffer.
52(defvar cua--last-rectangle nil)
53
54;; Rectangle restored by undo.
55(defvar cua--restored-rectangle nil)
56
57;; Last rectangle copied/killed; nil if last kill was not a rectangle.
58(defvar cua--last-killed-rectangle nil)
59
60;; List of overlays used to display current rectangle.
61(defvar cua--rectangle-overlays nil)
62(make-variable-buffer-local 'cua--rectangle-overlays)
c0e4cc19 63(put 'cua--rectangle-overlays 'permanent-local t)
72cc582e 64
1e71278b
KS
65(defvar cua--overlay-keymap
66 (let ((map (make-sparse-keymap)))
67 (define-key map "\r" 'cua-rotate-rectangle)))
68
72797108
KS
69(defvar cua--virtual-edges-debug nil)
70
e4907bbe
KS
71;; Undo rectangle commands.
72
73(defvar cua--rect-undo-set-point nil)
72cc582e 74
72cc582e
KS
75(defun cua--rectangle-undo-boundary ()
76 (when (listp buffer-undo-list)
e4907bbe
KS
77 (let ((s (cua--rect-start-position))
78 (e (cua--rect-end-position)))
79 (undo-boundary)
80 (push (list 'apply 0 s e
81 'cua--rect-undo-handler
82 (copy-sequence cua--rectangle) t s e)
83 buffer-undo-list))))
84
85(defun cua--rect-undo-handler (rect on s e)
86 (if (setq on (not on))
87 (setq cua--rect-undo-set-point s)
88 (setq cua--restored-rectangle (copy-sequence rect))
89 (setq cua--buffer-and-point-before-command nil))
90 (push (list 'apply 0 s (if on e s)
91 'cua--rect-undo-handler rect on s e)
92 buffer-undo-list))
72cc582e
KS
93
94;;; Rectangle geometry
95
96(defun cua--rectangle-top (&optional val)
97 ;; Top of CUA rectangle (buffer position on first line).
98 (if (not val)
99 (aref cua--rectangle 0)
100 (setq val (line-beginning-position))
101 (if (<= val (aref cua--rectangle 1))
102 (aset cua--rectangle 0 val)
103 (aset cua--rectangle 1 val)
104 (cua--rectangle-corner 2))))
105
106(defun cua--rectangle-bot (&optional val)
107 ;; Bot of CUA rectangle (buffer position on last line).
108 (if (not val)
109 (aref cua--rectangle 1)
110 (setq val (line-end-position))
111 (if (>= val (aref cua--rectangle 0))
112 (aset cua--rectangle 1 val)
113 (aset cua--rectangle 0 val)
114 (cua--rectangle-corner 2))))
115
116(defun cua--rectangle-left (&optional val)
117 ;; Left column of CUA rectangle.
118 (if (integerp val)
119 (if (<= val (aref cua--rectangle 3))
120 (aset cua--rectangle 2 val)
121 (aset cua--rectangle 3 val)
122 (cua--rectangle-corner (if (cua--rectangle-right-side) -1 1)))
123 (aref cua--rectangle 2)))
124
125(defun cua--rectangle-right (&optional val)
126 ;; Right column of CUA rectangle.
127 (if (integerp val)
128 (if (>= val (aref cua--rectangle 2))
129 (aset cua--rectangle 3 val)
130 (aset cua--rectangle 2 val)
131 (cua--rectangle-corner (if (cua--rectangle-right-side) -1 1)))
132 (aref cua--rectangle 3)))
133
134(defun cua--rectangle-corner (&optional advance)
135 ;; Currently active corner of rectangle.
136 (let ((c (aref cua--rectangle 4)))
137 (if (not (integerp advance))
138 c
a1506d29 139 (aset cua--rectangle 4
72cc582e
KS
140 (if (= advance 0)
141 (- 3 c) ; opposite corner
142 (mod (+ c 4 advance) 4)))
143 (aset cua--rectangle 5 0))))
144
145(defun cua--rectangle-right-side (&optional topbot)
146 ;; t if point is on right side of rectangle.
147 (if (and topbot (= (cua--rectangle-left) (cua--rectangle-right)))
148 (< (cua--rectangle-corner) 2)
149 (= (mod (cua--rectangle-corner) 2) 1)))
150
151(defun cua--rectangle-column ()
152 (if (cua--rectangle-right-side)
153 (cua--rectangle-right)
154 (cua--rectangle-left)))
155
156(defun cua--rectangle-insert-col (&optional col)
157 ;; Currently active corner of rectangle.
158 (if (integerp col)
159 (aset cua--rectangle 5 col)
160 (if (cua--rectangle-right-side t)
161 (if (= (aref cua--rectangle 5) 0)
162 (1+ (cua--rectangle-right))
163 (aref cua--rectangle 5))
164 (cua--rectangle-left))))
165
72797108
KS
166(defun cua--rectangle-virtual-edges (&optional set val)
167 ;; Current setting of rectangle virtual-edges
72cc582e
KS
168 (if set
169 (aset cua--rectangle 6 val))
72797108 170 (and ;(not buffer-read-only)
72cc582e
KS
171 (aref cua--rectangle 6)))
172
173(defun cua--rectangle-restriction (&optional val bounded negated)
174 ;; Current rectangle restriction
175 (if val
176 (aset cua--rectangle 7
177 (and (stringp val)
178 (> (length val) 0)
179 (list val bounded negated)))
180 (aref cua--rectangle 7)))
181
182(defun cua--rectangle-assert ()
183 (message "%S (%d)" cua--rectangle (point))
184 (if (< (cua--rectangle-right) (cua--rectangle-left))
185 (message "rectangle right < left"))
186 (if (< (cua--rectangle-bot) (cua--rectangle-top))
187 (message "rectangle bot < top")))
188
72797108 189(defun cua--rectangle-get-corners ()
72cc582e
KS
190 ;; Calculate the rectangular region represented by point and mark,
191 ;; putting start in the upper left corner and end in the
192 ;; bottom right corner.
193 (let ((top (point)) (bot (mark)) r l corner)
194 (save-excursion
195 (goto-char top)
196 (setq l (current-column))
197 (goto-char bot)
198 (setq r (current-column))
199 (if (<= top bot)
200 (setq corner (if (<= l r) 0 1))
201 (setq top (prog1 bot (setq bot top)))
202 (setq corner (if (<= l r) 2 3)))
203 (if (<= l r)
204 (if (< l r)
205 (setq r (1- r)))
206 (setq l (prog1 r (setq r l)))
207 (goto-char top)
72797108 208 (move-to-column l)
72cc582e
KS
209 (setq top (point))
210 (goto-char bot)
72797108 211 (move-to-column r)
72cc582e 212 (setq bot (point))))
72797108 213 (vector top bot l r corner 0 cua-virtual-rectangle-edges nil)))
72cc582e
KS
214
215(defun cua--rectangle-set-corners ()
216 ;; Set mark and point in opposite corners of current rectangle.
217 (let (pp pc mp mc (c (cua--rectangle-corner)))
218 (cond
219 ((= c 0) ; top/left -> bot/right
220 (setq pp (cua--rectangle-top) pc (cua--rectangle-left)
221 mp (cua--rectangle-bot) mc (cua--rectangle-right)))
222 ((= c 1) ; top/right -> bot/left
223 (setq pp (cua--rectangle-top) pc (cua--rectangle-right)
224 mp (cua--rectangle-bot) mc (cua--rectangle-left)))
225 ((= c 2) ; bot/left -> top/right
226 (setq pp (cua--rectangle-bot) pc (cua--rectangle-left)
227 mp (cua--rectangle-top) mc (cua--rectangle-right)))
228 ((= c 3) ; bot/right -> top/left
229 (setq pp (cua--rectangle-bot) pc (cua--rectangle-right)
230 mp (cua--rectangle-top) mc (cua--rectangle-left))))
231 (goto-char mp)
72797108 232 (move-to-column mc)
72cc582e
KS
233 (set-mark (point))
234 (goto-char pp)
78edd3b7 235 ;; Move cursor inside rectangle, except if char at right edge is a tab.
e2ea72e9 236 (if (and (if (cua--rectangle-right-side)
1e71278b
KS
237 (and (= (move-to-column pc) (- pc tab-width))
238 (not (eolp)))
e2ea72e9
KS
239 (> (move-to-column pc) pc))
240 (not (bolp)))
241 (backward-char 1))
72797108 242 ))
72cc582e 243
e4907bbe
KS
244(defun cua--rect-start-position ()
245 ;; Return point of top left corner
246 (save-excursion
247 (goto-char (cua--rectangle-top))
248 (and (> (move-to-column (cua--rectangle-left))
249 (cua--rectangle-left))
250 (not (bolp))
251 (backward-char 1))
252 (point)))
253
254(defun cua--rect-end-position ()
255 ;; Return point of bottom right cornet
256 (save-excursion
257 (goto-char (cua--rectangle-bot))
258 (and (= (move-to-column (cua--rectangle-right))
259 (- (cua--rectangle-right) tab-width))
260 (not (eolp))
261 (not (bolp))
262 (backward-char 1))
263 (point)))
264
72cc582e
KS
265;;; Rectangle resizing
266
72797108 267(defun cua--forward-line (n)
72cc582e 268 ;; Move forward/backward one line. Returns t if movement.
1e71278b
KS
269 (let ((pt (point)))
270 (and (= (forward-line n) 0)
271 ;; Deal with end of buffer
272 (or (not (eobp))
273 (goto-char pt)))))
72cc582e
KS
274
275(defun cua--rectangle-resized ()
276 ;; Refresh state after resizing rectangle
277 (setq cua--buffer-and-point-before-command nil)
72cc582e
KS
278 (cua--rectangle-insert-col 0)
279 (cua--rectangle-set-corners)
280 (cua--keep-active))
281
282(defun cua-resize-rectangle-right (n)
283 "Resize rectangle to the right."
284 (interactive "p")
72797108 285 (let ((resized (> n 0)))
72cc582e
KS
286 (while (> n 0)
287 (setq n (1- n))
288 (cond
72cc582e 289 ((cua--rectangle-right-side)
72797108
KS
290 (cua--rectangle-right (1+ (cua--rectangle-right)))
291 (move-to-column (cua--rectangle-right)))
72cc582e 292 (t
72797108
KS
293 (cua--rectangle-left (1+ (cua--rectangle-left)))
294 (move-to-column (cua--rectangle-right)))))
72cc582e
KS
295 (if resized
296 (cua--rectangle-resized))))
297
298(defun cua-resize-rectangle-left (n)
299 "Resize rectangle to the left."
300 (interactive "p")
72797108 301 (let (resized)
72cc582e
KS
302 (while (> n 0)
303 (setq n (1- n))
304 (if (or (= (cua--rectangle-right) 0)
305 (and (not (cua--rectangle-right-side)) (= (cua--rectangle-left) 0)))
306 (setq n 0)
307 (cond
72cc582e 308 ((cua--rectangle-right-side)
72797108
KS
309 (cua--rectangle-right (1- (cua--rectangle-right)))
310 (move-to-column (cua--rectangle-right)))
72cc582e 311 (t
72797108
KS
312 (cua--rectangle-left (1- (cua--rectangle-left)))
313 (move-to-column (cua--rectangle-right))))
72cc582e
KS
314 (setq resized t)))
315 (if resized
316 (cua--rectangle-resized))))
317
318(defun cua-resize-rectangle-down (n)
319 "Resize rectangle downwards."
320 (interactive "p")
72797108 321 (let (resized)
72cc582e
KS
322 (while (> n 0)
323 (setq n (1- n))
324 (cond
325 ((>= (cua--rectangle-corner) 2)
326 (goto-char (cua--rectangle-bot))
72797108
KS
327 (when (cua--forward-line 1)
328 (move-to-column (cua--rectangle-column))
72cc582e
KS
329 (cua--rectangle-bot t)
330 (setq resized t)))
331 (t
332 (goto-char (cua--rectangle-top))
72797108
KS
333 (when (cua--forward-line 1)
334 (move-to-column (cua--rectangle-column))
72cc582e
KS
335 (cua--rectangle-top t)
336 (setq resized t)))))
337 (if resized
338 (cua--rectangle-resized))))
339
340(defun cua-resize-rectangle-up (n)
341 "Resize rectangle upwards."
342 (interactive "p")
72797108 343 (let (resized)
72cc582e
KS
344 (while (> n 0)
345 (setq n (1- n))
346 (cond
347 ((>= (cua--rectangle-corner) 2)
348 (goto-char (cua--rectangle-bot))
72797108
KS
349 (when (cua--forward-line -1)
350 (move-to-column (cua--rectangle-column))
72cc582e
KS
351 (cua--rectangle-bot t)
352 (setq resized t)))
353 (t
354 (goto-char (cua--rectangle-top))
72797108
KS
355 (when (cua--forward-line -1)
356 (move-to-column (cua--rectangle-column))
72cc582e
KS
357 (cua--rectangle-top t)
358 (setq resized t)))))
359 (if resized
360 (cua--rectangle-resized))))
361
362(defun cua-resize-rectangle-eol ()
363 "Resize rectangle to end of line."
364 (interactive)
365 (unless (eolp)
366 (end-of-line)
367 (if (> (current-column) (cua--rectangle-right))
368 (cua--rectangle-right (current-column)))
369 (if (not (cua--rectangle-right-side))
370 (cua--rectangle-corner 1))
371 (cua--rectangle-resized)))
372
373(defun cua-resize-rectangle-bol ()
374 "Resize rectangle to beginning of line."
375 (interactive)
376 (unless (bolp)
377 (beginning-of-line)
378 (cua--rectangle-left (current-column))
379 (if (cua--rectangle-right-side)
380 (cua--rectangle-corner -1))
381 (cua--rectangle-resized)))
382
383(defun cua-resize-rectangle-bot ()
384 "Resize rectangle to bottom of buffer."
385 (interactive)
386 (goto-char (point-max))
72797108 387 (move-to-column (cua--rectangle-column))
72cc582e
KS
388 (cua--rectangle-bot t)
389 (cua--rectangle-resized))
390
391(defun cua-resize-rectangle-top ()
392 "Resize rectangle to top of buffer."
393 (interactive)
394 (goto-char (point-min))
72797108 395 (move-to-column (cua--rectangle-column))
72cc582e
KS
396 (cua--rectangle-top t)
397 (cua--rectangle-resized))
398
399(defun cua-resize-rectangle-page-up ()
400 "Resize rectangle upwards by one scroll page."
401 (interactive)
72797108
KS
402 (scroll-down)
403 (move-to-column (cua--rectangle-column))
404 (if (>= (cua--rectangle-corner) 2)
405 (cua--rectangle-bot t)
406 (cua--rectangle-top t))
407 (cua--rectangle-resized))
72cc582e
KS
408
409(defun cua-resize-rectangle-page-down ()
410 "Resize rectangle downwards by one scroll page."
411 (interactive)
72797108
KS
412 (scroll-up)
413 (move-to-column (cua--rectangle-column))
414 (if (>= (cua--rectangle-corner) 2)
415 (cua--rectangle-bot t)
416 (cua--rectangle-top t))
417 (cua--rectangle-resized))
72cc582e
KS
418
419;;; Mouse support
420
421;; This is pretty simplistic, but it does the job...
422
423(defun cua-mouse-resize-rectangle (event)
424 "Set rectangle corner at mouse click position."
425 (interactive "e")
426 (mouse-set-point event)
72797108
KS
427 ;; FIX ME -- need to calculate virtual column.
428 (if (cua--rectangle-virtual-edges)
72cc582e
KS
429 (move-to-column (car (posn-col-row (event-end event))) t))
430 (if (cua--rectangle-right-side)
431 (cua--rectangle-right (current-column))
432 (cua--rectangle-left (current-column)))
433 (if (>= (cua--rectangle-corner) 2)
434 (cua--rectangle-bot t)
435 (cua--rectangle-top t))
436 (cua--rectangle-resized))
437
438(defvar cua--mouse-last-pos nil)
439
440(defun cua-mouse-set-rectangle-mark (event)
441 "Start rectangle at mouse click position."
442 (interactive "e")
443 (when cua--rectangle
444 (cua--deactivate-rectangle)
445 (cua--deactivate t))
446 (setq cua--last-rectangle nil)
447 (mouse-set-point event)
72797108 448 ;; FIX ME -- need to calculate virtual column.
72cc582e
KS
449 (cua-set-rectangle-mark)
450 (setq cua--buffer-and-point-before-command nil)
451 (setq cua--mouse-last-pos nil))
452
453(defun cua-mouse-save-then-kill-rectangle (event arg)
454 "Expand rectangle to mouse click position and copy rectangle.
455If command is repeated at same position, delete the rectangle."
456 (interactive "e\nP")
457 (if (and (eq this-command last-command)
458 (eq (point) (car-safe cua--mouse-last-pos))
459 (eq cua--last-killed-rectangle (cdr-safe cua--mouse-last-pos)))
460 (progn
461 (unless buffer-read-only
462 (cua--delete-rectangle))
463 (cua--deactivate))
464 (cua-mouse-resize-rectangle event)
465 (let ((cua-keep-region-after-copy t))
466 (cua-copy-rectangle arg)
467 (setq cua--mouse-last-pos (cons (point) cua--last-killed-rectangle)))))
72797108 468
72cc582e
KS
469(defun cua--mouse-ignore (event)
470 (interactive "e")
471 (setq this-command last-command))
472
473(defun cua--rectangle-move (dir)
72797108 474 (let ((moved t)
72cc582e
KS
475 (top (cua--rectangle-top))
476 (bot (cua--rectangle-bot))
477 (l (cua--rectangle-left))
478 (r (cua--rectangle-right)))
479 (cond
480 ((eq dir 'up)
481 (goto-char top)
72797108 482 (when (cua--forward-line -1)
72cc582e
KS
483 (cua--rectangle-top t)
484 (goto-char bot)
485 (forward-line -1)
486 (cua--rectangle-bot t)))
487 ((eq dir 'down)
488 (goto-char bot)
72797108 489 (when (cua--forward-line 1)
72cc582e
KS
490 (cua--rectangle-bot t)
491 (goto-char top)
72797108 492 (cua--forward-line 1)
72cc582e
KS
493 (cua--rectangle-top t)))
494 ((eq dir 'left)
495 (when (> l 0)
496 (cua--rectangle-left (1- l))
497 (cua--rectangle-right (1- r))))
498 ((eq dir 'right)
499 (cua--rectangle-right (1+ r))
500 (cua--rectangle-left (1+ l)))
501 (t
502 (setq moved nil)))
503 (when moved
504 (setq cua--buffer-and-point-before-command nil)
72cc582e
KS
505 (cua--rectangle-set-corners)
506 (cua--keep-active))))
507
508
509;;; Operations on current rectangle
510
72797108
KS
511(defun cua--tabify-start (start end)
512 ;; Return position where auto-tabify should start (or nil if not required).
513 (save-excursion
514 (save-restriction
515 (widen)
516 (and (not buffer-read-only)
517 cua-auto-tabify-rectangles
518 (if (or (not (integerp cua-auto-tabify-rectangles))
519 (= (point-min) (point-max))
520 (progn
521 (goto-char (max (point-min)
522 (- start cua-auto-tabify-rectangles)))
523 (search-forward "\t" (min (point-max)
524 (+ end cua-auto-tabify-rectangles)) t)))
525 start)))))
526
527(defun cua--rectangle-operation (keep-clear visible undo pad tabify &optional fct post-fct)
72cc582e
KS
528 ;; Call FCT for each line of region with 4 parameters:
529 ;; Region start, end, left-col, right-col
530 ;; Point is at start when FCT is called
72797108
KS
531 ;; Call fct with (s,e) = whole lines if VISIBLE non-nil.
532 ;; Only call fct for visible lines if VISIBLE==t.
72cc582e 533 ;; Set undo boundary if UNDO is non-nil.
72797108
KS
534 ;; Rectangle is padded if PAD = t or numeric and (cua--rectangle-virtual-edges)
535 ;; Perform auto-tabify after operation if TABIFY is non-nil.
72cc582e 536 ;; Mark is kept if keep-clear is 'keep and cleared if keep-clear is 'clear.
4e687447
KS
537 (let* ((inhibit-field-text-motion t)
538 (start (cua--rectangle-top))
72cc582e
KS
539 (end (cua--rectangle-bot))
540 (l (cua--rectangle-left))
541 (r (1+ (cua--rectangle-right)))
542 (m (make-marker))
543 (tabpad (and (integerp pad) (= pad 2)))
72797108
KS
544 (sel (cua--rectangle-restriction))
545 (tabify-start (and tabify (cua--tabify-start start end))))
72cc582e
KS
546 (if undo
547 (cua--rectangle-undo-boundary))
548 (if (integerp pad)
72797108 549 (setq pad (cua--rectangle-virtual-edges)))
72cc582e
KS
550 (save-excursion
551 (save-restriction
552 (widen)
553 (when (> (cua--rectangle-corner) 1)
554 (goto-char end)
555 (and (bolp) (not (eolp)) (not (eobp))
556 (setq end (1+ end))))
72797108 557 (when (eq visible t)
72cc582e
KS
558 (setq start (max (window-start) start))
559 (setq end (min (window-end) end)))
560 (goto-char end)
561 (setq end (line-end-position))
e2ea72e9
KS
562 (if (and visible (bolp) (not (eobp)))
563 (setq end (1+ end)))
72cc582e
KS
564 (goto-char start)
565 (setq start (line-beginning-position))
566 (narrow-to-region start end)
567 (goto-char (point-min))
568 (while (< (point) (point-max))
569 (move-to-column r pad)
570 (and (not pad) (not visible) (> (current-column) r)
571 (backward-char 1))
572 (if (and tabpad (not pad) (looking-at "\t"))
573 (forward-char 1))
574 (set-marker m (point))
575 (move-to-column l pad)
72797108 576 (if (and fct (or visible (and (>= (current-column) l) (<= (current-column) r))))
72cc582e
KS
577 (let ((v t) (p (point)))
578 (when sel
579 (if (car (cdr sel))
580 (setq v (looking-at (car sel)))
581 (setq v (re-search-forward (car sel) m t))
582 (goto-char p))
583 (if (car (cdr (cdr sel)))
584 (setq v (null v))))
585 (if visible
72797108 586 (funcall fct p m l r v)
72cc582e
KS
587 (if v
588 (funcall fct p m l r)))))
589 (set-marker m nil)
590 (forward-line 1))
591 (if (not visible)
592 (cua--rectangle-bot t))
593 (if post-fct
72797108
KS
594 (funcall post-fct l r))
595 (when tabify-start
596 (tabify tabify-start (point)))))
72cc582e
KS
597 (cond
598 ((eq keep-clear 'keep)
599 (cua--keep-active))
600 ((eq keep-clear 'clear)
601 (cua--deactivate))
602 ((eq keep-clear 'corners)
603 (cua--rectangle-set-corners)
604 (cua--keep-active)))
605 (setq cua--buffer-and-point-before-command nil)))
606
607(put 'cua--rectangle-operation 'lisp-indent-function 4)
608
72cc582e 609(defun cua--delete-rectangle ()
72797108
KS
610 (let ((lines 0))
611 (if (not (cua--rectangle-virtual-edges))
612 (cua--rectangle-operation nil nil t 2 t
613 '(lambda (s e l r v)
614 (setq lines (1+ lines))
615 (if (and (> e s) (<= e (point-max)))
616 (delete-region s e))))
617 (cua--rectangle-operation nil 1 t nil t
618 '(lambda (s e l r v)
619 (setq lines (1+ lines))
620 (when (and (> e s) (<= e (point-max)))
621 (delete-region s e)))))
622 lines))
72cc582e
KS
623
624(defun cua--extract-rectangle ()
625 (let (rect)
72797108
KS
626 (if (not (cua--rectangle-virtual-edges))
627 (cua--rectangle-operation nil nil nil nil nil ; do not tabify
628 '(lambda (s e l r)
c5eb971b 629 (setq rect (cons (cua--filter-buffer-noprops s e) rect))))
72797108
KS
630 (cua--rectangle-operation nil 1 nil nil nil ; do not tabify
631 '(lambda (s e l r v)
632 (let ((copy t) (bs 0) (as 0) row)
633 (if (= s e) (setq e (1+ e)))
634 (goto-char s)
635 (move-to-column l)
636 (if (= (point) (line-end-position))
637 (setq bs (- r l)
638 copy nil)
55920229 639 (skip-chars-forward "\s\t" e)
72797108
KS
640 (setq bs (- (min r (current-column)) l)
641 s (point))
642 (move-to-column r)
55920229 643 (skip-chars-backward "\s\t" s)
72797108
KS
644 (setq as (- r (max (current-column) l))
645 e (point)))
646 (setq row (if (and copy (> e s))
c5eb971b 647 (cua--filter-buffer-noprops s e)
72797108
KS
648 ""))
649 (when (> bs 0)
650 (setq row (concat (make-string bs ?\s) row)))
651 (when (> as 0)
652 (setq row (concat row (make-string as ?\s))))
653 (setq rect (cons row rect))))))
654 (nreverse rect)))
655
656(defun cua--insert-rectangle (rect &optional below paste-column line-count)
72cc582e
KS
657 ;; Insert rectangle as insert-rectangle, but don't set mark and exit with
658 ;; point at either next to top right or below bottom left corner
659 ;; Notice: In overwrite mode, the rectangle is inserted as separate text lines.
72797108 660 (if (eq below 'auto)
72cc582e
KS
661 (setq below (and (bolp)
662 (or (eolp) (eobp) (= (1+ (point)) (point-max))))))
72797108
KS
663 (unless paste-column
664 (setq paste-column (current-column)))
72cc582e 665 (let ((lines rect)
72cc582e 666 (first t)
72797108
KS
667 (tabify-start (cua--tabify-start (point) (point)))
668 last-column
72cc582e
KS
669 p)
670 (while (or lines below)
671 (or first
672 (if overwrite-mode
673 (insert ?\n)
674 (forward-line 1)
72797108
KS
675 (or (bolp) (insert ?\n))))
676 (unless overwrite-mode
677 (move-to-column paste-column t))
72cc582e
KS
678 (if (not lines)
679 (setq below nil)
680 (insert-for-yank (car lines))
72797108
KS
681 (unless last-column
682 (setq last-column (current-column)))
72cc582e
KS
683 (setq lines (cdr lines))
684 (and first (not below)
685 (setq p (point))))
72797108
KS
686 (setq first nil)
687 (if (and line-count (= (setq line-count (1- line-count)) 0))
688 (setq lines nil)))
689 (when (and line-count last-column (not overwrite-mode))
690 (while (> line-count 0)
691 (forward-line 1)
692 (or (bolp) (insert ?\n))
693 (move-to-column paste-column t)
694 (insert-char ?\s (- last-column paste-column -1))
695 (setq line-count (1- line-count))))
696 (when (and tabify-start
697 (not overwrite-mode))
698 (tabify tabify-start (point)))
72cc582e
KS
699 (and p (not overwrite-mode)
700 (goto-char p))))
701
702(defun cua--copy-rectangle-as-kill (&optional ring)
703 (if cua--register
704 (set-register cua--register (cua--extract-rectangle))
705 (setq killed-rectangle (cua--extract-rectangle))
706 (setq cua--last-killed-rectangle (cons (and kill-ring (car kill-ring)) killed-rectangle))
707 (if ring
708 (kill-new (mapconcat
709 (function (lambda (row) (concat row "\n")))
710 killed-rectangle "")))))
711
72797108 712(defun cua--activate-rectangle ()
72cc582e
KS
713 ;; Turn on rectangular marking mode by disabling transient mark mode
714 ;; and manually handling highlighting from a post command hook.
715 ;; Be careful if we are already marking a rectangle.
a1506d29
JB
716 (setq cua--rectangle
717 (if (and cua--last-rectangle
72cc582e
KS
718 (eq (car cua--last-rectangle) (current-buffer))
719 (eq (car (cdr cua--last-rectangle)) (point)))
720 (cdr (cdr cua--last-rectangle))
72797108
KS
721 (cua--rectangle-get-corners))
722 cua--status-string (if (cua--rectangle-virtual-edges) " [R]" "")
72cc582e
KS
723 cua--last-rectangle nil))
724
725;; (defvar cua-save-point nil)
726
727(defun cua--deactivate-rectangle ()
728 ;; This is used to clean up after `cua--activate-rectangle'.
026056a4 729 (mapc (function delete-overlay) cua--rectangle-overlays)
a1506d29 730 (setq cua--last-rectangle (cons (current-buffer)
72cc582e
KS
731 (cons (point) ;; cua-save-point
732 cua--rectangle))
733 cua--rectangle nil
734 cua--rectangle-overlays nil
735 cua--status-string nil
736 cua--mouse-last-pos nil))
737
738(defun cua--highlight-rectangle ()
739 ;; This function is used to highlight the rectangular region.
740 ;; We do this by putting an overlay on each line within the rectangle.
741 ;; Each overlay extends across all the columns of the rectangle.
742 ;; We try to reuse overlays where possible because this is more efficient
743 ;; and results in less flicker.
72797108 744 ;; If cua--rectangle-virtual-edges is nil and the buffer contains tabs or short lines,
72cc582e
KS
745 ;; the higlighted region may not be perfectly rectangular.
746 (let ((deactivate-mark deactivate-mark)
747 (old cua--rectangle-overlays)
748 (new nil)
749 (left (cua--rectangle-left))
750 (right (1+ (cua--rectangle-right))))
751 (when (/= left right)
752 (sit-for 0) ; make window top/bottom reliable
72797108 753 (cua--rectangle-operation nil t nil nil nil ; do not tabify
72cc582e 754 '(lambda (s e l r v)
1e98d199 755 (let ((rface (if v 'cua-rectangle 'cua-rectangle-noselect))
e2ea72e9 756 overlay bs ms as)
72797108
KS
757 (when (cua--rectangle-virtual-edges)
758 (let ((lb (line-beginning-position))
759 (le (line-end-position))
760 cl cl0 pl cr cr0 pr)
761 (goto-char s)
762 (setq cl (move-to-column l)
763 pl (point))
764 (setq cr (move-to-column r)
765 pr (point))
766 (if (= lb pl)
767 (setq cl0 0)
768 (goto-char (1- pl))
769 (setq cl0 (current-column)))
770 (if (= lb le)
771 (setq cr0 0)
772 (goto-char (1- pr))
773 (setq cr0 (current-column)))
774 (unless (and (= cl l) (= cr r))
775 (when (/= cl l)
776 (setq bs (propertize
777 (make-string
778 (- l cl0 (if (and (= le pl) (/= le lb)) 1 0))
779 (if cua--virtual-edges-debug ?. ?\s))
98faed9f 780 'face (or (get-text-property (1- s) 'face) 'default)))
72797108
KS
781 (if (/= pl le)
782 (setq s (1- s))))
783 (cond
784 ((= cr r)
e2ea72e9
KS
785 (if (and (/= pr le)
786 (/= cr0 (1- cr))
787 (or bs (/= cr0 (- cr tab-width)))
788 (/= (mod cr tab-width) 0))
72797108
KS
789 (setq e (1- e))))
790 ((= cr cl)
e2ea72e9
KS
791 (setq ms (propertize
792 (make-string
793 (- r l)
794 (if cua--virtual-edges-debug ?, ?\s))
795 'face rface))
796 (if (cua--rectangle-right-side)
b53b41ba
KS
797 (put-text-property (1- (length ms)) (length ms) 'cursor 2 ms)
798 (put-text-property 0 1 'cursor 2 ms))
e2ea72e9 799 (setq bs (concat bs ms))
72797108 800 (setq rface nil))
e2ea72e9 801 (t
72797108
KS
802 (setq as (propertize
803 (make-string
804 (- r cr0 (if (= le pr) 1 0))
805 (if cua--virtual-edges-debug ?~ ?\s))
806 'face rface))
e2ea72e9 807 (if (cua--rectangle-right-side)
b53b41ba
KS
808 (put-text-property (1- (length as)) (length as) 'cursor 2 as)
809 (put-text-property 0 1 'cursor 2 as))
72797108
KS
810 (if (/= pr le)
811 (setq e (1- e))))))))
812 ;; Trim old leading overlays.
72cc582e
KS
813 (while (and old
814 (setq overlay (car old))
815 (< (overlay-start overlay) s)
816 (/= (overlay-end overlay) e))
817 (delete-overlay overlay)
818 (setq old (cdr old)))
819 ;; Reuse an overlay if possible, otherwise create one.
820 (if (and old
821 (setq overlay (car old))
822 (or (= (overlay-start overlay) s)
823 (= (overlay-end overlay) e)))
824 (progn
825 (move-overlay overlay s e)
826 (setq old (cdr old)))
827 (setq overlay (make-overlay s e)))
e2ea72e9 828 (overlay-put overlay 'before-string bs)
72797108
KS
829 (overlay-put overlay 'after-string as)
830 (overlay-put overlay 'face rface)
1e71278b 831 (overlay-put overlay 'keymap cua--overlay-keymap)
f2856495 832 (overlay-put overlay 'window (selected-window))
72797108 833 (setq new (cons overlay new))))))
72cc582e 834 ;; Trim old trailing overlays.
026056a4 835 (mapc (function delete-overlay) old)
72cc582e
KS
836 (setq cua--rectangle-overlays (nreverse new))))
837
838(defun cua--indent-rectangle (&optional ch to-col clear)
839 ;; Indent current rectangle.
840 (let ((col (cua--rectangle-insert-col))
72797108 841 (pad (cua--rectangle-virtual-edges))
72cc582e 842 indent)
e2ea72e9 843 (cua--rectangle-operation (if clear 'clear 'corners) nil t pad nil
72cc582e
KS
844 '(lambda (s e l r)
845 (move-to-column col pad)
846 (if (and (eolp)
847 (< (current-column) col))
848 (move-to-column col t))
849 (cond
850 (to-col (indent-to to-col))
c872c51e 851 ((and ch (not (eq ch ?\t))) (insert ch))
72cc582e
KS
852 (t (tab-to-tab-stop)))
853 (if (cua--rectangle-right-side t)
854 (cua--rectangle-insert-col (current-column))
855 (setq indent (- (current-column) l))))
856 '(lambda (l r)
857 (when (and indent (> indent 0))
858 (aset cua--rectangle 2 (+ l indent))
859 (aset cua--rectangle 3 (+ r indent -1)))))))
860
861;;
862;; rectangle functions / actions
863;;
864
865(defvar cua--rectangle-initialized nil)
866
867(defun cua-set-rectangle-mark (&optional reopen)
868 "Set mark and start in CUA rectangle mode.
869With prefix argument, activate previous rectangle if possible."
870 (interactive "P")
871 (unless cua--rectangle-initialized
872 (cua--init-rectangles))
873 (when (not cua--rectangle)
874 (if (and reopen
875 cua--last-rectangle
876 (eq (car cua--last-rectangle) (current-buffer)))
877 (goto-char (car (cdr cua--last-rectangle)))
878 (if (not mark-active)
879 (push-mark nil nil t)))
880 (cua--activate-rectangle)
881 (cua--rectangle-set-corners)
882 (setq mark-active t
883 cua--explicit-region-start t)
884 (if cua-enable-rectangle-auto-help
885 (cua-help-for-rectangle t))))
886
887(defun cua-clear-rectangle-mark ()
888 "Cancel current rectangle."
889 (interactive)
890 (when cua--rectangle
891 (setq mark-active nil
892 cua--explicit-region-start nil)
893 (cua--deactivate-rectangle)))
894
895(defun cua-toggle-rectangle-mark ()
896 (interactive)
897 (if cua--rectangle
898 (cua--deactivate-rectangle)
899 (unless cua--rectangle-initialized
900 (cua--init-rectangles))
901 (cua--activate-rectangle))
902 (if cua--rectangle
903 (if cua-enable-rectangle-auto-help
904 (cua-help-for-rectangle t))
905 (if cua-enable-region-auto-help
906 (cua-help-for-region t))))
907
908(defun cua-restrict-regexp-rectangle (arg)
909 "Restrict rectangle to lines (not) matching REGEXP.
910With prefix argument, the toggle restriction."
911 (interactive "P")
912 (let ((r (cua--rectangle-restriction)) regexp)
913 (if (and r (null (car (cdr r))))
914 (if arg
915 (cua--rectangle-restriction (car r) nil (not (car (cdr (cdr r)))))
916 (cua--rectangle-restriction "" nil nil))
917 (cua--rectangle-restriction
918 (read-from-minibuffer "Restrict rectangle (regexp): "
919 nil nil nil nil) nil arg))))
920
921(defun cua-restrict-prefix-rectangle (arg)
922 "Restrict rectangle to lines (not) starting with CHAR.
923With prefix argument, the toggle restriction."
924 (interactive "P")
925 (let ((r (cua--rectangle-restriction)) regexp)
926 (if (and r (car (cdr r)))
927 (if arg
928 (cua--rectangle-restriction (car r) t (not (car (cdr (cdr r)))))
929 (cua--rectangle-restriction "" nil nil))
930 (cua--rectangle-restriction
a1506d29 931 (format "[%c]"
72cc582e
KS
932 (read-char "Restrictive rectangle (char): ")) t arg))))
933
934(defun cua-move-rectangle-up ()
935 (interactive)
936 (cua--rectangle-move 'up))
937
938(defun cua-move-rectangle-down ()
939 (interactive)
940 (cua--rectangle-move 'down))
941
942(defun cua-move-rectangle-left ()
943 (interactive)
944 (cua--rectangle-move 'left))
945
946(defun cua-move-rectangle-right ()
947 (interactive)
948 (cua--rectangle-move 'right))
949
950(defun cua-copy-rectangle (arg)
951 (interactive "P")
952 (setq arg (cua--prefix-arg arg))
953 (cua--copy-rectangle-as-kill arg)
954 (if cua-keep-region-after-copy
955 (cua--keep-active)
956 (cua--deactivate)))
957
958(defun cua-cut-rectangle (arg)
959 (interactive "P")
960 (if buffer-read-only
961 (cua-copy-rectangle arg)
962 (setq arg (cua--prefix-arg arg))
963 (goto-char (min (mark) (point)))
964 (cua--copy-rectangle-as-kill arg)
965 (cua--delete-rectangle))
966 (cua--deactivate))
967
968(defun cua-delete-rectangle ()
969 (interactive)
970 (goto-char (min (point) (mark)))
971 (if cua-delete-copy-to-register-0
972 (set-register ?0 (cua--extract-rectangle)))
973 (cua--delete-rectangle)
974 (cua--deactivate))
975
976(defun cua-rotate-rectangle ()
977 (interactive)
978 (cua--rectangle-corner (if (= (cua--rectangle-left) (cua--rectangle-right)) 0 1))
e2ea72e9
KS
979 (cua--rectangle-set-corners)
980 (if (cua--rectangle-virtual-edges)
981 (setq cua--buffer-and-point-before-command nil)))
72cc582e 982
72797108 983(defun cua-toggle-rectangle-virtual-edges ()
72cc582e 984 (interactive)
72797108
KS
985 (cua--rectangle-virtual-edges t (not (cua--rectangle-virtual-edges)))
986 (cua--rectangle-set-corners)
987 (setq cua--status-string (and (cua--rectangle-virtual-edges) " [R]"))
72cc582e
KS
988 (cua--keep-active))
989
990(defun cua-do-rectangle-padding ()
991 (interactive)
992 (if buffer-read-only
8482d727 993 (message "Cannot do padding in read-only buffer")
72797108 994 (cua--rectangle-operation nil nil t t t)
72cc582e
KS
995 (cua--rectangle-set-corners))
996 (cua--keep-active))
997
998(defun cua-open-rectangle ()
999 "Blank out CUA rectangle, shifting text right.
1000The text previously in the region is not overwritten by the blanks,
1001but instead winds up to the right of the rectangle."
1002 (interactive)
72797108 1003 (cua--rectangle-operation 'corners nil t 1 nil
72cc582e
KS
1004 '(lambda (s e l r)
1005 (skip-chars-forward " \t")
1006 (let ((ws (- (current-column) l))
1007 (p (point)))
1008 (skip-chars-backward " \t")
1009 (delete-region (point) p)
1010 (indent-to (+ r ws))))))
1011
1012(defun cua-close-rectangle (arg)
1013 "Delete all whitespace starting at left edge of CUA rectangle.
1014On each line in the rectangle, all continuous whitespace starting
1015at that column is deleted.
1016With prefix arg, also delete whitespace to the left of that column."
1017 (interactive "P")
72797108 1018 (cua--rectangle-operation 'clear nil t 1 nil
72cc582e
KS
1019 '(lambda (s e l r)
1020 (when arg
1021 (skip-syntax-backward " " (line-beginning-position))
1022 (setq s (point)))
1023 (skip-syntax-forward " " (line-end-position))
1024 (delete-region s (point)))))
1025
1026(defun cua-blank-rectangle ()
1027 "Blank out CUA rectangle.
1028The text previously in the rectangle is overwritten by the blanks."
1029 (interactive)
72797108 1030 (cua--rectangle-operation 'keep nil nil 1 nil
72cc582e
KS
1031 '(lambda (s e l r)
1032 (goto-char e)
1033 (skip-syntax-forward " " (line-end-position))
1034 (setq e (point))
1035 (let ((column (current-column)))
1036 (goto-char s)
1037 (skip-syntax-backward " " (line-beginning-position))
1038 (delete-region (point) e)
1039 (indent-to column)))))
1040
1041(defun cua-align-rectangle ()
1042 "Align rectangle lines to left column."
1043 (interactive)
1044 (let (x)
72797108 1045 (cua--rectangle-operation 'clear nil t t nil
72cc582e
KS
1046 '(lambda (s e l r)
1047 (let ((b (line-beginning-position)))
1048 (skip-syntax-backward "^ " b)
1049 (skip-syntax-backward " " b)
1050 (setq s (point)))
1051 (skip-syntax-forward " " (line-end-position))
1052 (delete-region s (point))
1053 (indent-to l))
1054 '(lambda (l r)
1055 (move-to-column l)
1056 ;; (setq cua-save-point (point))
1057 ))))
1058
2c0f8564
GM
1059(declare-function cua--cut-rectangle-to-global-mark "cua-gmrk" (as-text))
1060(declare-function cua--copy-rectangle-to-global-mark "cua-gmrk" (as-text))
1061
72cc582e
KS
1062(defun cua-copy-rectangle-as-text (&optional arg delete)
1063 "Copy rectangle, but store as normal text."
1064 (interactive "P")
1065 (if cua--global-mark-active
1066 (if delete
1067 (cua--cut-rectangle-to-global-mark t)
1068 (cua--copy-rectangle-to-global-mark t))
1069 (let* ((rect (cua--extract-rectangle))
1070 (text (mapconcat
1071 (function (lambda (row) (concat row "\n")))
1072 rect "")))
1073 (setq arg (cua--prefix-arg arg))
1074 (if cua--register
1075 (set-register cua--register text)
1076 (kill-new text)))
1077 (if delete
1078 (cua--delete-rectangle))
1079 (cua--deactivate)))
1080
1081(defun cua-cut-rectangle-as-text (arg)
1082 "Kill rectangle, but store as normal text."
1083 (interactive "P")
1084 (cua-copy-rectangle-as-text arg (not buffer-read-only)))
1085
1086(defun cua-string-rectangle (string)
1087 "Replace CUA rectangle contents with STRING on each line.
1088The length of STRING need not be the same as the rectangle width."
1089 (interactive "sString rectangle: ")
72797108 1090 (cua--rectangle-operation 'keep nil t t nil
72cc582e
KS
1091 '(lambda (s e l r)
1092 (delete-region s e)
1093 (skip-chars-forward " \t")
1094 (let ((ws (- (current-column) l)))
1095 (delete-region s (point))
1096 (insert string)
1097 (indent-to (+ (current-column) ws))))
1098 (unless (cua--rectangle-restriction)
1099 '(lambda (l r)
1100 (cua--rectangle-right (max l (+ l (length string) -1)))))))
1101
8482d727 1102(defun cua-fill-char-rectangle (character)
72cc582e
KS
1103 "Replace CUA rectangle contents with CHARACTER."
1104 (interactive "cFill rectangle with character: ")
72797108 1105 (cua--rectangle-operation 'clear nil t 1 nil
72cc582e
KS
1106 '(lambda (s e l r)
1107 (delete-region s e)
1108 (move-to-column l t)
8482d727 1109 (insert-char character (- r l)))))
72cc582e
KS
1110
1111(defun cua-replace-in-rectangle (regexp newtext)
1112 "Replace REGEXP with NEWTEXT in each line of CUA rectangle."
1113 (interactive "sReplace regexp: \nsNew text: ")
1114 (if buffer-read-only
1115 (message "Cannot replace in read-only buffer")
72797108 1116 (cua--rectangle-operation 'keep nil t 1 nil
72cc582e
KS
1117 '(lambda (s e l r)
1118 (if (re-search-forward regexp e t)
1119 (replace-match newtext nil nil))))))
1120
1121(defun cua-incr-rectangle (increment)
1122 "Increment each line of CUA rectangle by prefix amount."
1123 (interactive "p")
72797108 1124 (cua--rectangle-operation 'keep nil t 1 nil
72cc582e
KS
1125 '(lambda (s e l r)
1126 (cond
1127 ((re-search-forward "0x\\([0-9a-fA-F]+\\)" e t)
c5eb971b 1128 (let* ((txt (cua--filter-buffer-noprops (match-beginning 1) (match-end 1)))
72cc582e
KS
1129 (n (string-to-number txt 16))
1130 (fmt (format "0x%%0%dx" (length txt))))
1131 (replace-match (format fmt (+ n increment)))))
1132 ((re-search-forward "\\( *-?[0-9]+\\)" e t)
c5eb971b 1133 (let* ((txt (cua--filter-buffer-noprops (match-beginning 1) (match-end 1)))
72cc582e
KS
1134 (prefix (if (= (aref txt 0) ?0) "0" ""))
1135 (n (string-to-number txt 10))
1136 (fmt (format "%%%s%dd" prefix (length txt))))
1137 (replace-match (format fmt (+ n increment)))))
1138 (t nil)))))
1139
1140(defvar cua--rectangle-seq-format "%d"
8482d727 1141 "Last format used by `cua-sequence-rectangle'.")
72cc582e 1142
8482d727 1143(defun cua-sequence-rectangle (first incr format)
72cc582e
KS
1144 "Resequence each line of CUA rectangle starting from FIRST.
1145The numbers are formatted according to the FORMAT string."
a1506d29 1146 (interactive
72cc582e
KS
1147 (list (if current-prefix-arg
1148 (prefix-numeric-value current-prefix-arg)
1149 (string-to-number
1150 (read-string "Start value: (0) " nil nil "0")))
1151 (string-to-number
1152 (read-string "Increment: (1) " nil nil "1"))
1153 (read-string (concat "Format: (" cua--rectangle-seq-format ") "))))
8482d727
JB
1154 (if (= (length format) 0)
1155 (setq format cua--rectangle-seq-format)
1156 (setq cua--rectangle-seq-format format))
72797108 1157 (cua--rectangle-operation 'clear nil t 1 nil
72cc582e
KS
1158 '(lambda (s e l r)
1159 (delete-region s e)
8482d727 1160 (insert (format format first))
72cc582e
KS
1161 (setq first (+ first incr)))))
1162
72797108
KS
1163(defmacro cua--convert-rectangle-as (command tabify)
1164 `(cua--rectangle-operation 'clear nil nil nil ,tabify
3b365de3
KS
1165 '(lambda (s e l r)
1166 (,command s e))))
1167
72cc582e
KS
1168(defun cua-upcase-rectangle ()
1169 "Convert the rectangle to upper case."
1170 (interactive)
72797108 1171 (cua--convert-rectangle-as upcase-region nil))
72cc582e
KS
1172
1173(defun cua-downcase-rectangle ()
1174 "Convert the rectangle to lower case."
1175 (interactive)
72797108 1176 (cua--convert-rectangle-as downcase-region nil))
3b365de3
KS
1177
1178(defun cua-upcase-initials-rectangle ()
1179 "Convert the rectangle initials to upper case."
1180 (interactive)
72797108 1181 (cua--convert-rectangle-as upcase-initials-region nil))
3b365de3
KS
1182
1183(defun cua-capitalize-rectangle ()
1184 "Convert the rectangle to proper case."
1185 (interactive)
72797108 1186 (cua--convert-rectangle-as capitalize-region nil))
72cc582e
KS
1187
1188
1189;;; Replace/rearrange text in current rectangle
1190
1191(defun cua--rectangle-aux-replace (width adjust keep replace pad format-fct &optional setup-fct)
1192 ;; Process text inserted by calling SETUP-FCT or current rectangle if nil.
1193 ;; Then call FORMAT-FCT on text (if non-nil); takes two args: start and end.
1194 ;; Fill to WIDTH characters if > 0 or fill to current width if == 0.
1195 ;; Don't fill if WIDTH < 0.
1196 ;; Replace current rectangle by filled text if REPLACE is non-nil
1197 (let ((auxbuf (get-buffer-create "*CUA temp*"))
1198 (w (if (> width 1) width
1199 (- (cua--rectangle-right) (cua--rectangle-left) -1)))
1200 (r (or setup-fct (cua--extract-rectangle)))
1201 y z (tr 0))
937e6a56 1202 (with-current-buffer auxbuf
72cc582e
KS
1203 (erase-buffer)
1204 (if setup-fct
1205 (funcall setup-fct)
1206 (cua--insert-rectangle r))
1207 (if format-fct
1208 (let ((fill-column w))
1209 (funcall format-fct (point-min) (point-max))))
1210 (when replace
1211 (goto-char (point-min))
1212 (while (not (eobp))
15ac4d58 1213 (setq z (cons (filter-buffer-substring (point) (line-end-position)) z))
72cc582e
KS
1214 (forward-line 1))))
1215 (if (not cua--debug)
1216 (kill-buffer auxbuf))
1217 (when replace
1218 (setq z (reverse z))
1219 (if cua--debug
1220 (print z auxbuf))
72797108 1221 (cua--rectangle-operation nil nil t pad nil
72cc582e
KS
1222 '(lambda (s e l r)
1223 (let (cc)
1224 (goto-char e)
1225 (skip-chars-forward " \t")
1226 (setq cc (current-column))
1227 (if cua--debug
1228 (print (list cc s e) auxbuf))
1229 (delete-region s (point))
1230 (if (not z)
1231 (setq y 0)
1232 (move-to-column l t)
1233 (insert (car z))
1234 (when (> (current-column) (+ l w))
1235 (setq y (point))
1236 (move-to-column (+ l w) t)
1237 (delete-region (point) y)
1238 (setq tr (1+ tr)))
1239 (setq z (cdr z)))
1240 (if cua--debug
1241 (print (list (current-column) cc) auxbuf))
e87aa620 1242 (just-one-space 0)
72cc582e
KS
1243 (indent-to cc))))
1244 (if (> tr 0)
1245 (message "Warning: Truncated %d row%s" tr (if (> tr 1) "s" "")))
1246 (if adjust
1247 (cua--rectangle-right (+ (cua--rectangle-left) w -1)))
1248 (if keep
1249 (cua--rectangle-resized)))))
1250
1251(put 'cua--rectangle-aux-replace 'lisp-indent-function 4)
1252
1253(defun cua--left-fill-rectangle (start end)
1254 (beginning-of-line)
1255 (while (< (point) (point-max))
1256 (delete-horizontal-space nil)
1257 (forward-line 1))
1258 (fill-region-as-paragraph (point-min) (point-max) 'left nil)
1259 (untabify (point-min) (point-max)))
1260
1261(defun cua-text-fill-rectangle (width text)
1262 "Replace rectagle with filled TEXT read from minibuffer.
1263A numeric prefix argument is used a new width for the filled rectangle."
1264 (interactive (list
1265 (prefix-numeric-value current-prefix-arg)
1266 (read-from-minibuffer "Enter text: "
1267 nil nil nil nil)))
1268 (cua--rectangle-aux-replace width t t t 1
1269 'cua--left-fill-rectangle
1270 '(lambda () (insert text))))
1271
1272(defun cua-refill-rectangle (width)
1273 "Fill contents of current rectagle.
1274A numeric prefix argument is used as new width for the filled rectangle."
1275 (interactive "P")
1276 (cua--rectangle-aux-replace
1277 (if width (prefix-numeric-value width) 0)
1278 t t t 1 'cua--left-fill-rectangle))
1279
1280(defun cua-shell-command-on-rectangle (replace command)
1281 "Run shell command on rectangle like `shell-command-on-region'.
1282With prefix arg, replace rectangle with output from command."
1283 (interactive (list
1284 current-prefix-arg
1285 (read-from-minibuffer "Shell command on rectangle: "
a1506d29 1286 nil nil nil
72cc582e
KS
1287 'shell-command-history)))
1288 (cua--rectangle-aux-replace -1 t t replace 1
1289 '(lambda (s e)
1290 (shell-command-on-region s e command
1291 replace replace nil))))
1292
1293(defun cua-reverse-rectangle ()
1294 "Reverse the lines of the rectangle."
1295 (interactive)
1296 (cua--rectangle-aux-replace 0 t t t t 'reverse-region))
1297
1298(defun cua-scroll-rectangle-up ()
1299 "Remove the first line of the rectangle and scroll remaining lines up."
1300 (interactive)
1301 (cua--rectangle-aux-replace 0 t t t t
a1506d29 1302 '(lambda (s e)
72cc582e
KS
1303 (if (= (forward-line 1) 0)
1304 (delete-region s (point))))))
1305
1306(defun cua-scroll-rectangle-down ()
1307 "Insert a blank line at the first line of the rectangle.
1308The remaining lines are scrolled down, losing the last line."
1309 (interactive)
1310 (cua--rectangle-aux-replace 0 t t t t
1311 '(lambda (s e)
1312 (goto-char s)
1313 (insert "\n"))))
1314
1315
1316;;; Insert/delete text to left or right of rectangle
1317
1318(defun cua-insert-char-rectangle (&optional ch)
1319 (interactive)
1320 (if buffer-read-only
1321 (ding)
1322 (cua--indent-rectangle (or ch (aref (this-single-command-keys) 0)))
1323 (cua--keep-active))
1324 t)
1325
1326(defun cua-indent-rectangle (column)
1327 "Indent rectangle to next tab stop.
1328With prefix arg, indent to that column."
1329 (interactive "P")
1330 (if (null column)
1331 (cua-insert-char-rectangle ?\t)
1332 (cua--indent-rectangle nil (prefix-numeric-value column))))
1333
1334(defun cua-delete-char-rectangle ()
1335 "Delete char to left or right of rectangle."
1336 (interactive)
1337 (let ((col (cua--rectangle-insert-col))
72797108 1338 (pad (cua--rectangle-virtual-edges))
72cc582e 1339 indent)
72797108 1340 (cua--rectangle-operation 'corners nil t pad nil
72cc582e 1341 '(lambda (s e l r)
a1506d29 1342 (move-to-column
72cc582e
KS
1343 (if (cua--rectangle-right-side t)
1344 (max (1+ r) col) l)
1345 pad)
1346 (if (bolp)
1347 nil
d355a0b7 1348 (delete-char -1)
72cc582e
KS
1349 (if (cua--rectangle-right-side t)
1350 (cua--rectangle-insert-col (current-column))
1351 (setq indent (- l (current-column))))))
1352 '(lambda (l r)
1353 (when (and indent (> indent 0))
1354 (aset cua--rectangle 2 (- l indent))
1355 (aset cua--rectangle 3 (- r indent 1)))))))
1356
1357(defun cua-help-for-rectangle (&optional help)
1358 (interactive)
1638bf54
KS
1359 (let ((M (cond ((eq cua--rectangle-modifier-key 'hyper) " H-")
1360 ((eq cua--rectangle-modifier-key 'super) " s-")
a4520735 1361 ((eq cua--rectangle-modifier-key 'alt) " A-")
1638bf54 1362 (t " M-"))))
a1506d29 1363 (message
72cc582e 1364 (concat (if help "C-?:help" "")
a1506d29 1365 M "p:pad" M "o:open" M "c:close" M "b:blank"
72cc582e
KS
1366 M "s:string" M "f:fill" M "i:incr" M "n:seq"))))
1367
1368
1369;;; CUA-like cut & paste for rectangles
1370
1371(defun cua--cancel-rectangle ()
1372 ;; Cancel rectangle
1373 (if cua--rectangle
1374 (cua--deactivate-rectangle))
1375 (setq cua--last-rectangle nil))
1376
1377(defun cua--rectangle-post-command ()
1378 (if cua--restored-rectangle
e4907bbe
KS
1379 (progn
1380 (setq cua--rectangle cua--restored-rectangle
1381 cua--restored-rectangle nil
1382 mark-active t
1383 deactivate-mark nil)
1384 (cua--rectangle-set-corners))
72cc582e
KS
1385 (when (and cua--rectangle cua--buffer-and-point-before-command
1386 (equal (car cua--buffer-and-point-before-command) (current-buffer))
1387 (not (= (cdr cua--buffer-and-point-before-command) (point))))
1388 (if (cua--rectangle-right-side)
1389 (cua--rectangle-right (current-column))
1390 (cua--rectangle-left (current-column)))
1391 (if (>= (cua--rectangle-corner) 2)
1392 (cua--rectangle-bot t)
72797108 1393 (cua--rectangle-top t))))
72cc582e
KS
1394 (if cua--rectangle
1395 (if (and mark-active
1396 (not deactivate-mark))
1397 (cua--highlight-rectangle)
c0e4cc19
KS
1398 (cua--deactivate-rectangle))
1399 (when cua--rectangle-overlays
1400 ;; clean-up after revert-buffer
026056a4 1401 (mapc (function delete-overlay) cua--rectangle-overlays)
c0e4cc19
KS
1402 (setq cua--rectangle-overlays nil)
1403 (setq deactivate-mark t)))
e4907bbe
KS
1404 (when cua--rect-undo-set-point
1405 (goto-char cua--rect-undo-set-point)
1406 (setq cua--rect-undo-set-point nil)))
72cc582e
KS
1407
1408;;; Initialization
1409
1410(defun cua--rect-M/H-key (key cmd)
1411 (cua--M/H-key cua--rectangle-keymap key cmd))
1412
72cc582e 1413(defun cua--init-rectangles ()
27366995
KS
1414 (define-key cua--rectangle-keymap cua-rectangle-mark-key 'cua-clear-rectangle-mark)
1415 (define-key cua--region-keymap cua-rectangle-mark-key 'cua-toggle-rectangle-mark)
1638bf54
KS
1416 (unless (eq cua--rectangle-modifier-key 'meta)
1417 (cua--rect-M/H-key ?\s 'cua-clear-rectangle-mark)
1418 (cua--M/H-key cua--region-keymap ?\s 'cua-toggle-rectangle-mark))
72cc582e
KS
1419
1420 (define-key cua--rectangle-keymap [remap copy-region-as-kill] 'cua-copy-rectangle)
1421 (define-key cua--rectangle-keymap [remap kill-ring-save] 'cua-copy-rectangle)
1422 (define-key cua--rectangle-keymap [remap kill-region] 'cua-cut-rectangle)
1423 (define-key cua--rectangle-keymap [remap delete-char] 'cua-delete-rectangle)
1424 (define-key cua--rectangle-keymap [remap set-mark-command] 'cua-toggle-rectangle-mark)
1425
1426 (define-key cua--rectangle-keymap [remap forward-char] 'cua-resize-rectangle-right)
1427 (define-key cua--rectangle-keymap [remap backward-char] 'cua-resize-rectangle-left)
1428 (define-key cua--rectangle-keymap [remap next-line] 'cua-resize-rectangle-down)
1429 (define-key cua--rectangle-keymap [remap previous-line] 'cua-resize-rectangle-up)
1430 (define-key cua--rectangle-keymap [remap end-of-line] 'cua-resize-rectangle-eol)
1431 (define-key cua--rectangle-keymap [remap beginning-of-line] 'cua-resize-rectangle-bol)
1432 (define-key cua--rectangle-keymap [remap end-of-buffer] 'cua-resize-rectangle-bot)
1433 (define-key cua--rectangle-keymap [remap beginning-of-buffer] 'cua-resize-rectangle-top)
1434 (define-key cua--rectangle-keymap [remap scroll-down] 'cua-resize-rectangle-page-up)
1435 (define-key cua--rectangle-keymap [remap scroll-up] 'cua-resize-rectangle-page-down)
32129746
JL
1436 (define-key cua--rectangle-keymap [remap scroll-down-command] 'cua-resize-rectangle-page-up)
1437 (define-key cua--rectangle-keymap [remap scroll-up-command] 'cua-resize-rectangle-page-down)
72cc582e
KS
1438
1439 (define-key cua--rectangle-keymap [remap delete-backward-char] 'cua-delete-char-rectangle)
1440 (define-key cua--rectangle-keymap [remap backward-delete-char] 'cua-delete-char-rectangle)
1441 (define-key cua--rectangle-keymap [remap backward-delete-char-untabify] 'cua-delete-char-rectangle)
1442 (define-key cua--rectangle-keymap [remap self-insert-command] 'cua-insert-char-rectangle)
1443 (define-key cua--rectangle-keymap [remap self-insert-iso] 'cua-insert-char-rectangle)
a1506d29 1444
e3f01f30
KS
1445 ;; Catch self-inserting characters which are "stolen" by other modes
1446 (define-key cua--rectangle-keymap [t]
1447 '(menu-item "sic" cua-insert-char-rectangle :filter cua--self-insert-char-p))
1448
72cc582e
KS
1449 (define-key cua--rectangle-keymap "\r" 'cua-rotate-rectangle)
1450 (define-key cua--rectangle-keymap "\t" 'cua-indent-rectangle)
1451
1452 (define-key cua--rectangle-keymap [(control ??)] 'cua-help-for-rectangle)
1453
1454 (define-key cua--rectangle-keymap [mouse-1] 'cua-mouse-set-rectangle-mark)
1455 (define-key cua--rectangle-keymap [down-mouse-1] 'cua--mouse-ignore)
1456 (define-key cua--rectangle-keymap [drag-mouse-1] 'cua--mouse-ignore)
1457 (define-key cua--rectangle-keymap [mouse-3] 'cua-mouse-save-then-kill-rectangle)
1458 (define-key cua--rectangle-keymap [down-mouse-3] 'cua--mouse-ignore)
1459 (define-key cua--rectangle-keymap [drag-mouse-3] 'cua--mouse-ignore)
1460
1461 (cua--rect-M/H-key 'up 'cua-move-rectangle-up)
1462 (cua--rect-M/H-key 'down 'cua-move-rectangle-down)
1463 (cua--rect-M/H-key 'left 'cua-move-rectangle-left)
1464 (cua--rect-M/H-key 'right 'cua-move-rectangle-right)
1465
1466 (cua--rect-M/H-key '(control up) 'cua-scroll-rectangle-up)
1467 (cua--rect-M/H-key '(control down) 'cua-scroll-rectangle-down)
1468
1469 (cua--rect-M/H-key ?a 'cua-align-rectangle)
1470 (cua--rect-M/H-key ?b 'cua-blank-rectangle)
1471 (cua--rect-M/H-key ?c 'cua-close-rectangle)
1472 (cua--rect-M/H-key ?f 'cua-fill-char-rectangle)
1473 (cua--rect-M/H-key ?i 'cua-incr-rectangle)
1474 (cua--rect-M/H-key ?k 'cua-cut-rectangle-as-text)
1475 (cua--rect-M/H-key ?l 'cua-downcase-rectangle)
1476 (cua--rect-M/H-key ?m 'cua-copy-rectangle-as-text)
1477 (cua--rect-M/H-key ?n 'cua-sequence-rectangle)
1478 (cua--rect-M/H-key ?o 'cua-open-rectangle)
72797108 1479 (cua--rect-M/H-key ?p 'cua-toggle-rectangle-virtual-edges)
72cc582e
KS
1480 (cua--rect-M/H-key ?P 'cua-do-rectangle-padding)
1481 (cua--rect-M/H-key ?q 'cua-refill-rectangle)
1482 (cua--rect-M/H-key ?r 'cua-replace-in-rectangle)
1483 (cua--rect-M/H-key ?R 'cua-reverse-rectangle)
1484 (cua--rect-M/H-key ?s 'cua-string-rectangle)
1485 (cua--rect-M/H-key ?t 'cua-text-fill-rectangle)
1486 (cua--rect-M/H-key ?u 'cua-upcase-rectangle)
1487 (cua--rect-M/H-key ?| 'cua-shell-command-on-rectangle)
1488 (cua--rect-M/H-key ?' 'cua-restrict-prefix-rectangle)
1489 (cua--rect-M/H-key ?/ 'cua-restrict-regexp-rectangle)
1490
1491 (setq cua--rectangle-initialized t))
1492
2c0f8564
GM
1493(provide 'cua-rect)
1494
72cc582e 1495;;; cua-rect.el ends here