Source

edit-utils / avoid.el

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
;;; avoid.el --- make mouse pointer stay out of the way of editing

;;; Copyright (C) 1993, 1994 Free Software Foundation, Inc.

;; Author: Boris Goldowsky <boris@gnu.ai.mit.edu>
;; Keywords: mouse
;; Version: 1.10

;; This file is part of XEmacs.

;; XEmacs 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)
;; any later version.

;; XEmacs is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;; General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with XEmacs; see the file COPYING.  If not, write to the Free
;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
;; 02111-1307, USA.

;;; Synched up with: FSF 19.34.

;;; Commentary:

;; For those who are annoyed by the mouse pointer obscuring text,
;; this mode moves the mouse pointer - either just a little out of
;; the way, or all the way to the corner of the frame. 
;; To use, load or evaluate this file and type M-x mouse-avoidance-mode .
;; To set up permanently, put this file on your .emacs: 
;;
;; (if window-system (mouse-avoidance-mode 'animate))
;;
;; The 'animate can be 'jump or 'banish or 'exile or 'protean if you prefer.
;; See the documentation for function `mouse-avoidance-mode' for
;; details of the different modes.
;;
;; For added silliness, make the animatee animate...
;; put something similar to the following into your .emacs:
;;
;; (if window-system
;;     (mouse-avoidance-set-pointer-shape
;;	     (eval (nth (random 4)
;;			'(x-pointer-man x-pointer-spider
;;                       x-pointer-gobbler x-pointer-gumby)))))
;;
;; For completely random pointer shape, replace the setq above with:
;; (setq x-pointer-shape (mouse-avoidance-random-shape))
;; 
;; Bugs / Warnings / To-Do:
;;
;; - Using this code does slow emacs down.  "banish" mode shouldn't
;;   be too bad, and on my workstation even "animate" is reasonable.
;;
;; - It ought find out where any overlapping frames are and avoid them,
;;   rather than always raising the frame.

;; Credits:
;; This code was helped by all those who contributed suggestions, 
;;   fixes, and additions
;; Joe Harrington (and his advisor), for the original inspiration.
;; Ken Manheimer, for dreaming up the Protean mode.
;; Richard Stallman, for the awful cat-and-mouse pun, among other things.
;; Mike Williams, Denis Howe, Bill Benedetto, Chris Moore, Don Morris,
;; Simon Marshall, and M.S. Ashton, for their feedback.

;;; Code:

(provide 'avoid)

(defgroup avoid nil
  "Make mouse pointer stay out of the way of editing."
  :prefix "mouse-avoidance-"
  :group 'mouse)


;;;###autoload
(defcustom mouse-avoidance-mode nil
  "Value is t or a symbol if the mouse pointer should avoid the cursor.
See function `mouse-avoidance-mode' for possible values. Normally, you
shouldn't modify this variable by hand, but use the function instead. However,
the default value can be customized from the options menu."
  :type '(radio
	  (const :tag "No mouse avoidance" nil)
	  (const :tag "Move the mouse on keypress" banish)
	  (const :tag "Move the mouse if the cursor gets too close" exile)
	  (const :tag "Displace the mouse if the cursor gets too close" jump)
	  (const :tag "Animate the mouse" animate)
	  (const :tag "Animate + change shape" proteus))
  :set (lambda (symbol value)
	 (mouse-avoidance-mode (or value 'none)))
  :initialize 'custom-initialize-default
  :require 'avoid
  :group 'avoid)

(defcustom mouse-avoidance-nudge-dist 15
  "*Average distance that mouse will be moved when approached by cursor.
Only applies in mouse-avoidance-mode `jump' and its derivatives.
For best results make this larger than `mouse-avoidance-threshold'."
  :type 'integer
  :group 'avoid)

(defcustom mouse-avoidance-nudge-var 10
  "*Variability of `mouse-avoidance-nudge-dist' (which see)."
  :type 'integer
  :group 'avoid)

(defcustom mouse-avoidance-animation-delay .01
  "Delay between animation steps, in seconds."
  :type 'number
  :group 'avoid)

(defcustom mouse-avoidance-threshold 5
  "*Mouse-pointer's flight distance.
If the cursor gets closer than this, the mouse pointer will move away.
Only applies in mouse-avoidance-modes `animate' and `jump'."
  :type 'integer
  :group 'avoid)

;;;###autoload
(defcustom mouse-avoidance-mode-line-string " Avoid"
  "*String to display in the modeline when `mouse-avoidance-mode' is active.
Set this to nil if you don't want a modeline indicator."
  :type '(choice string
		 (const :tag "none" nil))
  :group 'avoid)

;; Internal variables
(defvar mouse-avoidance-state nil)
(defvar mouse-avoidance-pointer-shapes nil)
(defvar mouse-avoidance-n-pointer-shapes 0)
(defvar mouse-avoidance-old-pointer-shape nil)

;;; Functions:

(defsubst mouse-avoidance-set-pointer-shape (shape)
  "Set the shape of the mouse pointer to SHAPE."
  (setq x-pointer-shape shape)
  (set-mouse-color nil))

;; XEmacs change -- this is so ugly. [FSF version is totally different -sb]
(defun mouse-avoidance-point-position ()
  "Returns (WINDOW X . Y) of current point - analogous to mouse-position"
  (let* ((beg (window-start))
         (pos (point))
         (col (current-column))
         (row))
    (setq row (count-lines beg pos))
    (cons (selected-window) (cons col row))))

;(defun mouse-avoidance-point-position-test ()
;  (interactive)
;  (message "point=%s mouse=%s" 
;	   (cdr (mouse-avoidance-point-position))
;	   (cdr (mouse-position))))

(defun mouse-avoidance-set-mouse-position (pos)
  ;; Carefully set mouse position to given position (X . Y)
  ;; Ideally, should check if X,Y is in the current frame, and if not,
  ;; leave the mouse where it was.  However, this is currently
  ;; difficult to do, so we just raise the frame to avoid frame switches.
  ;; Returns t if it moved the mouse.
  (let ((f (selected-frame)))
    (raise-frame f)
    ;; XEmacs: FSF version of set-mouse-position requires FRAME parameter
    (set-mouse-position (frame-selected-window f) (car pos) (cdr pos))
    t))
      
(defun mouse-avoidance-too-close-p (mouse)
  ;;  Return t if mouse pointer and point cursor are too close.
  ;; Acceptable distance is defined by mouse-avoidance-threshold.
  (let ((point (mouse-avoidance-point-position)))
    (and (eq (car mouse) (car point))
	 (car (cdr mouse))
	 (< (abs (- (car (cdr mouse)) (car (cdr point))))
	    mouse-avoidance-threshold)
	 (< (abs (- (cdr (cdr mouse)) (cdr (cdr point))))
	    mouse-avoidance-threshold))))

(defun mouse-avoidance-banish-destination ()
  "The position to which mouse-avoidance-mode `banish' moves the mouse.
You can redefine this if you want the mouse banished to a different corner."
 (cons (1- (frame-width))
       0))

(defun mouse-avoidance-banish-mouse ()
  ;; Put the mouse pointer in the upper-right corner of the current frame.
  (mouse-avoidance-set-mouse-position (mouse-avoidance-banish-destination)))

(defsubst mouse-avoidance-delta (cur delta dist var min max)
  ;; Decide how far to move in either dimension.
  ;; Args are the CURRENT location, the desired DELTA for
  ;; warp-conservation, the DISTANCE we like to move, the VARIABILITY
  ;; in distance allowed, and the MIN and MAX possible window positions.
  ;; Returns something as close to DELTA as possible within the constraints.
  (let ((L1 (max (- min cur) (+ (- dist) (- var))))
	(R1                  (+ (- dist)    var ))
	(L2                  (+    dist  (- var)))
	(R2 (min (- max cur) (+    dist     var))))
    (if (< R1 (- min cur)) (setq L1 nil R1 nil))
    (if (> L2 (- max cur)) (setq L2 nil R2 nil))
    (cond ((and L1 (< delta L1)) L1)
	  ((and R1 (< delta R1)) delta)
	  ((and R1 (< delta 0)) R1)
	  ((and L2 (< delta L2)) L2)
	  ((and R2 (< delta R2)) delta)
	  (R2)
	  ((or R1 L2))
	  (t 0))))

(defun mouse-avoidance-nudge-mouse () 
  ;; Push the mouse a little way away, possibly animating the move
  ;; For these modes, state keeps track of the total offset that we've
  ;; accumulated, and tries to keep it close to zero.
  (let* ((cur (mouse-position))
	 (cur-frame (car cur))
	 (cur-pos (cdr cur))
	 (deltax (mouse-avoidance-delta 
		  (car cur-pos) (- (random mouse-avoidance-nudge-var)
				   (car mouse-avoidance-state))
		  mouse-avoidance-nudge-dist mouse-avoidance-nudge-var
		  0 (frame-width)))
	 (deltay (mouse-avoidance-delta 
		  (cdr cur-pos) (- (random mouse-avoidance-nudge-var)
				   (cdr mouse-avoidance-state))
		  mouse-avoidance-nudge-dist mouse-avoidance-nudge-var
		  0 (frame-height))))
    (setq mouse-avoidance-state
	  (cons (+ (car mouse-avoidance-state) deltax)
		(+ (cdr mouse-avoidance-state) deltay)))
    (if (or (eq mouse-avoidance-mode 'animate) 
	    (eq mouse-avoidance-mode 'proteus))
	(let ((i 0.0)
	      ;; XEmacs change
	      (color (cdr (assoc 'mouse-color (frame-parameters)))))
	  (while (<= i 1)
	    (mouse-avoidance-set-mouse-position 
	     (cons (+ (car cur-pos) (round (* i deltax)))
		   (+ (cdr cur-pos) (round (* i deltay)))))
    	    (setq i (+ i (max .1 (/ 1.0 mouse-avoidance-nudge-dist))))
	    (if (eq mouse-avoidance-mode 'proteus)
		;; XEmacs change
		(progn
		  (setq x-pointer-shape (mouse-avoidance-random-shape))
		  (set-mouse-color color)))
	    (sit-for mouse-avoidance-animation-delay)))
      (mouse-avoidance-set-mouse-position (cons (+ (car (cdr cur)) deltax)
						(+ (cdr (cdr cur)) deltay))))))

(defun mouse-avoidance-random-shape ()
  "Return a random cursor shape.
This assumes that any variable whose name begins with x-pointer- and
has an integer value is a valid cursor shape.  You might want to
redefine this function to suit your own tastes."
  (if (null mouse-avoidance-pointer-shapes)
      (progn
	(setq mouse-avoidance-pointer-shapes
	      (mapcar '(lambda (x) (symbol-value (intern x)))
		      (all-completions "x-pointer-" obarray
				       '(lambda (x) 
					  (and (boundp x)
					       (integerp (symbol-value x)))))))
	(setq mouse-avoidance-n-pointer-shapes 
	      (length mouse-avoidance-pointer-shapes))))
  (nth (random mouse-avoidance-n-pointer-shapes)
       mouse-avoidance-pointer-shapes))

(defun mouse-avoidance-banish-hook ()
  (if (and (not executing-kbd-macro)	; don't check inside macro
	   (mouse-avoidance-kbd-command (this-command-keys)))
      (mouse-avoidance-banish-mouse)))

(defun mouse-avoidance-exile-hook ()
  ;; For exile mode, the state is nil when the mouse is in its normal
  ;; position, and set to the old mouse-position when the mouse is in exile.
  (if (and (not executing-kbd-macro)
	   (mouse-avoidance-kbd-command (this-command-keys)))
      (let ((mp (mouse-position)))
	(cond ((and (not mouse-avoidance-state)
		    (mouse-avoidance-too-close-p mp))
	       (setq mouse-avoidance-state mp)
	       (mouse-avoidance-banish-mouse))
	      ((and mouse-avoidance-state
		    (not (mouse-avoidance-too-close-p mouse-avoidance-state)))
	       ;; XEmacs change
	       (if (and (eq (car mp) (if (and (< emacs-major-version 20)
					      (< emacs-minor-version 12))
					 (selected-frame)
				       (selected-window)))
			(equal (cdr mp) (mouse-avoidance-banish-destination)))
		   (mouse-avoidance-set-mouse-position
		    ;; move back only if user has not moved mouse
		    (cdr mouse-avoidance-state)))
	       ;; but clear state anyway, to be ready for another move
	       (setq mouse-avoidance-state nil))))))

(defun mouse-avoidance-fancy-hook ()
  ;; Used for the "fancy" modes, ie jump et al.
  (if (and (not executing-kbd-macro)	; don't check inside macro
	   (mouse-avoidance-kbd-command (this-command-keys))
	   (mouse-avoidance-too-close-p (mouse-position)))
      (let ((old-pos (mouse-position)))
	(mouse-avoidance-nudge-mouse)
	;; XEmacs change
	(if (not (eq (if (and (< emacs-major-version 20)
			      (< emacs-minor-version 12))
			 (selected-frame)
		       (selected-window)) (car old-pos))) ; move went awry
	    (set-mouse-position (car old-pos) ; sigh..
				(car (cdr old-pos))
				(cdr (cdr old-pos)))))))

(defun mouse-avoidance-kbd-command (key)
  "Return t if the KEYSEQENCE is composed of keyboard events only.
Return nil if there are any lists in the key sequence."
  (cond ((null key) nil)       		; Null event seems to be
					; returned occasionally.
	((not (vectorp key)) t)		; Strings are keyboard events.
	((catch 'done
	   (let ((i 0)
		 (l (length key)))
	     (while (< i l)
	       ;; XEmacs change: (Emacs version was: (listp (aref key i)))
	       (if (not (key-press-event-p (aref key i)))
		   (throw 'done nil))
	       (setq i (1+ i))))
	   t))))

;;;###autoload
(defun mouse-avoidance-mode (&optional mode)
  "Set cursor avoidance mode to MODE.
MODE should be one of the symbols `banish', `exile', `jump', `animate',
`cat-and-mouse', `proteus', or `none'.

If MODE is nil, toggle mouse avoidance between `none` and `banish'
modes.  Positive numbers and symbols other than the above are treated
as equivalent to `banish'; negative numbers and `-' are equivalent to `none'.

Effects of the different modes: 
 * banish: Move the mouse to the upper-right corner on any keypress.
 * exile: Move the mouse to the corner only if the cursor gets too close,
     and allow it to return once the cursor is out of the way.
 * jump: If the cursor gets too close to the mouse, displace the mouse
     a random distance & direction.
 * animate: As `jump', but shows steps along the way for illusion of motion.
 * cat-and-mouse: Same as `animate'.
 * proteus: As `animate', but changes the shape of the mouse pointer too.

Whenever the mouse is moved, the frame is also raised.

\(see `mouse-avoidance-threshold' for definition of \"too close\",
and `mouse-avoidance-nudge-dist' and `mouse-avoidance-nudge-var' for
definition of \"random distance\".)"
  (interactive
   (list (intern (completing-read
		  "Select cursor avoidance technique (SPACE for list): "
		  '(("banish") ("exile") ("jump") ("animate")
		    ("cat-and-mouse") ("proteus") ("none"))
		  nil t))))
  (if (eq mode 'cat-and-mouse)
      (setq mode 'animate))
  ;; XEmacs change - We don't have post-command-idle-hook
  (remove-hook 'post-command-hook 'mouse-avoidance-banish-hook)
  (remove-hook 'post-command-hook 'mouse-avoidance-exile-hook)
  (remove-hook 'post-command-hook 'mouse-avoidance-fancy-hook)
  ;; Restore pointer shape if necessary
  (if (eq mouse-avoidance-mode 'proteus)
      (mouse-avoidance-set-pointer-shape mouse-avoidance-old-pointer-shape))

  ;; Do additional setup depending on version of mode requested
  (cond	((eq mode 'none)
	 (setq mouse-avoidance-mode nil))
	((or (eq mode 'jump)
	     (eq mode 'animate)
	     (eq mode 'proteus))
	 ;; XEmacs: we don't have post-command-idle-hook
	 (add-hook 'post-command-hook 'mouse-avoidance-fancy-hook)
	 (setq mouse-avoidance-mode mode
	       mouse-avoidance-state (cons 0 0)
	       mouse-avoidance-old-pointer-shape x-pointer-shape))
	((eq mode 'exile)
	 ;; XEmacs: FSF uses post-command-idle-hook
	 (add-hook 'post-command-hook 'mouse-avoidance-exile-hook)
	 (setq mouse-avoidance-mode mode
	       mouse-avoidance-state nil))
	((or (eq mode 'banish) 
	     (eq mode t)
	     (and (null mode) (null mouse-avoidance-mode))
	     (and mode (> (prefix-numeric-value mode) 0)))
	 ;; XEmacs: FSF uses post-command-idle-hook
	 (add-hook 'post-command-hook 'mouse-avoidance-banish-hook)
	 (setq mouse-avoidance-mode 'banish))
	(t (setq mouse-avoidance-mode nil)))
  (force-mode-line-update))

;(or (assq 'mouse-avoidance-mode minor-mode-alist)
;    (setq minor-mode-alist (cons '(mouse-avoidance-mode " Avoid")
;				 minor-mode-alist)))
;;XEmacs: do it right.
;;;###autoload
(add-minor-mode 'mouse-avoidance-mode 'mouse-avoidance-mode-line-string)

;;; avoid.el ends here
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.