(vc-fetch-master-properties): Count cvs status "Needs Patch" as
[bpt/emacs.git] / lisp / mouse.el
index e73d74d..d70d40d 100644 (file)
@@ -1,6 +1,6 @@
 ;;; mouse.el --- window system-independent mouse support.
 
-;;; Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+;;; Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: hardware
     (define-key newmap (vector (car event))
       (nconc (make-sparse-keymap "Menu")
             (mouse-major-mode-menu-1
-             (lookup-key (current-local-map) [menu-bar]))))
+             (and (current-local-map)
+                  (lookup-key (current-local-map) [menu-bar])))))
     (mouse-major-mode-menu-compute-equiv-keys newmap)
-    (command-execute
-     ;; Make NEWMAP override the usual definition
-     ;; of the mouse button that got us here.
-     ;; Then read the user's menu choice.
-     (let ((minor-mode-map-alist
-           (cons (cons t newmap) minor-mode-map-alist)))
-       (lookup-key newmap (read-key-sequence ""))))))
+    ;; Make NEWMAP override the usual definition
+    ;; of the mouse button that got us here.
+    ;; Then read the user's menu choice.
+    (let* ((minor-mode-map-alist
+           (cons (cons t newmap) minor-mode-map-alist))
+          ;; read-key-sequence quits if the user aborts the menu.
+          ;; If that happens, do nothing silently.
+          (keyseq (condition-case nil
+                      (read-key-sequence "")
+                    (quit nil)))
+          (command (if keyseq (lookup-key newmap keyseq))))
+      (if command
+         (command-execute command)))))
 
 ;; Compute and cache the equivalent keys in MENU and all its submenus.
 (defun mouse-major-mode-menu-compute-equiv-keys (menu)
@@ -292,6 +299,17 @@ This should be bound to a mouse click event type."
     (if (numberp (posn-point posn))
        (goto-char (posn-point posn)))))
 
+(defvar mouse-last-region-beg nil)
+(defvar mouse-last-region-end nil)
+(defvar mouse-last-region-tick nil)
+
+(defun mouse-region-match ()
+  "Return non-nil if there's an active region that was set with the mouse."
+  (and (mark t) mark-active
+       (eq mouse-last-region-beg (region-beginning))
+       (eq mouse-last-region-end (region-end))
+       (eq mouse-last-region-tick (buffer-modified-tick))))
+
 (defun mouse-set-region (click)
   "Set the region to the text dragged over, and copy to kill ring.
 This should be bound to a mouse drag event."
@@ -303,8 +321,9 @@ This should be bound to a mouse drag event."
     (if (numberp (posn-point posn))
        (goto-char (posn-point posn)))
     ;; If mark is highlighted, no need to bounce the cursor.
-    (or (and transient-mark-mode
-            (framep (selected-frame)))
+    ;; On X, we highlight while dragging, thus once again no need to bounce.
+    (or transient-mark-mode
+       (eq (framep (selected-frame)) 'x)
        (sit-for 1))
     (push-mark)
     (set-mark (point))
@@ -312,8 +331,15 @@ This should be bound to a mouse drag event."
        (goto-char (posn-point end)))
     ;; Don't set this-command to kill-region, so that a following
     ;; C-w will not double the text in the kill ring.
-    (let (this-command)
-      (copy-region-as-kill (mark) (point)))))
+    ;; Ignore last-command so we don't append to a preceding kill.
+    (let (this-command last-command)
+      (copy-region-as-kill (mark) (point)))
+    (mouse-set-region-1)))
+
+(defun mouse-set-region-1 ()
+  (setq mouse-last-region-beg (region-beginning))
+  (setq mouse-last-region-end (region-end))
+  (setq mouse-last-region-tick (buffer-modified-tick)))
 
 (defvar mouse-scroll-delay 0.25
   "*The pause between scroll steps caused by mouse drags, in seconds.
@@ -353,11 +379,15 @@ Upon exit, point is at the far edge of the newly visible text."
     (or (eq window (selected-window))
        (goto-char opoint))))
 
+;; Create an overlay and immediately delete it, to get "overlay in no buffer".
 (defvar mouse-drag-overlay (make-overlay 1 1))
+(delete-overlay mouse-drag-overlay)
 (overlay-put mouse-drag-overlay 'face 'region)
 
 (defvar mouse-selection-click-count 0)
 
+(defvar mouse-selection-click-count-buffer nil)
+
 (defun mouse-drag-region (start-event)
   "Set the region to the text that the mouse is dragged over.
 Highlight the drag area as you move the mouse.
@@ -378,6 +408,7 @@ release the mouse button.  Otherwise, it does not."
                   (1- (nth 3 bounds))))
         (click-count (1- (event-click-count start-event))))
     (setq mouse-selection-click-count click-count)
+    (setq mouse-selection-click-count-buffer (current-buffer))
     (mouse-set-point start-event)
     (let ((range (mouse-start-end start-point start-point click-count)))
       (move-overlay mouse-drag-overlay (car range) (nth 1 range)
@@ -413,21 +444,30 @@ release the mouse button.  Otherwise, it does not."
                  (mouse-scroll-subr start-window (1+ (- mouse-row bottom))
                                     mouse-drag-overlay start-point)))))))))
       (if (consp event)
-;;; When we scroll into the mode line or menu bar, or out of the window,
-;;; we get events that don't fit these criteria.
-;;;           (eq (get (event-basic-type event) 'event-kind) 'mouse-click)
-;;;           (eq (posn-window (event-end event)) start-window)
-;;;           (numberp (posn-point (event-end event)))
          (let ((fun (key-binding (vector (car event)))))
-           (if (not (= (overlay-start mouse-drag-overlay)
-                       (overlay-end mouse-drag-overlay)))
-               (let (last-command this-command)
-                 (push-mark (overlay-start mouse-drag-overlay) t t)
-                 (goto-char (overlay-end mouse-drag-overlay))
-                 (copy-region-as-kill (point) (mark t)))
-             (goto-char (overlay-end mouse-drag-overlay))
-             (setq this-command 'mouse-set-point))))
-      (delete-overlay mouse-drag-overlay))))
+           ;; Run the binding of the terminating up-event, if possible.
+           ;; In the case of a multiple click, it gives the wrong results,
+           ;; because it would fail to set up a region.
+           (if (and (= (mod mouse-selection-click-count 3) 0) (fboundp fun))
+               ;; In this case, we can just let the up-event execute normally.
+               (progn
+                 ;; Delete the overlay before calling the function,
+                 ;; because delete-overlay increases buffer-modified-tick.
+                 (delete-overlay mouse-drag-overlay)
+                 (setq unread-command-events
+                       (cons event unread-command-events)))
+             (if (not (= (overlay-start mouse-drag-overlay)
+                         (overlay-end mouse-drag-overlay)))
+                 (let (last-command this-command)
+                   (push-mark (overlay-start mouse-drag-overlay) t t)
+                   (goto-char (overlay-end mouse-drag-overlay))
+                   (delete-overlay mouse-drag-overlay)
+                   (copy-region-as-kill (point) (mark t))
+                   (mouse-set-region-1))
+               (goto-char (overlay-end mouse-drag-overlay))
+               (setq this-command 'mouse-set-point)
+               (delete-overlay mouse-drag-overlay))))
+       (delete-overlay mouse-drag-overlay)))))
 \f
 ;; Commands to handle xterm-style multiple clicks.
 
@@ -551,6 +591,7 @@ regardless of where you click."
   (run-hooks 'mouse-leave-buffer-hook)
   (or mouse-yank-at-point (mouse-set-point click))
   (setq this-command 'yank)
+  (setq mouse-selection-click-count 0)
   (yank arg))
 
 (defun mouse-kill-ring-save (click)
@@ -618,73 +659,85 @@ selection through the word or line clicked on.  If you do this
 again in a different position, it extends the selection again.
 If you do this twice in the same position, the selection is killed." 
   (interactive "e")
-  (mouse-minibuffer-check click)
-  (let ((click-posn (posn-point (event-start click)))
-       ;; Don't let a subsequent kill command append to this one:
-       ;; prevent setting this-command to kill-region.
-       (this-command this-command))
-    (if (> (mod mouse-selection-click-count 3) 0)
-       (if (not (and (eq last-command 'mouse-save-then-kill)
-                     (equal click-posn
-                            (car (cdr-safe (cdr-safe mouse-save-then-kill-posn))))))
-           ;; Find both ends of the object selected by this click.
-           (let* ((range
-                   (mouse-start-end click-posn click-posn
-                                    mouse-selection-click-count)))
-             ;; Move whichever end is closer to the click.
-             ;; That's what xterm does, and it seems reasonable.
-             (if (< (abs (- click-posn (mark t)))
-                    (abs (- click-posn (point))))
-                 (set-mark (car range))
-               (goto-char (nth 1 range)))
-             ;; We have already put the old region in the kill ring.
-             ;; Replace it with the extended region.
-             ;; (It would be annoying to make a separate entry.)
-             (kill-new (buffer-substring (point) (mark t)) t)
-             ;; Arrange for a repeated mouse-3 to kill this region.
-             (setq mouse-save-then-kill-posn
-                   (list (car kill-ring) (point) click-posn))
-             (mouse-show-mark))
-         ;; If we click this button again without moving it,
-         ;; that time kill.
-         (mouse-save-then-kill-delete-region (point) (mark))
-         (setq mouse-selection-click-count 0)
-         (setq mouse-save-then-kill-posn nil))
-      (if (and (eq last-command 'mouse-save-then-kill)
-              mouse-save-then-kill-posn
-              (eq (car mouse-save-then-kill-posn) (car kill-ring))
-              (equal (cdr mouse-save-then-kill-posn) (list (point) click-posn)))
-         ;; If this is the second time we've called
-         ;; mouse-save-then-kill, delete the text from the buffer.
-         (progn
-           (mouse-save-then-kill-delete-region (point) (mark))
-           ;; After we kill, another click counts as "the first time".
+  (let ((before-scroll point-before-scroll))
+    (mouse-minibuffer-check click)
+    (let ((click-posn (posn-point (event-start click)))
+         ;; Don't let a subsequent kill command append to this one:
+         ;; prevent setting this-command to kill-region.
+         (this-command this-command))
+      (if (and (save-excursion
+                (set-buffer (window-buffer (posn-window (event-start click))))
+                (and (mark t) (> (mod mouse-selection-click-count 3) 0)
+                     ;; Don't be fooled by a recent click in some other buffer.
+                     (eq mouse-selection-click-count-buffer 
+                         (current-buffer)))))
+         (if (not (and (eq last-command 'mouse-save-then-kill)
+                       (equal click-posn
+                              (car (cdr-safe (cdr-safe mouse-save-then-kill-posn))))))
+             ;; Find both ends of the object selected by this click.
+             (let* ((range
+                     (mouse-start-end click-posn click-posn
+                                      mouse-selection-click-count)))
+               ;; Move whichever end is closer to the click.
+               ;; That's what xterm does, and it seems reasonable.
+               (if (< (abs (- click-posn (mark t)))
+                      (abs (- click-posn (point))))
+                   (set-mark (car range))
+                 (goto-char (nth 1 range)))
+               ;; We have already put the old region in the kill ring.
+               ;; Replace it with the extended region.
+               ;; (It would be annoying to make a separate entry.)
+               (kill-new (buffer-substring (point) (mark t)) t)
+               (mouse-set-region-1)
+               ;; Arrange for a repeated mouse-3 to kill this region.
+               (setq mouse-save-then-kill-posn
+                     (list (car kill-ring) (point) click-posn))
+               (mouse-show-mark))
+           ;; If we click this button again without moving it,
+           ;; that time kill.
+           (mouse-save-then-kill-delete-region (mark) (point))
+           (setq mouse-selection-click-count 0)
            (setq mouse-save-then-kill-posn nil))
-       (if (or (and (eq last-command 'mouse-save-then-kill)
-                    mouse-save-then-kill-posn)
-               (and mark-active transient-mark-mode)
-               (and (eq last-command 'mouse-drag-region)
-                    (or mark-even-if-inactive
-                        (not transient-mark-mode))))
-           ;; We have a selection or suitable region, so adjust it.
-           (let* ((posn (event-start click))
-                  (new (posn-point posn)))
-             (select-window (posn-window posn))
-             (if (numberp new)
-                 (progn
-                   ;; Move whichever end of the region is closer to the click.
-                   ;; That is what xterm does, and it seems reasonable.
-                   (if (< (abs (- new (point))) (abs (- new (mark t))))
-                       (goto-char new)
-                     (set-mark new))
-                   (setq deactivate-mark nil)))
-             (kill-new (buffer-substring (point) (mark t)) t))
-         ;; We just have point, so set mark here.
-         (mouse-set-mark-fast click)
-         (kill-ring-save (point) (mark t)))
-       (mouse-show-mark)
-       (setq mouse-save-then-kill-posn
-             (list (car kill-ring) (point) click-posn))))))
+       (if (and (eq last-command 'mouse-save-then-kill)
+                mouse-save-then-kill-posn
+                (eq (car mouse-save-then-kill-posn) (car kill-ring))
+                (equal (cdr mouse-save-then-kill-posn) (list (point) click-posn)))
+           ;; If this is the second time we've called
+           ;; mouse-save-then-kill, delete the text from the buffer.
+           (progn
+             (mouse-save-then-kill-delete-region (point) (mark))
+             ;; After we kill, another click counts as "the first time".
+             (setq mouse-save-then-kill-posn nil))
+         (if (or (and (eq last-command 'mouse-save-then-kill)
+                      mouse-save-then-kill-posn)
+                 (and mark-active transient-mark-mode)
+                 (and (memq last-command
+                            '(mouse-drag-region mouse-set-region))
+                      (or mark-even-if-inactive
+                          (not transient-mark-mode))))
+             ;; We have a selection or suitable region, so adjust it.
+             (let* ((posn (event-start click))
+                    (new (posn-point posn)))
+               (select-window (posn-window posn))
+               (if (numberp new)
+                   (progn
+                     ;; Move whichever end of the region is closer to the click.
+                     ;; That is what xterm does, and it seems reasonable.
+                     (if (< (abs (- new (point))) (abs (- new (mark t))))
+                         (goto-char new)
+                       (set-mark new))
+                     (setq deactivate-mark nil)))
+               (kill-new (buffer-substring (point) (mark t)) t)
+               (mouse-show-mark))
+           ;; Set the mark where point is, then move where clicked.
+           (mouse-set-mark-fast click)
+           (if before-scroll
+               (goto-char before-scroll))
+           (exchange-point-and-mark)
+           (kill-new (buffer-substring (point) (mark t))))
+         (mouse-set-region-1)
+         (setq mouse-save-then-kill-posn
+               (list (car kill-ring) (point) click-posn)))))))
 \f
 (global-set-key [M-mouse-1] 'mouse-start-secondary)
 (global-set-key [M-drag-mouse-1] 'mouse-set-secondary)
@@ -854,12 +907,13 @@ is to prevent accidents."
                (window-buffer (posn-window (event-start click)))
              (current-buffer)))
        (error "Select or click on the buffer where the secondary selection is")))
-  (save-excursion
-    (set-buffer (overlay-buffer mouse-secondary-overlay))
-    (kill-region (overlay-start mouse-secondary-overlay)
-                (overlay-end mouse-secondary-overlay)))
+  (let (this-command)
+    (save-excursion
+      (set-buffer (overlay-buffer mouse-secondary-overlay))
+      (kill-region (overlay-start mouse-secondary-overlay)
+                  (overlay-end mouse-secondary-overlay))))
   (delete-overlay mouse-secondary-overlay)
-  (x-set-selection 'SECONDARY nil)
+;;;  (x-set-selection 'SECONDARY nil)
   (setq mouse-secondary-overlay nil))
 
 (defun mouse-secondary-save-then-kill (click)
@@ -973,8 +1027,8 @@ again.  If you do this twice in the same position, it kills the selection."
                  (overlay-put mouse-secondary-overlay 'face 'secondary-selection))))
          (setq mouse-save-then-kill-posn
                (list (car kill-ring) (point) click-posn))))
-      (x-set-selection 'SECONDARY
-                      (if (overlay-buffer mouse-secondary-overlay)
+      (if (overlay-buffer mouse-secondary-overlay)
+         (x-set-selection 'SECONDARY
                           (buffer-substring
                            (overlay-start mouse-secondary-overlay)
                            (overlay-end mouse-secondary-overlay)))))))
@@ -1002,20 +1056,25 @@ and selects that window."
                         (let ((elt (car tail)))
                           (if (not (string-match "^ "
                                                  (buffer-name elt)))
-                              (setq head (cons
-                                          (cons
-                                           (format
-                                            (format "%%%ds  %%s%%s  %%s"
-                                                    maxbuf)
-                                            (buffer-name elt)
-                                            (if (buffer-modified-p elt)
-                                                "*" " ")
-                                            (save-excursion
-                                              (set-buffer elt)
-                                              (if buffer-read-only "%" " "))
-                                            (or (buffer-file-name elt) ""))
-                                           elt)
-                                          head))))
+                              (setq head
+                               (cons
+                                (cons
+                                 (format
+                                  (format "%%%ds  %%s%%s  %%s" maxbuf)
+                                  (buffer-name elt)
+                                  (if (buffer-modified-p elt) "*" " ")
+                                  (save-excursion
+                                    (set-buffer elt)
+                                    (if buffer-read-only "%" " "))
+                                  (or (buffer-file-name elt) 
+                                      (save-excursion
+                                        (set-buffer elt)
+                                        (if list-buffers-directory
+                                            (expand-file-name
+                                             list-buffers-directory)))
+                                      ""))
+                                 elt)
+                                head))))
                         (setq tail (cdr tail)))
                       (reverse head))))))
     (let ((buf (x-popup-menu event menu))