aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTak Kunihiro2017-12-17 11:19:19 +0100
committerMartin Rudalics2017-12-17 11:19:19 +0100
commitc62ced5b4d48e5aeef9c3b4d9c6f1b687a9aaa79 (patch)
tree8e996b0aebe5d31950bccefd55d500f63331df61
parent2e9eba2013521cb55dc4613733a65d3c6408c65f (diff)
downloademacs-c62ced5b4d48e5aeef9c3b4d9c6f1b687a9aaa79.tar.gz
emacs-c62ced5b4d48e5aeef9c3b4d9c6f1b687a9aaa79.zip
Make 'mouse-drag-and-drop-region' more robust and customizable
* lisp/mouse.el (mouse-drag-and-drop-region-cut-when-buffers-differ): New option to permit 'mouse-drag-and-drop-region' to cut text also when source and destination buffers differ. (mouse-drag-and-drop-region-show-tooltip): New option to toggle display of tooltip during mouse dragging on graphic displays. (mouse-drag-and-drop-region-show-cursor): New option to toggle moving point with mouse cursor during mouse dragging of region. (mouse-drag-and-drop-region): New face to highlight original text while dragging. (mouse-drag-and-drop-region): Make use of new options and face. Ignore errors during tracking.
-rw-r--r--lisp/mouse.el337
1 files changed, 278 insertions, 59 deletions
diff --git a/lisp/mouse.el b/lisp/mouse.el
index 17d1732e501..bbcc5c5ba01 100644
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -2345,10 +2345,10 @@ choose a font."
2345 2345
2346;; Drag and drop support. 2346;; Drag and drop support.
2347(defcustom mouse-drag-and-drop-region nil 2347(defcustom mouse-drag-and-drop-region nil
2348 "If non-nil, dragging the mouse drags the region, if that exists. 2348 "If non-nil, dragging the mouse drags the region, if it exists.
2349If the value is a modifier, such as `control' or `shift' or `meta', 2349If the value is a modifier, such as `control' or `shift' or
2350then if that modifier key is pressed when dropping the region, region 2350`meta', then if that modifier key is pressed when dropping the
2351text is copied instead of being cut." 2351region, text is copied instead of being cut."
2352 :type `(choice 2352 :type `(choice
2353 (const :tag "Disable dragging the region" nil) 2353 (const :tag "Disable dragging the region" nil)
2354 ,@(mapcar 2354 ,@(mapcar
@@ -2361,6 +2361,45 @@ text is copied instead of being cut."
2361 :version "26.1" 2361 :version "26.1"
2362 :group 'mouse) 2362 :group 'mouse)
2363 2363
2364(defcustom mouse-drag-and-drop-region-cut-when-buffers-differ nil
2365 "If non-nil, cut text also when source and destination buffers differ.
2366If this option is nil, `mouse-drag-and-drop-region' will leave
2367the text in the source buffer alone when dropping it in a
2368different buffer. If this is non-nil, it will cut the text just
2369as it does when dropping text in the source buffer."
2370 :type 'boolean
2371 :version "26.1"
2372 :group 'mouse)
2373
2374(defcustom mouse-drag-and-drop-region-show-tooltip 256
2375 "If non-nil, text is shown by a tooltip in a graphic display.
2376If this option is nil, `mouse-drag-and-drop-region' does not show
2377tooltips. If this is t, it shows the entire text dragged in a
2378tooltip. If this is an integer (as with the default value of
2379256), it will show that many characters of the dragged text in
2380a tooltip."
2381 :type 'integer
2382 :version "26.1"
2383 :group 'mouse)
2384
2385(defcustom mouse-drag-and-drop-region-show-cursor t
2386 "If non-nil, move point with mouse cursor during dragging.
2387If this is nil, `mouse-drag-and-drop-region' leaves point alone.
2388Otherwise, it will move point together with the mouse cursor and,
2389in addition, temporarily highlight the original region with the
2390`mouse-drag-and-drop-region' face."
2391 :type 'boolean
2392 :version "26.1"
2393 :group 'mouse)
2394
2395(defface mouse-drag-and-drop-region '((t :inherit region))
2396 "Face to highlight original text during dragging.
2397This face is used by `mouse-drag-and-drop-region' to temporarily
2398highlight the original region when
2399`mouse-drag-and-drop-region-show-cursor' is non-nil."
2400 :version "26.1"
2401 :group 'mouse)
2402
2364(defun mouse-drag-and-drop-region (event) 2403(defun mouse-drag-and-drop-region (event)
2365 "Move text in the region to point where mouse is dragged to. 2404 "Move text in the region to point where mouse is dragged to.
2366The transportation of text is also referred as `drag and drop'. 2405The transportation of text is also referred as `drag and drop'.
@@ -2369,66 +2408,246 @@ modifier key was pressed when dropping, and the value of the
2369variable `mouse-drag-and-drop-region' is that modifier, the text 2408variable `mouse-drag-and-drop-region' is that modifier, the text
2370is copied instead of being cut." 2409is copied instead of being cut."
2371 (interactive "e") 2410 (interactive "e")
2372 (require 'tooltip) 2411 (let* ((mouse-button (event-basic-type last-input-event))
2373 (let ((start (region-beginning)) 2412 (mouse-drag-and-drop-region-show-tooltip
2374 (end (region-end)) 2413 (when (and mouse-drag-and-drop-region-show-tooltip
2375 (point (point)) 2414 (display-multi-frame-p)
2376 (buffer (current-buffer)) 2415 (require 'tooltip))
2377 (window (selected-window)) 2416 mouse-drag-and-drop-region-show-tooltip))
2378 value-selection) 2417 (start (region-beginning))
2379 (track-mouse 2418 (end (region-end))
2380 ;; When event was click instead of drag, skip loop 2419 (point (point))
2381 (while (progn 2420 (buffer (current-buffer))
2382 (setq event (read-event)) 2421 (window (selected-window))
2383 (or (mouse-movement-p event) 2422 (text-from-read-only buffer-read-only)
2384 ;; Handle `mouse-autoselect-window'. 2423 (mouse-drag-and-drop-overlay (make-overlay start end))
2385 (eq (car-safe event) 'select-window))) 2424 point-to-paste
2386 (unless value-selection ; initialization 2425 point-to-paste-read-only
2387 (delete-overlay mouse-secondary-overlay) 2426 window-to-paste
2388 (setq value-selection (buffer-substring start end)) 2427 buffer-to-paste
2389 (move-overlay mouse-secondary-overlay start end)) ; (deactivate-mark) 2428 cursor-in-text-area
2390 (ignore-errors (deactivate-mark) ; care existing region in other window 2429 no-modifier-on-drop
2391 (mouse-set-point event) 2430 drag-but-negligible
2392 (tooltip-show value-selection))) 2431 clicked
2393 (tooltip-hide)) 2432 value-selection ; This remains nil when event was "click".
2394 ;; Do not modify buffer under mouse when "event was click", 2433 text-tooltip
2395 ;; "drag negligible", or 2434 states
2396 ;; "drag to read-only". 2435 window-exempt)
2397 (if (or (equal (mouse-posn-property (event-end event) 'face) 'region) ; "event was click" 2436
2398 (member 'secondary-selection ; "drag negligible" 2437 ;; STATES stores for each window on this frame its start and point
2399 (mapcar (lambda (xxx) (overlay-get xxx 'face)) 2438 ;; positions so we can restore them on all windows but for the one
2400 (overlays-at (posn-point (event-end event))))) 2439 ;; where the drop occurs. For inter-frame drags we'll have to do
2401 buffer-read-only) 2440 ;; this for all windows on all visible frames. In addition we save
2402 ;; Do not modify buffer under mouse. 2441 ;; also the cursor type for the window's buffer so we can restore it
2442 ;; in case we modified it.
2443 ;; https://lists.gnu.org/archive/html/emacs-devel/2017-12/msg00090.html
2444 (walk-window-tree
2445 (lambda (window)
2446 (setq states
2447 (cons
2448 (list
2449 window
2450 (copy-marker (window-start window))
2451 (copy-marker (window-point window))
2452 (with-current-buffer (window-buffer window)
2453 cursor-type))
2454 states))))
2455
2456 (ignore-errors
2457 (track-mouse
2458 ;; When event was "click" instead of "drag", skip loop.
2459 (while (progn
2460 (setq event (read-key)) ; read-event or read-key
2461 (or (mouse-movement-p event)
2462 ;; Handle `mouse-autoselect-window'.
2463 (eq (car-safe event) 'select-window)))
2464 ;; Obtain the dragged text in region. When the loop was
2465 ;; skipped, value-selection remains nil.
2466 (unless value-selection
2467 (setq value-selection (buffer-substring start end))
2468 (when mouse-drag-and-drop-region-show-tooltip
2469 (let ((text-size mouse-drag-and-drop-region-show-tooltip))
2470 (setq text-tooltip
2471 (if (and (integerp text-size)
2472 (> (length value-selection) text-size))
2473 (concat
2474 (substring value-selection 0 (/ text-size 2))
2475 "\n...\n"
2476 (substring value-selection (- (/ text-size 2)) -1))
2477 value-selection))))
2478
2479 ;; Check if selected text is read-only.
2480 (setq text-from-read-only (or text-from-read-only
2481 (get-text-property start 'read-only)
2482 (not (equal
2483 (next-single-char-property-change
2484 start 'read-only nil end)
2485 end)))))
2486 (setq window-to-paste (posn-window (event-end event)))
2487 (setq point-to-paste (posn-point (event-end event)))
2488 ;; Set nil when target buffer is minibuffer.
2489 (setq buffer-to-paste (let (buf)
2490 (when (windowp window-to-paste)
2491 (setq buf (window-buffer window-to-paste))
2492 (when (not (minibufferp buf))
2493 buf))))
2494 (setq cursor-in-text-area (and window-to-paste
2495 point-to-paste
2496 buffer-to-paste))
2497
2498 (when cursor-in-text-area
2499 ;; Check if point under mouse is read-only.
2500 (save-window-excursion
2501 (select-window window-to-paste)
2502 (setq point-to-paste-read-only
2503 (or buffer-read-only
2504 (get-text-property point-to-paste 'read-only))))
2505
2506 ;; Check if "drag but negligible". Operation "drag but
2507 ;; negligible" is defined as drag-and-drop the text to
2508 ;; the original region. When modifier is pressed, the
2509 ;; text will be inserted to inside of the original
2510 ;; region.
2511 (setq drag-but-negligible
2512 (and (eq (overlay-buffer mouse-drag-and-drop-overlay)
2513 buffer-to-paste)
2514 (< (overlay-start mouse-drag-and-drop-overlay)
2515 point-to-paste)
2516 (< point-to-paste
2517 (overlay-end mouse-drag-and-drop-overlay)))))
2518
2519 ;; Show a tooltip.
2520 (if mouse-drag-and-drop-region-show-tooltip
2521 (tooltip-show text-tooltip)
2522 (tooltip-hide))
2523
2524 ;; Show cursor and highlight the original region.
2525 (when mouse-drag-and-drop-region-show-cursor
2526 ;; Modify cursor even when point is out of frame.
2527 (setq cursor-type (cond
2528 ((not cursor-in-text-area)
2529 nil)
2530 ((or point-to-paste-read-only
2531 drag-but-negligible)
2532 'hollow)
2533 (t
2534 'bar)))
2535 (when cursor-in-text-area
2536 (overlay-put mouse-drag-and-drop-overlay
2537 'face 'mouse-drag-and-drop-region)
2538 (deactivate-mark) ; Maintain region in other window.
2539 (mouse-set-point event)))))
2540
2541 ;; Hide a tooltip.
2542 (when mouse-drag-and-drop-region-show-tooltip (tooltip-hide))
2543
2544 ;; Check if modifier was pressed on drop.
2545 (setq no-modifier-on-drop
2546 (not (member mouse-drag-and-drop-region (event-modifiers event))))
2547
2548 ;; Check if event was "click".
2549 (setq clicked (not value-selection))
2550
2551 ;; Restore status on drag to outside of text-area or non-mouse input.
2552 (when (or (not cursor-in-text-area)
2553 (not (equal (event-basic-type event) mouse-button)))
2554 (setq drag-but-negligible t
2555 no-modifier-on-drop t))
2556
2557 ;; Do not modify any buffers when event is "click",
2558 ;; "drag but negligible", or "drag to read-only".
2559 (let* ((mouse-drag-and-drop-region-cut-when-buffers-differ
2560 (if no-modifier-on-drop
2561 mouse-drag-and-drop-region-cut-when-buffers-differ
2562 (not mouse-drag-and-drop-region-cut-when-buffers-differ)))
2563 (wanna-paste-to-same-buffer (equal buffer-to-paste buffer))
2564 (wanna-cut-on-same-buffer (and wanna-paste-to-same-buffer
2565 no-modifier-on-drop))
2566 (wanna-cut-on-other-buffer
2567 (and (not wanna-paste-to-same-buffer)
2568 mouse-drag-and-drop-region-cut-when-buffers-differ))
2569 (cannot-paste (or point-to-paste-read-only
2570 (when (or wanna-cut-on-same-buffer
2571 wanna-cut-on-other-buffer)
2572 text-from-read-only))))
2573
2403 (cond 2574 (cond
2404 ;; "drag negligible" or "drag to read-only", restore region. 2575 ;; Move point within region.
2405 (value-selection 2576 (clicked
2406 (select-window window) ; In case miss drag to other window 2577 (deactivate-mark)
2578 (mouse-set-point event))
2579 ;; Undo operation. Set back the original text as region.
2580 ((or (and drag-but-negligible
2581 no-modifier-on-drop)
2582 cannot-paste)
2583 ;; Inform user either source or destination buffer cannot be modified.
2584 (when (and (not drag-but-negligible)
2585 cannot-paste)
2586 (message "Buffer is read-only"))
2587
2588 ;; Select source window back and restore region.
2589 ;; (set-window-point window point)
2590 (select-window window)
2407 (goto-char point) 2591 (goto-char point)
2408 (setq deactivate-mark nil) 2592 (setq deactivate-mark nil)
2409 (activate-mark)) 2593 (activate-mark))
2410 ;; "event was click" 2594 ;; Modify buffers.
2411 (t 2595 (t
2412 (deactivate-mark) 2596 ;; * DESTINATION BUFFER::
2413 (mouse-set-point event))) 2597 ;; Insert the text to destination buffer under mouse.
2414 ;; Modify buffer under mouse by inserting text. 2598 (select-window window-to-paste)
2415 (push-mark) 2599 (setq window-exempt window-to-paste)
2416 (insert value-selection) 2600 (goto-char point-to-paste)
2417 (when (not (equal (mark) (point))) ; on success insert 2601 (push-mark)
2418 (setq deactivate-mark nil) 2602 (insert value-selection)
2419 (activate-mark)) ; have region on destination 2603 ;; On success, set the text as region on destination buffer.
2420 ;; Take care of initial region on source. 2604 (when (not (equal (mark) (point)))
2421 (if (equal (current-buffer) buffer) ; when same buffer 2605 (setq deactivate-mark nil)
2422 (let (deactivate-mark) ; remove text 2606 (activate-mark))
2423 (unless (member mouse-drag-and-drop-region (event-modifiers event)) 2607
2424 (kill-region (overlay-start mouse-secondary-overlay) 2608 ;; * SOURCE BUFFER::
2425 (overlay-end mouse-secondary-overlay)))) 2609 ;; Set back the original text as region or delete the original
2426 (let ((window1 (selected-window))) ; when beyond buffer 2610 ;; text, on source buffer.
2427 (select-window window) 2611 (if wanna-paste-to-same-buffer
2428 (goto-char point) ; restore point on source window 2612 ;; When source buffer and destination buffer are the same,
2429 (activate-mark) ; restore region 2613 ;; remove the original text.
2430 (select-window window1)))) 2614 (when no-modifier-on-drop
2431 (delete-overlay mouse-secondary-overlay))) 2615 (let (deactivate-mark)
2616 (delete-region (overlay-start mouse-drag-and-drop-overlay)
2617 (overlay-end mouse-drag-and-drop-overlay))))
2618 ;; When source buffer and destination buffer are different,
2619 ;; keep (set back the original text as region) or remove the
2620 ;; original text.
2621 (select-window window) ; Select window with source buffer.
2622 (goto-char point) ; Move point to the original text on source buffer.
2623
2624 (if mouse-drag-and-drop-region-cut-when-buffers-differ
2625 ;; Remove the dragged text from source buffer like
2626 ;; operation `cut'.
2627 (delete-region (overlay-start mouse-drag-and-drop-overlay)
2628 (overlay-end mouse-drag-and-drop-overlay))
2629 ;; Set back the dragged text as region on source buffer
2630 ;; like operation `copy'.
2631 (activate-mark))
2632 (select-window window-to-paste))))))
2633
2634 ;; Clean up.
2635 (delete-overlay mouse-drag-and-drop-overlay)
2636
2637 ;; Restore old states but for the window where the drop
2638 ;; occurred. Restore cursor types for all windows.
2639 (dolist (state states)
2640 (let ((window (car state)))
2641 (when (and window-exempt
2642 (not (eq window window-exempt)))
2643 (set-window-start window (nth 1 state) 'noforce)
2644 (set-marker (nth 1 state) nil)
2645 ;; If window is selected, the following automatically sets
2646 ;; point for that window's buffer.
2647 (set-window-point window (nth 2 state))
2648 (set-marker (nth 2 state) nil))
2649 (with-current-buffer (window-buffer window)
2650 (setq cursor-type (nth 3 state)))))))
2432 2651
2433 2652
2434;;; Bindings for mouse commands. 2653;;; Bindings for mouse commands.