[comp.emacs] Dragging with the mouse

moore@ucbcad.berkeley.edu (Peter X Moore) (01/07/87)

Well, if gnuemacs is now supporting both up and down mouse events,
then hopefully the following will prove useful to some.  It is set 
of x-mouse bindings that allows you to drag over a region in order 
to delete it, drag the mode-line to change window size, etc..

Peter Moore
ucbvax!moore
moore@Berkeley

;; Mouse support for X window system.
;; Copyright (C) 1985 Richard M. Stallman.

;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.


(provide 'x-mouse)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;									     ;;
;;	Bindings to allow dragging, requires up events to be sent	     ;;
;;	by x-proc-mouse-event, which seems to be in version 18-35.	     ;;
;;									     ;;
;;	Terminology:							     ;;
;;	  To click a button is press it down and then release without	     ;;
;;	      moving the mouse						     ;;
;;	  To drag a button is to press it down, move the mouse, and	     ;;
;;	      then release it.						     ;;
;;	  mouse position: position of the mouse cursor, as opposed to	     ;;
;;	      the text cursor.						     ;;
;;									     ;;
;;	The bindings set up here are:					     ;;
;;									     ;;
;;	Inside a window:						     ;;
;;									     ;;
;;	    left button click:  Set the mark at the current point and then   ;;
;;	       move the to the mouse position. Thus clicking the right	     ;;
;;	       button at two points marks the region between those points.   ;;
;;									     ;;
;;	    middle button click: sets point to the mouse position and does a ;;
;;	       yank at that point.					     ;;
;;									     ;;
;;	    right button click: sets point to the mouse position and then    ;;
;;	       scrolls that line to the top of the window.  If the mouse is  ;;
;;	       currently on the top line then the window is scrolled down.   ;;
;;									     ;;
;;	    left button drag: do a copy-region-as-kill of the region dragged ;;
;;									     ;;
;;	    middle button drag: do a kill-region of the region dragged	     ;;
;;									     ;;
;;	    right button drag: do a indent-region of the region dragged	     ;;
;;									     ;;
;;	    shift left button click - Copy from point to end of line in to   ;;
;;	        the X cut buffer and then insert it back into the buffer     ;;
;;									     ;;
;;	    shift middle button drag - Copy the region dragged into the X    ;;
;;		cut buffer						     ;;
;;									     ;;
;;	    shift right button click - insert the X cut buffer at the mouse  ;;
;;	        point							     ;;
;;									     ;;
;;	Inside the mode-line of a window:				     ;;
;;									     ;;
;;	    left button click - do a (beginning-of-defun) from the current   ;;
;;	        point							     ;;
;;									     ;;
;;	    right-button click - call (end-of-defun) from the current point  ;;
;;									     ;;
;;	    left button drag - resize the window so the mode line moves to   ;;
;;	        the new mouse position. I.e. `drag' the mode line.	     ;;
;;									     ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defconst x-button-right (char-to-string 0))
(defconst x-button-u-right (char-to-string 4))
(defconst x-button-middle (char-to-string 1))
(defconst x-button-u-middle (char-to-string 5))
(defconst x-button-left (char-to-string 2))
(defconst x-button-u-left (char-to-string 6))

(defconst x-button-s-right (char-to-string 16))
(defconst x-button-s-u-right (char-to-string 20))
(defconst x-button-s-middle (char-to-string 17))
(defconst x-button-s-u-middle (char-to-string 21))
(defconst x-button-s-left (char-to-string 18))
(defconst x-button-s-u-left (char-to-string 22))

(defconst x-button-m-right (char-to-string 32))
(defconst x-button-m-u-right (char-to-string 36))
(defconst x-button-m-middle (char-to-string 33))
(defconst x-button-m-u-middle (char-to-string 37))
(defconst x-button-m-left (char-to-string 34))
(defconst x-button-m-u-left (char-to-string 38))

(defconst x-button-c-right (char-to-string 64))
(defconst x-button-c-u-right (char-to-string 68))
(defconst x-button-c-middle (char-to-string 65))
(defconst x-button-c-u-middle (char-to-string 69))
(defconst x-button-c-left (char-to-string 66))
(defconst x-button-c-u-left (char-to-string 70))

(defconst x-button-m-s-right (char-to-string 48))
(defconst x-button-m-s-u-right (char-to-string 52))
(defconst x-button-m-s-middle (char-to-string 49))
(defconst x-button-m-s-u-middle (char-to-string 53))
(defconst x-button-m-s-left (char-to-string 50))
(defconst x-button-m-s-u-left (char-to-string 54))

(defconst x-button-c-s-right (char-to-string 80))
(defconst x-button-c-s-u-right (char-to-string 84))
(defconst x-button-c-s-middle (char-to-string 81))
(defconst x-button-c-s-u-middle (char-to-string 85))
(defconst x-button-c-s-left (char-to-string 82))
(defconst x-button-c-s-u-left (char-to-string 86))

(defconst x-button-c-m-right (char-to-string 96))
(defconst x-button-c-m-u-right (char-to-string 100))
(defconst x-button-c-m-middle (char-to-string 97))
(defconst x-button-c-m-u-middle (char-to-string 101))
(defconst x-button-c-m-left (char-to-string 98))
(defconst x-button-c-m-u-left (char-to-string 102))

(defconst x-button-c-m-s-right (char-to-string 112))
(defconst x-button-c-m-s-u-right (char-to-string 116))
(defconst x-button-c-m-s-middle (char-to-string 113))
(defconst x-button-c-m-s-u-middle (char-to-string 117))
(defconst x-button-c-m-s-left (char-to-string 114))
(defconst x-button-c-m-s-u-left (char-to-string 118))


(defvar x-process-mouse-hook nil
  "Hook to run after each mouse event is processed.  Should take two
arguments; the first being a list (XPOS YPOS) corresponding to character
offset from top left of screen and the second being a specifier for the
buttons/keys.

This will normally be set on a per-buffer basis.")

(defun x-flush-mouse-queue ()
  "Process all queued mouse events."
  ;; A mouse event causes a special character sequence to be given
  ;; as keyboard input.  That runs this function, which process all
  ;; queued mouse events and returns.
  (interactive)
  (while (> (x-mouse-events) 0)
    (x-proc-mouse-event))
  (and (boundp 'x-process-mouse-hook)
       (symbol-value 'x-process-mouse-hook)
       (funcall x-process-mouse-hook x-mouse-pos x-mouse-item)))

(define-key global-map "\C-c\C-m" 'x-flush-mouse-queue)
(define-key global-map "\C-x\C-@" 'x-flush-mouse-queue)

(defun x-mouse-select (arg)
  "Select Emacs window the mouse is on."
  (let ((start-w (selected-window))
	(done nil)
	(w (selected-window))
	(rel-coordinate nil))
    (while (and (not done)
		(null (setq rel-coordinate
			    (coordinates-in-window-p arg w))))
      (setq w (next-window w))
      (if (eq w start-w)
	  (setq done t)))
    (select-window w)
    rel-coordinate))

(defvar x-mouse-last-pos (list -1 -1) "position of the last mouse down event")
(defvar x-mouse-last-dot 0 "Value of dot BEFORE the last mouse down event")
(defvar x-mouse-doing-title-drag nil
  "Are we in the middle of a window resize?")
(defvar x-mouse-drag-window nil "Window which we are resizing")

(define-key mouse-map x-button-right 'x-mouse-mark)
(define-key mouse-map x-button-u-right 'x-mouse-fill-to-top)
(define-key mouse-map x-button-left 'x-mouse-mark-or-title)
(define-key mouse-map x-button-u-left 'x-mouse-copy-move-title)
(define-key mouse-map x-button-middle 'x-mouse-mark)
(define-key mouse-map x-button-u-middle 'x-mouse-delete-insert)
(define-key mouse-map x-button-s-middle 'x-mouse-mark)
(define-key mouse-map x-button-s-u-middle 'x-mouse-cut)
(define-key mouse-map x-button-s-right 'x-mouse-mark)
(define-key mouse-map x-button-s-u-right 'x-mouse-insert-cut)
(define-key mouse-map x-button-s-left 'x-mouse-mark)
(define-key mouse-map x-button-s-u-left 'x-mouse-re-enter-line)

(defun x-mouse-no-op (arg))

(defun x-mouse-in-title (arg)
  "If the coordinate ARG is in a title bar, return the window, else NIL"
  (let ((start-w (selected-window))
	(done nil)
	(w (selected-window))
	(y (car (cdr arg))))
    (while (and (not done)
		(/= y (1- (nth 3 (window-edges w)))))
      (setq w (next-window w))
      (if (eq w start-w)
	  (setq done t w nil)))
    w))

(defun x-mouse-do-title-drag (arg)
  (let ((owin (selected-window))
	(y (car (cdr arg)))
	(last-y (car (cdr x-mouse-last-pos))))
    (select-window x-mouse-drag-window)
    (enlarge-window (- y last-y))
    (select-window owin))
  (setq x-mouse-doing-title-drag nil))

(defun x-mouse-set-point (arg)
  "Select Emacs window mouse is on, and move point to mouse position."
  (let* ((relative-coordinate (x-mouse-select arg))
	 (rel-x (car relative-coordinate))
	 (rel-y (car (cdr relative-coordinate))))
    (if relative-coordinate
	(progn
	  (move-to-window-line rel-y)
	  (move-to-column (+ rel-x (current-column)))
	  relative-coordinate))))

(defun x-mouse-mark (arg)
  (setq x-mouse-last-dot (dot))
  (if (x-mouse-set-point arg)
      (progn
	(setq x-mouse-last-pos arg))))

(defun x-mouse-click-p (arg)
  (equal x-mouse-last-pos arg))

(defun x-mouse-mark-or-title (arg)
  (setq x-mouse-last-dot (dot))
  (let ((title-window (x-mouse-in-title arg)))
    (if title-window
	(progn
	  (setq x-mouse-drag-window title-window)
	  (setq x-mouse-doing-title-drag t)
	  (setq x-mouse-last-pos arg))
      (if (x-mouse-set-point arg)
	  (progn
	    (setq x-mouse-last-pos arg))))))

(defun x-mouse-copy-move-title (arg)
  (if x-mouse-doing-title-drag
      (if (x-mouse-click-p arg)
	  (progn
	    (end-of-defun)
	    (recenter 0)
	    (setq x-mouse-doing-title-drag nil))
	(x-mouse-do-title-drag arg))
    (if (x-mouse-click-p arg)
	(set-mark x-mouse-last-dot)
      (let ((odot (dot)))
	(if (x-mouse-set-point arg)
	    (copy-region-as-kill odot (dot)))))))

(defun x-mouse-delete-insert (arg)
  (if (x-mouse-click-p arg)
      (yank)
    (let ((odot (dot)))
      (if (x-mouse-set-point arg)
	  (kill-region odot (dot))))))

(defun x-mouse-fill-to-top (arg)
  (if (x-mouse-click-p arg)
      (if (x-mouse-in-title arg)
	  (progn
	    (beginning-of-defun)
	    (recenter 4)
	    (setq x-mouse-doing-title-drag nil))
	(let ((rel-pos (x-mouse-set-point arg)))
	  (if (eq (nth 1 rel-pos) 0) ;; first line of the window?
	      (scroll-down nil) ;; then scroll backwards
	    (recenter 0))))
    (let ((odot (dot)))
      (if (x-mouse-set-point arg)
	  (indent-region odot (dot) nil)))))

(defun x-mouse-cut (arg)
  (let ((odot (dot)))
    (if (x-mouse-set-point arg)
	(x-store-cut-buffer (buffer-substring odot (dot))))))

(defun x-mouse-insert-cut (arg)
  (if (x-mouse-set-point arg)
      (insert (x-get-cut-buffer))))

(defun x-mouse-re-enter-line (arg)
  (if (x-mouse-click-p arg)
	  (let ((odot (dot)))
	    (x-store-cut-buffer
	     (buffer-substring odot (progn (end-of-line) (dot))))
	    (goto-char (point-max))
	    (insert (x-get-cut-buffer))
	    (setq unread-command-char ?\^m))))