6

When I have Emacs (24.4, Mac OS, NS build) in the foreground, I often have many windows open in one frame. I sometimes want to switch windows by clicking in an inactive window. By default, this calls mouse-set-point which (1) activates the window I clicked in, and (2) repositions the point to wherever I clicked. I want to change this behavior so clicking in an inactive window would only activate it, but not move the cursor. If I'm in an active window, I want it to reposition the cursor to wherever I clicked.

Is there any reasonable way built into Emacs to do this?

Edit 1: I just tried this:

(defadvice mouse-set-point (around cv/mouse-set-point (event) activate)
  (let ((event-target-window (caadr event)))
    (if (eql event-target-window (frame-selected-window))
        ad-do-it
      (set-frame-selected-window nil event-target-window))))

That doesn't work because mouse-set-point seems to fire in the new window to position itself.

Edit 2: Updated attempt:

(defadvice mouse-set-point (around cv/mouse-set-point (event) activate)
  (let ((event-name (car event))
        (event-target-window (caadr event)))
    (if (and (eql 'down-mouse-1 event-name)
             (eql event-target-window (frame-selected-window)))
        ad-do-it
      (set-frame-selected-window nil event-target-window))))

This does not work in a weird way. When I click above the cursor position in the inactive window, it does what I want. When I click below the cursor position the inactive window, it moves the cursor. Two events fire consistently: down-mouse-1 and then mouse-1. Both call mouse-set-point.

Edit 3: Replacing mouse-set-point does work, provided I also unset [down-mouse-1] (normally bound to mouse-drag-region):

(defun mouse-set-point-2 (event)
  (interactive "e")
  (mouse-minibuffer-check event)
  (let ((event-target-window (caadr event)))
    (if (eql event-target-window (frame-selected-window))
        (posn-set-point (event-end event))
      (set-frame-selected-window nil event-target-window))))

(global-set-key [mouse-1] 'mouse-set-point-2)

(global-unset-key [down-mouse-1])

Not good enough, I don't want to lose drag-selection with the mouse.

Edit 4: Adding advice to both mouse-set-point and mouse-drag-region seems to work.

Scott Weldon
  • 2,695
  • 1
  • 17
  • 31
gcv
  • 221
  • 1
  • 7
  • 1
    I would suggest something that does both -- if current window, then set point -- if not current window, then select window only. – lawlist Jan 13 '15 at 20:32
  • I posted an answer saying that binding `[mouse-1]` to `mouse-select-window` ought to do the trick, but when I try it, that doesn't work. Perhaps because event `` invokes `mouse-drag-region` first? Besides @lawlist makes a good point. – Harald Hanche-Olsen Jan 13 '15 at 20:33
  • @Harald Hanche-Olsen -- the variable `flyspell-mouse-map` in `flyspell` has an example where `mouse-2` is `nil` and `down-mouse-2` is `flyspell-correct-word` -- perhaps a similar setup could be used. However, I imagine the final solution will be an `advice` *before* `mouse-set-point` that checks the current window and uses the function you suggested if its not the current window -- else, the default setting. – lawlist Jan 13 '15 at 20:40
  • The underlying function is only about 4 lines of code -- why not just make a new function that tests for whether the current window is the proposed selected window and then either select window (if not current window) or set point (if current window)? – lawlist Jan 13 '15 at 21:08
  • That works, as long as I unbind `mouse-drag-region` from `[down-mouse-1]`, since that seems to do its own window switching. Unfortunately, that kills mouse drag-selection. – gcv Jan 13 '15 at 21:45
  • I think I got it now. Updating question and posting answer. – gcv Jan 13 '15 at 22:02
  • I have been trying to figure out a way to prevent a mouse click from moving the cursor _when the mouse click was to give focus to the frame_. Under Windows, a mouse click is required to raise a window (frame) or give it focus. – Sue D. Nymme Apr 12 '18 at 13:48

2 Answers2

5

The following advice achieves the behavior I want:

(defadvice mouse-set-point (around cv/mouse-set-point (event) activate)
  (let ((event-name (car event))
        (event-target-window (posn-window (event-start event))))
    (if (and (eql 'down-mouse-1 event-name)
             (eql event-target-window (selected-window)))
        ad-do-it
      (select-window event-target-window))))

(defadvice mouse-drag-region (around cv/mouse-drag-region (event) activate)
  (let ((event-target-window (posn-window (event-start event))))
    (if (eql event-target-window (selected-window))
        ad-do-it
      (select-window event-target-window))))
Stefan
  • 26,154
  • 3
  • 46
  • 84
gcv
  • 221
  • 1
  • 7
  • 1
    Why use advice (dangerous) when you can define your own command and bind it? – PythonNut Jan 14 '15 at 17:53
  • Faaaaantastic! :D – Ole Jan 06 '16 at 20:25
  • 1
    Thanks very much for the solution, but was getting "Symbol's function is void" from the caadr on Emacs 24.5. Fixed by adding (require 'cl) just before the defadvice stuff. – cel Dec 07 '16 at 14:04
0

Here is another solution that works well for me:

(defadvice mouse-set-point (after wlh/mouse-set-point-advice (event &optional promote-to-region) activate)
  "Advice mouse click to remove multi cursors when present."
  (when (> (mc/num-cursors) 1)
    (mc/remove-fake-cursors)))