Merge from emacs--devo--0
[bpt/emacs.git] / lisp / emulation / cua-base.el
index 26d94e9..337be13 100644 (file)
@@ -1,7 +1,7 @@
 ;;; cua-base.el --- emulate CUA key bindings
 
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005 Free Software Foundation, Inc.
+;;   2005, 2006, 2007 Free Software Foundation, Inc.
 
 ;; Author: Kim F. Storm <storm@cua.dk>
 ;; Keywords: keyboard emulation convenience cua
@@ -10,7 +10,7 @@
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -305,11 +305,31 @@ If the value is nil, use a shifted prefix key to inhibit the override."
                 (const :tag "No delay" nil))
   :group 'cua)
 
+(defcustom cua-delete-selection t
+  "*If non-nil, typed text replaces text in the active selection."
+  :type '(choice (const :tag "Disabled" nil)
+                (other :tag "Enabled" t))
+  :group 'cua)
+
 (defcustom cua-keep-region-after-copy nil
   "If non-nil, don't deselect the region after copying."
   :type 'boolean
   :group 'cua)
 
+(defcustom cua-toggle-set-mark t
+  "*If non-nil, the `cua-set-mark' command toggles the mark."
+  :type '(choice (const :tag "Disabled" nil)
+                (other :tag "Enabled" t))
+  :group 'cua)
+
+(defcustom cua-auto-mark-last-change nil
+  "*If non-nil, set implicit mark at position of last buffer change.
+This means that \\[universal-argument] \\[cua-set-mark] will jump to the position
+of the last buffer change before jumping to the explicit marks on the mark ring.
+See `cua-set-mark' for details."
+  :type 'boolean
+  :group 'cua)
+
 (defcustom cua-enable-register-prefix 'not-ctrl-u
   "*If non-nil, registers are supported via numeric prefix arg.
 If the value is t, any numeric prefix arg in the range 0 to 9 will be
@@ -346,6 +366,15 @@ managers, so try setting this to nil, if prefix override doesn't work."
   :type 'boolean
   :group 'cua)
 
+(defcustom cua-paste-pop-rotate-temporarily nil
+  "*If non-nil, \\[cua-paste-pop] only rotates the kill-ring temporarily.
+This means that both \\[yank] and the first \\[yank-pop] in a sequence always insert
+the most recently killed text.  Each immediately following \\[cua-paste-pop] replaces
+the previous text with the next older element on the `kill-ring'.
+With prefix arg, \\[universal-argument] \\[yank-pop] inserts the same text as the most
+recent \\[yank-pop] (or \\[yank]) command."
+  :type 'boolean
+  :group 'cua)
 
 ;;; Rectangle Customization
 
@@ -370,12 +399,32 @@ and after the region marked by the rectangle to search."
                 (other :tag "Enabled" t))
   :group 'cua)
 
+(defvar cua-global-keymap)             ; forward
+(defvar cua--region-keymap)            ; forward
+
+(defcustom cua-rectangle-mark-key [(control return)]
+  "Global key used to toggle the cua rectangle mark."
+  :set #'(lambda (symbol value)
+          (set symbol value)
+          (when (and (boundp 'cua--keymaps-initalized)
+                     cua--keymaps-initalized)
+            (define-key cua-global-keymap value
+              'cua-set-rectangle-mark)
+            (when (boundp 'cua--rectangle-keymap)
+              (define-key cua--rectangle-keymap value
+                'cua-clear-rectangle-mark)
+              (define-key cua--region-keymap value
+                'cua-toggle-rectangle-mark))))
+  :type 'key-sequence
+  :group 'cua)
+
 (defcustom cua-rectangle-modifier-key 'meta
   "*Modifier key used for rectangle commands bindings.
 On non-window systems, always use the meta modifier.
 Must be set prior to enabling CUA."
   :type '(choice (const :tag "Meta key" meta)
-                (const :tag "Hyper key" hyper )
+                (const :tag "Alt key" alt)
+                (const :tag "Hyper key" hyper)
                 (const :tag "Super key" super))
   :group 'cua)
 
@@ -753,7 +802,7 @@ Save a copy in register 0 if `cua-delete-copy-to-register-0' is non-nil."
   (let ((start (mark)) (end (point)))
     (or (<= start end)
        (setq start (prog1 end (setq end start))))
-    (setq cua--last-deleted-region-text (buffer-substring start end))
+    (setq cua--last-deleted-region-text (filter-buffer-substring start end))
     (if cua-delete-copy-to-register-0
        (set-register ?0 cua--last-deleted-region-text))
     (delete-region start end)
@@ -767,7 +816,7 @@ Save a copy in register 0 if `cua-delete-copy-to-register-0' is non-nil."
 (defun cua-replace-region ()
   "Replace the active region with the character you type."
   (interactive)
-  (let ((not-empty (cua-delete-region)))
+  (let ((not-empty (and cua-delete-selection (cua-delete-region))))
     (unless (eq this-original-command this-command)
       (let ((overwrite-mode
             (and overwrite-mode
@@ -842,16 +891,13 @@ If global mark is active, copy from register or one character."
       (if regtxt
          (cua--insert-at-global-mark regtxt)
        (when (not (eobp))
-         (cua--insert-at-global-mark (buffer-substring (point) (+ (point) count)))
+         (cua--insert-at-global-mark (filter-buffer-substring (point) (+ (point) count)))
          (forward-char count))))
      (buffer-read-only
       (message "Cannot paste into a read-only buffer"))
      (t
       ;; Must save register here, since delete may override reg 0.
       (if mark-active
-         ;; Before a yank command, make sure we don't yank
-         ;; the same region that we are going to delete.
-         ;; That would make yank a no-op.
          (if cua--rectangle
              (progn
                (goto-char (min (mark) (point)))
@@ -859,8 +905,16 @@ If global mark is active, copy from register or one character."
                (setq paste-lines (cua--delete-rectangle))
                (if (= paste-lines 1)
                    (setq paste-lines nil))) ;; paste all
-           (if (string= (buffer-substring (point) (mark))
-                        (car kill-ring))
+           ;; Before a yank command, make sure we don't yank the
+           ;; head of the kill-ring that really comes from the
+           ;; currently active region we are going to delete
+           ;; (when last-command is one that uses copy-region-as-kill
+           ;; or kill-new).  That would make yank a no-op.
+           (if (and (string= (filter-buffer-substring (point) (mark))
+                             (car kill-ring))
+                    (memq last-command
+                          '(mouse-set-region mouse-drag-region
+                            mouse-save-then-kill mouse-secondary-save-then-kill)))
                (current-kill 1))
            (cua-delete-region)))
       (cond
@@ -881,17 +935,57 @@ If global mark is active, copy from register or one character."
          (if arg (goto-char pt))))
        ((eq this-original-command 'clipboard-yank)
        (clipboard-yank))
+       ((eq this-original-command 'x-clipboard-yank)
+       (x-clipboard-yank))
        (t (yank arg)))))))
 
+
+;; cua-paste-pop-rotate-temporarily == t mechanism:
+;;
+;; C-y M-y M-y => only rotates kill ring temporarily,
+;;                so next C-y yanks what previous C-y yanked,
+;;
+;; M-y M-y M-y => equivalent to C-y M-y M-y
+;;
+;; But: After another command, C-u M-y remembers the temporary
+;;      kill-ring position, so
+;; C-u M-y     => yanks what the last M-y yanked
+;;
+
+(defvar cua-paste-pop-count nil)
+
 (defun cua-paste-pop (arg)
   "Replace a just-pasted text or rectangle with a different text.
-See `yank-pop' for details."
+See `yank-pop' for details about the default behaviour.  For an alternative
+behaviour, see `cua-paste-pop-rotate-temporarily'."
   (interactive "P")
-  (if (eq last-command 'cua--paste-rectangle)
-      (progn
-       (undo)
-       (yank arg))
-    (yank-pop (prefix-numeric-value arg))))
+  (cond
+   ((eq last-command 'cua--paste-rectangle)
+    (undo)
+    (yank arg))
+   ((not cua-paste-pop-rotate-temporarily)
+    (yank-pop (prefix-numeric-value arg)))
+   (t
+    (let ((rotate (if (consp arg) 1 (prefix-numeric-value arg))))
+      (cond
+       ((or (null cua-paste-pop-count)
+           (eq last-command 'yank)
+           (eq last-command 'cua-paste))
+       (setq cua-paste-pop-count rotate)
+       (setq last-command 'yank)
+       (yank-pop cua-paste-pop-count))
+       ((and (eq last-command 'cua-paste-pop) (not (consp arg)))
+       (setq cua-paste-pop-count (+ cua-paste-pop-count rotate))
+       (setq last-command 'yank)
+       (yank-pop cua-paste-pop-count))
+       (t
+       (setq cua-paste-pop-count
+             (if (consp arg) (+ cua-paste-pop-count rotate -1) 1))
+       (yank (1+ cua-paste-pop-count)))))
+    ;; Undo rotating the kill-ring, so next C-y will
+    ;; yank the original head.
+    (setq kill-ring-yank-pointer kill-ring)
+    (setq this-command 'cua-paste-pop))))
 
 (defun cua-exchange-point-and-mark (arg)
   "Exchanges point and mark, but don't activate the mark.
@@ -932,14 +1026,14 @@ of text."
                (if (and s (= (cdr u) s))
                    (setq s (car u))
                  (setq s (car u) e (cdr u)))))))
-         (setq cua--repeat-replace-text
-               (cond ((and s e (<= s e) (= s (mark t)))
-                      (buffer-substring-no-properties s e))
-                     ((and (null s) (eq u elt)) ;; nothing inserted
-                      "")
-                     (t
-                      (message "Cannot locate replacement text")
-                      nil))))))
+         (cond ((and s e (<= s e) (= s (mark t)))
+                (setq cua--repeat-replace-text
+                      (filter-buffer-substring s e nil t)))
+               ((and (null s) (eq u elt)) ;; nothing inserted
+                (setq cua--repeat-replace-text
+                      ""))
+               (t
+                (message "Cannot locate replacement text"))))))
     (setq cua--last-deleted-region-pos nil))
   (if (and cua--last-deleted-region-text
           cua--repeat-replace-text
@@ -956,6 +1050,28 @@ of text."
 
 ;;; Shift activated / extended region
 
+(defun cua-pop-to-last-change ()
+  (let ((undo-list buffer-undo-list)
+       pos elt)
+    (while (and (not pos)
+               (consp undo-list))
+      (setq elt (car undo-list)
+           undo-list (cdr undo-list))
+      (cond
+       ((integerp elt)
+       (setq pos elt))
+       ((not (consp elt)))
+       ((and (integerp (cdr elt))
+            (or (integerp (car elt)) (stringp (car elt))))
+       (setq pos (cdr elt)))
+       ((and (eq (car elt) 'apply) (consp (cdr elt)) (integerp (cadr elt)))
+       (setq pos (nth 3 elt)))))
+    (when (and pos
+              (/= pos (point))
+              (>= pos (point-min)) (<= pos (point-max)))
+      (goto-char pos)
+      t)))
+
 (defun cua-set-mark (&optional arg)
   "Set mark at where point is, clear mark, or jump to mark.
 
@@ -964,12 +1080,15 @@ mark, and push old mark position on local mark ring; also push mark on
 global mark ring if last mark was set in another buffer.
 
 With argument, jump to mark, and pop a new position for mark off
-the local mark ring \(this does not affect the global mark ring\).
+the local mark ring (this does not affect the global mark ring).
 Use \\[pop-global-mark] to jump to a mark off the global mark ring
-\(see `pop-global-mark'\).
+\(see `pop-global-mark').
+
+If `cua-auto-mark-last-change' is non-nil, this command behaves as if there
+was an implicit mark at the position of the last buffer change.
 
 Repeating the command without the prefix jumps to the next position
-off the local \(or global\) mark ring.
+off the local (or global) mark ring.
 
 With a double \\[universal-argument] prefix argument, unconditionally set mark."
   (interactive "P")
@@ -984,8 +1103,10 @@ With a double \\[universal-argument] prefix argument, unconditionally set mark."
     (pop-global-mark))
    (arg
     (setq this-command 'pop-to-mark-command)
-    (pop-to-mark-command))
-   (mark-active
+    (or (and cua-auto-mark-last-change
+            (cua-pop-to-last-change))
+       (pop-to-mark-command)))
+   ((and cua-toggle-set-mark mark-active)
     (cua--deactivate)
     (message "Mark Cleared"))
    (t
@@ -1068,73 +1189,79 @@ If ARG is the atom `-', scroll upward by nearly full screen."
 ;;; Pre-command hook
 
 (defun cua--pre-command-handler-1 ()
-  (let ((movement (eq (get this-command 'CUA) 'move)))
-
-    ;; Cancel prefix key timeout if user enters another key.
-    (when cua--prefix-override-timer
-      (if (timerp cua--prefix-override-timer)
-         (cancel-timer cua--prefix-override-timer))
-      (setq cua--prefix-override-timer nil))
-
-    ;; Handle shifted cursor keys and other movement commands.
-    ;; If region is not active, region is activated if key is shifted.
-    ;; If region is active, region is cancelled if key is unshifted (and region not started with C-SPC).
-    ;; If rectangle is active, expand rectangle in specified direction and ignore the movement.
-    (if movement
-       (cond
-        ((if window-system
-             (memq 'shift (event-modifiers
-                           (aref (this-single-command-raw-keys) 0)))
-           (or
-            (memq 'shift (event-modifiers
-                          (aref (this-single-command-keys) 0)))
-            ;; See if raw escape sequence maps to a shifted event, e.g. S-up or C-S-home.
-            (and (boundp 'local-function-key-map)
-                 local-function-key-map
-                 (let ((ev (lookup-key local-function-key-map
-                                       (this-single-command-raw-keys))))
-                   (and (vector ev)
-                        (symbolp (setq ev (aref ev 0)))
-                        (string-match "S-" (symbol-name ev)))))))
-         (unless mark-active
-           (push-mark-command nil t))
-         (setq cua--last-region-shifted t)
-         (setq cua--explicit-region-start nil))
-        ((or cua--explicit-region-start cua--rectangle)
-         (unless mark-active
-           (push-mark-command nil nil)))
-        (t
-         ;; If we set mark-active to nil here, the region highlight will not be
-         ;; removed by the direct_output_ commands.
-         (setq deactivate-mark t)))
-
-      ;; Handle delete-selection property on other commands
-      (if (and mark-active (not deactivate-mark))
-         (let* ((ds (or (get this-command 'delete-selection)
-                        (get this-command 'pending-delete)))
-                (nc (cond
-                     ((not ds) nil)
-                     ((eq ds 'yank)
-                      'cua-paste)
-                     ((eq ds 'kill)
-                      (if cua--rectangle
-                          'cua-copy-rectangle
-                        'cua-copy-region))
-                     ((eq ds 'supersede)
-                      (if cua--rectangle
-                          'cua-delete-rectangle
-                        'cua-delete-region))
-                     (t
-                      (if cua--rectangle
-                          'cua-delete-rectangle ;; replace?
-                        'cua-replace-region)))))
-           (if nc
-               (setq this-original-command this-command
-                     this-command nc)))))
-
-    ;; Detect extension of rectangles by mouse or other movement
-    (setq cua--buffer-and-point-before-command
-         (if cua--rectangle (cons (current-buffer) (point))))))
+  ;; Cancel prefix key timeout if user enters another key.
+  (when cua--prefix-override-timer
+    (if (timerp cua--prefix-override-timer)
+       (cancel-timer cua--prefix-override-timer))
+    (setq cua--prefix-override-timer nil))
+
+  (cond
+   ;; Only symbol commands can have necessary properties
+   ((not (symbolp this-command))
+    nil)
+
+   ;; Handle delete-selection property on non-movement commands
+   ((not (eq (get this-command 'CUA) 'move))
+    (when (and mark-active (not deactivate-mark))
+      (let* ((ds (or (get this-command 'delete-selection)
+                    (get this-command 'pending-delete)))
+            (nc (cond
+                 ((not ds) nil)
+                 ((eq ds 'yank)
+                  'cua-paste)
+                 ((eq ds 'kill)
+                  (if cua--rectangle
+                      'cua-copy-rectangle
+                    'cua-copy-region))
+                 ((eq ds 'supersede)
+                  (if cua--rectangle
+                      'cua-delete-rectangle
+                    'cua-delete-region))
+                 (t
+                  (if cua--rectangle
+                      'cua-delete-rectangle ;; replace?
+                    'cua-replace-region)))))
+       (if nc
+           (setq this-original-command this-command
+                 this-command nc)))))
+
+   ;; Handle shifted cursor keys and other movement commands.
+   ;; If region is not active, region is activated if key is shifted.
+   ;; If region is active, region is cancelled if key is unshifted (and region not started with C-SPC).
+   ;; If rectangle is active, expand rectangle in specified direction and ignore the movement.
+   ((if window-system
+       (memq 'shift (event-modifiers
+                     (aref (this-single-command-raw-keys) 0)))
+      (or
+       (memq 'shift (event-modifiers
+                    (aref (this-single-command-keys) 0)))
+       ;; See if raw escape sequence maps to a shifted event, e.g. S-up or C-S-home.
+       (and (boundp 'local-function-key-map)
+           local-function-key-map
+           (let ((ev (lookup-key local-function-key-map
+                                 (this-single-command-raw-keys))))
+             (and (vector ev)
+                  (symbolp (setq ev (aref ev 0)))
+                  (string-match "S-" (symbol-name ev)))))))
+    (unless mark-active
+      (push-mark-command nil t))
+    (setq cua--last-region-shifted t)
+    (setq cua--explicit-region-start nil))
+
+   ;; Set mark if user explicitly said to do so
+   ((or cua--explicit-region-start cua--rectangle)
+    (unless mark-active
+      (push-mark-command nil nil)))
+
+   ;; Else clear mark after this command.
+   (t
+    ;; If we set mark-active to nil here, the region highlight will not be
+    ;; removed by the direct_output_ commands.
+    (setq deactivate-mark t)))
+
+  ;; Detect extension of rectangles by mouse or other movement
+  (setq cua--buffer-and-point-before-command
+       (if cua--rectangle (cons (current-buffer) (point)))))
 
 (defun cua--pre-command-handler ()
   (when cua-mode
@@ -1275,7 +1402,7 @@ If ARG is the atom `-', scroll upward by nearly full screen."
            cua-rectangle-modifier-key
          'meta))
   ;; C-return always toggles rectangle mark
-  (define-key cua-global-keymap [(control return)]     'cua-set-rectangle-mark)
+  (define-key cua-global-keymap cua-rectangle-mark-key 'cua-set-rectangle-mark)
   (unless (eq cua--rectangle-modifier-key 'meta)
     (cua--M/H-key cua-global-keymap ?\s                        'cua-set-rectangle-mark)
     (define-key cua-global-keymap
@@ -1286,6 +1413,7 @@ If ARG is the atom `-', scroll upward by nearly full screen."
   ;; replace region with rectangle or element on kill ring
   (define-key cua-global-keymap [remap yank]           'cua-paste)
   (define-key cua-global-keymap [remap clipboard-yank] 'cua-paste)
+  (define-key cua-global-keymap [remap x-clipboard-yank] 'cua-paste)
   ;; replace current yank with previous kill ring element
   (define-key cua-global-keymap [remap yank-pop]               'cua-paste-pop)
   ;; set mark
@@ -1393,10 +1521,16 @@ options:
 
 You can customize `cua-enable-cua-keys' to completely disable the
 CUA bindings, or `cua-prefix-override-inhibit-delay' to change
-the prefix fallback behavior."
+the prefix fallback behavior.
+
+CUA mode manages Transient Mark mode internally.  Trying to disable
+Transient Mark mode while CUA mode is enabled does not work; if you
+only want to highlight the region when it is selected using a
+shifted movement key, set `cua-highlight-region-shift-only'."
   :global t
   :group 'cua
-  :set-after '(cua-enable-modeline-indications cua-rectangle-modifier-key)
+  :set-after '(cua-enable-modeline-indications
+              cua-rectangle-mark-key cua-rectangle-modifier-key)
   :require 'cua-base
   :link '(emacs-commentary-link "cua-base.el")
   (setq mark-even-if-inactive t)
@@ -1476,8 +1610,8 @@ the prefix fallback behavior."
 
 ;;;###autoload (eval-after-load 'CUA-mode
 ;;;###autoload  '(error (concat "\n\n"
-;;;###autoload  "CUA-mode is now part of the standard GNU Emacs distribution,\n"
-;;;###autoload  "so you may now enable and customize CUA via the Options menu.\n\n"
+;;;###autoload  "CUA-mode is now part of the standard GNU Emacs distribution, so you may\n"
+;;;###autoload  "now enable CUA via the Options menu or by customizing option `cua-mode'.\n\n"
 ;;;###autoload  "You have loaded an older version of CUA-mode which does\n"
 ;;;###autoload  "not work correctly with this version of GNU Emacs.\n\n"
 ;;;###autoload  (if user-init-file (concat