diff options
| author | Tak Kunihiro | 2017-12-17 11:19:19 +0100 |
|---|---|---|
| committer | Martin Rudalics | 2017-12-17 11:19:19 +0100 |
| commit | c62ced5b4d48e5aeef9c3b4d9c6f1b687a9aaa79 (patch) | |
| tree | 8e996b0aebe5d31950bccefd55d500f63331df61 | |
| parent | 2e9eba2013521cb55dc4613733a65d3c6408c65f (diff) | |
| download | emacs-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.el | 337 |
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. |
| 2349 | If the value is a modifier, such as `control' or `shift' or `meta', | 2349 | If the value is a modifier, such as `control' or `shift' or |
| 2350 | then if that modifier key is pressed when dropping the region, region | 2350 | `meta', then if that modifier key is pressed when dropping the |
| 2351 | text is copied instead of being cut." | 2351 | region, 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. | ||
| 2366 | If this option is nil, `mouse-drag-and-drop-region' will leave | ||
| 2367 | the text in the source buffer alone when dropping it in a | ||
| 2368 | different buffer. If this is non-nil, it will cut the text just | ||
| 2369 | as 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. | ||
| 2376 | If this option is nil, `mouse-drag-and-drop-region' does not show | ||
| 2377 | tooltips. If this is t, it shows the entire text dragged in a | ||
| 2378 | tooltip. If this is an integer (as with the default value of | ||
| 2379 | 256), it will show that many characters of the dragged text in | ||
| 2380 | a 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. | ||
| 2387 | If this is nil, `mouse-drag-and-drop-region' leaves point alone. | ||
| 2388 | Otherwise, it will move point together with the mouse cursor and, | ||
| 2389 | in 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. | ||
| 2397 | This face is used by `mouse-drag-and-drop-region' to temporarily | ||
| 2398 | highlight 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. |
| 2366 | The transportation of text is also referred as `drag and drop'. | 2405 | The 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 | |||
| 2369 | variable `mouse-drag-and-drop-region' is that modifier, the text | 2408 | variable `mouse-drag-and-drop-region' is that modifier, the text |
| 2370 | is copied instead of being cut." | 2409 | is 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. |