aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPo Lu2022-03-16 12:33:15 +0800
committerPo Lu2022-03-16 12:33:15 +0800
commitf62a6acd00fa5045fbc537bcaa87756416e246a4 (patch)
treeff29580e4f687a5d5ec7841486cd1d62f002f27f
parent5ff13718a53c161c3a0d3e8795544a740c10064b (diff)
downloademacs-f62a6acd00fa5045fbc537bcaa87756416e246a4.tar.gz
emacs-f62a6acd00fa5045fbc537bcaa87756416e246a4.zip
Better handle drag-and-drop from one Emacs frame to another
* doc/lispref/frames.texi (Drag and Drop): Document new parameter `return-frame' to `x-begin-drag'. * lisp/mouse.el (mouse-drag-and-drop-region): Utilize new feature. * src/xfns.c (Fx_begin_drag): New parameter `return-frame'. * src/xterm.c (x_dnd_begin_drag_and_drop): New parameter return_frame_p. (handle_one_xevent): Set new flags and return frame whenever appropriate. * src/xterm.h: Update prototypes.
-rw-r--r--doc/lispref/frames.texi8
-rw-r--r--lisp/mouse.el235
-rw-r--r--src/xfns.c11
-rw-r--r--src/xterm.c50
-rw-r--r--src/xterm.h3
5 files changed, 185 insertions, 122 deletions
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 38897d6a0b3..ea5dd4c675b 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -4042,7 +4042,7 @@ you want to alter Emacs behavior, you can customize these variables.
4042 On some window systems, Emacs also supports dragging contents from 4042 On some window systems, Emacs also supports dragging contents from
4043itself to other frames. 4043itself to other frames.
4044 4044
4045@defun x-begin-drag targets action &optional frame 4045@defun x-begin-drag targets action &optional frame return-frame
4046This function begins a drag from @var{frame}, and returns when the 4046This function begins a drag from @var{frame}, and returns when the
4047session ends, either because the drop was successful, or because the 4047session ends, either because the drop was successful, or because the
4048drop was rejected. The drop occurs when all mouse buttons are 4048drop was rejected. The drop occurs when all mouse buttons are
@@ -4061,6 +4061,12 @@ the drop target, or @code{XdndActionMove}, which means the same as
4061@code{XdndActionCopy}, but also means the caller should delete 4061@code{XdndActionCopy}, but also means the caller should delete
4062whatever was saved into that selection afterwards. 4062whatever was saved into that selection afterwards.
4063 4063
4064If @var{return-frame} is non-nil and the mouse moves over an Emacs
4065frame after first moving out of @var{frame}, then that frame will be
4066returned immediately. This is useful when you want to treat dragging
4067content from one frame to another specially, while also being able to
4068drag content to other programs.
4069
4064If the drop was rejected or no drop target was found, this function 4070If the drop was rejected or no drop target was found, this function
4065returns @code{nil}. Otherwise, it returns a symbol describing the 4071returns @code{nil}. Otherwise, it returns a symbol describing the
4066action the target chose to perform, which can differ from @var{action} 4072action the target chose to perform, which can differ from @var{action}
diff --git a/lisp/mouse.el b/lisp/mouse.el
index 3e2097e761f..b650bea1bde 100644
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -3061,123 +3061,126 @@ is copied instead of being cut."
3061 (or (mouse-movement-p event) 3061 (or (mouse-movement-p event)
3062 ;; Handle `mouse-autoselect-window'. 3062 ;; Handle `mouse-autoselect-window'.
3063 (memq (car event) '(select-window switch-frame)))) 3063 (memq (car event) '(select-window switch-frame))))
3064 ;; Obtain the dragged text in region. When the loop was 3064 (catch 'drag-again
3065 ;; skipped, value-selection remains nil. 3065 ;; Obtain the dragged text in region. When the loop was
3066 (unless value-selection 3066 ;; skipped, value-selection remains nil.
3067 (setq value-selection (funcall region-extract-function nil)) 3067 (unless value-selection
3068 (when mouse-drag-and-drop-region-show-tooltip 3068 (setq value-selection (funcall region-extract-function nil))
3069 (let ((text-size mouse-drag-and-drop-region-show-tooltip)) 3069 (when mouse-drag-and-drop-region-show-tooltip
3070 (setq text-tooltip 3070 (let ((text-size mouse-drag-and-drop-region-show-tooltip))
3071 (if (and (integerp text-size) 3071 (setq text-tooltip
3072 (> (length value-selection) text-size)) 3072 (if (and (integerp text-size)
3073 (concat 3073 (> (length value-selection) text-size))
3074 (substring value-selection 0 (/ text-size 2)) 3074 (concat
3075 "\n...\n" 3075 (substring value-selection 0 (/ text-size 2))
3076 (substring value-selection (- (/ text-size 2)) -1)) 3076 "\n...\n"
3077 value-selection)))) 3077 (substring value-selection (- (/ text-size 2)) -1))
3078 3078 value-selection))))
3079 ;; Check if selected text is read-only. 3079
3080 (setq text-from-read-only 3080 ;; Check if selected text is read-only.
3081 (or text-from-read-only 3081 (setq text-from-read-only
3082 (catch 'loop 3082 (or text-from-read-only
3083 (dolist (bound (region-bounds)) 3083 (catch 'loop
3084 (when (text-property-not-all 3084 (dolist (bound (region-bounds))
3085 (car bound) (cdr bound) 'read-only nil) 3085 (when (text-property-not-all
3086 (throw 'loop t))))))) 3086 (car bound) (cdr bound) 'read-only nil)
3087 3087 (throw 'loop t)))))))
3088 (when (and mouse-drag-and-drop-region-cross-program 3088
3089 (fboundp 'x-begin-drag) 3089 (when (and mouse-drag-and-drop-region-cross-program
3090 (framep (posn-window (event-end event))) 3090 (fboundp 'x-begin-drag)
3091 (let ((location (posn-x-y (event-end event))) 3091 (framep (posn-window (event-end event)))
3092 (frame (posn-window (event-end event)))) 3092 (let ((location (posn-x-y (event-end event)))
3093 (or (< (car location) 0) 3093 (frame (posn-window (event-end event))))
3094 (< (cdr location) 0) 3094 (or (< (car location) 0)
3095 (> (car location) 3095 (< (cdr location) 0)
3096 (frame-pixel-width frame)) 3096 (> (car location)
3097 (> (cdr location) 3097 (frame-pixel-width frame))
3098 (frame-pixel-height frame))))) 3098 (> (cdr location)
3099 (tooltip-hide) 3099 (frame-pixel-height frame)))))
3100 (gui-set-selection 'XdndSelection value-selection) 3100 (tooltip-hide)
3101 (x-begin-drag '("UTF8_STRING" "STRING") 3101 (gui-set-selection 'XdndSelection value-selection)
3102 'XdndActionMove (posn-window (event-end event))) 3102 (when (framep
3103 (throw 'cross-program-drag nil)) 3103 (x-begin-drag '("UTF8_STRING" "STRING") 'XdndActionCopy
3104 3104 (posn-window (event-end event)) t))
3105 (setq window-to-paste (posn-window (event-end event))) 3105 (throw 'drag-again nil))
3106 (setq point-to-paste (posn-point (event-end event))) 3106 (throw 'cross-program-drag nil))
3107 ;; Set nil when target buffer is minibuffer. 3107
3108 (setq buffer-to-paste (let (buf) 3108 (setq window-to-paste (posn-window (event-end event)))
3109 (when (windowp window-to-paste) 3109 (setq point-to-paste (posn-point (event-end event)))
3110 (setq buf (window-buffer window-to-paste)) 3110 ;; Set nil when target buffer is minibuffer.
3111 (when (not (minibufferp buf)) 3111 (setq buffer-to-paste (let (buf)
3112 buf)))) 3112 (when (windowp window-to-paste)
3113 (setq cursor-in-text-area (and window-to-paste 3113 (setq buf (window-buffer window-to-paste))
3114 point-to-paste 3114 (when (not (minibufferp buf))
3115 buffer-to-paste)) 3115 buf))))
3116 3116 (setq cursor-in-text-area (and window-to-paste
3117 (when cursor-in-text-area 3117 point-to-paste
3118 ;; Check if point under mouse is read-only. 3118 buffer-to-paste))
3119 (save-window-excursion 3119
3120 (select-window window-to-paste)
3121 (setq point-to-paste-read-only
3122 (or buffer-read-only
3123 (get-text-property point-to-paste 'read-only))))
3124
3125 ;; Check if "drag but negligible". Operation "drag but
3126 ;; negligible" is defined as drag-and-drop the text to
3127 ;; the original region. When modifier is pressed, the
3128 ;; text will be inserted to inside of the original
3129 ;; region.
3130 ;;
3131 ;; If the region is rectangular, check if the newly inserted
3132 ;; rectangular text would intersect the already selected
3133 ;; region. If it would, then set "drag-but-negligible" to t.
3134 ;; As a special case, allow dragging the region freely anywhere
3135 ;; to the left, as this will never trigger its contents to be
3136 ;; inserted into the overlays tracking it.
3137 (setq drag-but-negligible
3138 (and (eq (overlay-buffer (car mouse-drag-and-drop-overlays))
3139 buffer-to-paste)
3140 (if region-noncontiguous
3141 (let ((dimensions (rectangle-dimensions start end))
3142 (start-coordinates
3143 (rectangle-position-as-coordinates start))
3144 (point-to-paste-coordinates
3145 (rectangle-position-as-coordinates
3146 point-to-paste)))
3147 (and (rectangle-intersect-p
3148 start-coordinates dimensions
3149 point-to-paste-coordinates dimensions)
3150 (not (< (car point-to-paste-coordinates)
3151 (car start-coordinates)))))
3152 (and (<= (overlay-start
3153 (car mouse-drag-and-drop-overlays))
3154 point-to-paste)
3155 (<= point-to-paste
3156 (overlay-end
3157 (car mouse-drag-and-drop-overlays))))))))
3158
3159 ;; Show a tooltip.
3160 (if mouse-drag-and-drop-region-show-tooltip
3161 (tooltip-show text-tooltip)
3162 (tooltip-hide))
3163
3164 ;; Show cursor and highlight the original region.
3165 (when mouse-drag-and-drop-region-show-cursor
3166 ;; Modify cursor even when point is out of frame.
3167 (setq cursor-type (cond
3168 ((not cursor-in-text-area)
3169 nil)
3170 ((or point-to-paste-read-only
3171 drag-but-negligible)
3172 'hollow)
3173 (t
3174 'bar)))
3175 (when cursor-in-text-area 3120 (when cursor-in-text-area
3176 (dolist (overlay mouse-drag-and-drop-overlays) 3121 ;; Check if point under mouse is read-only.
3177 (overlay-put overlay 3122 (save-window-excursion
3178 'face 'mouse-drag-and-drop-region)) 3123 (select-window window-to-paste)
3179 (deactivate-mark) ; Maintain region in other window. 3124 (setq point-to-paste-read-only
3180 (mouse-set-point event)))))) 3125 (or buffer-read-only
3126 (get-text-property point-to-paste 'read-only))))
3127
3128 ;; Check if "drag but negligible". Operation "drag but
3129 ;; negligible" is defined as drag-and-drop the text to
3130 ;; the original region. When modifier is pressed, the
3131 ;; text will be inserted to inside of the original
3132 ;; region.
3133 ;;
3134 ;; If the region is rectangular, check if the newly inserted
3135 ;; rectangular text would intersect the already selected
3136 ;; region. If it would, then set "drag-but-negligible" to t.
3137 ;; As a special case, allow dragging the region freely anywhere
3138 ;; to the left, as this will never trigger its contents to be
3139 ;; inserted into the overlays tracking it.
3140 (setq drag-but-negligible
3141 (and (eq (overlay-buffer (car mouse-drag-and-drop-overlays))
3142 buffer-to-paste)
3143 (if region-noncontiguous
3144 (let ((dimensions (rectangle-dimensions start end))
3145 (start-coordinates
3146 (rectangle-position-as-coordinates start))
3147 (point-to-paste-coordinates
3148 (rectangle-position-as-coordinates
3149 point-to-paste)))
3150 (and (rectangle-intersect-p
3151 start-coordinates dimensions
3152 point-to-paste-coordinates dimensions)
3153 (not (< (car point-to-paste-coordinates)
3154 (car start-coordinates)))))
3155 (and (<= (overlay-start
3156 (car mouse-drag-and-drop-overlays))
3157 point-to-paste)
3158 (<= point-to-paste
3159 (overlay-end
3160 (car mouse-drag-and-drop-overlays))))))))
3161
3162 ;; Show a tooltip.
3163 (if mouse-drag-and-drop-region-show-tooltip
3164 (tooltip-show text-tooltip)
3165 (tooltip-hide))
3166
3167 ;; Show cursor and highlight the original region.
3168 (when mouse-drag-and-drop-region-show-cursor
3169 ;; Modify cursor even when point is out of frame.
3170 (setq cursor-type (cond
3171 ((not cursor-in-text-area)
3172 nil)
3173 ((or point-to-paste-read-only
3174 drag-but-negligible)
3175 'hollow)
3176 (t
3177 'bar)))
3178 (when cursor-in-text-area
3179 (dolist (overlay mouse-drag-and-drop-overlays)
3180 (overlay-put overlay
3181 'face 'mouse-drag-and-drop-region))
3182 (deactivate-mark) ; Maintain region in other window.
3183 (mouse-set-point event)))))))
3181 3184
3182 ;; Hide a tooltip. 3185 ;; Hide a tooltip.
3183 (when mouse-drag-and-drop-region-show-tooltip (tooltip-hide)) 3186 (when mouse-drag-and-drop-region-show-tooltip (tooltip-hide))
diff --git a/src/xfns.c b/src/xfns.c
index 0d197c1dd7d..b5d0b2c54e8 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -6582,7 +6582,7 @@ The coordinates X and Y are interpreted in pixels relative to a position
6582 return Qnil; 6582 return Qnil;
6583} 6583}
6584 6584
6585DEFUN ("x-begin-drag", Fx_begin_drag, Sx_begin_drag, 1, 3, 0, 6585DEFUN ("x-begin-drag", Fx_begin_drag, Sx_begin_drag, 1, 4, 0,
6586 doc: /* Begin dragging contents on FRAME, with targets TARGETS. 6586 doc: /* Begin dragging contents on FRAME, with targets TARGETS.
6587TARGETS is a list of strings, which defines the X selection targets 6587TARGETS is a list of strings, which defines the X selection targets
6588that will be available to the drop target. Block until the mouse 6588that will be available to the drop target. Block until the mouse
@@ -6607,9 +6607,14 @@ Emacs. For that reason, they are not mentioned here. Consult
6607"Drag-and-Drop Protocol for the X Window System" for more details: 6607"Drag-and-Drop Protocol for the X Window System" for more details:
6608https://freedesktop.org/wiki/Specifications/XDND/. 6608https://freedesktop.org/wiki/Specifications/XDND/.
6609 6609
6610If RETURN-FRAME is non-nil, this function will return the frame if the
6611mouse pointer moves onto an Emacs frame, after first moving out of
6612FRAME.
6613
6610If ACTION is not specified or nil, `XdndActionCopy' is used 6614If ACTION is not specified or nil, `XdndActionCopy' is used
6611instead. */) 6615instead. */)
6612 (Lisp_Object targets, Lisp_Object action, Lisp_Object frame) 6616 (Lisp_Object targets, Lisp_Object action, Lisp_Object frame,
6617 Lisp_Object return_frame)
6613{ 6618{
6614 struct frame *f = decode_window_system_frame (frame); 6619 struct frame *f = decode_window_system_frame (frame);
6615 int ntargets = 0; 6620 int ntargets = 0;
@@ -6655,7 +6660,7 @@ instead. */)
6655 6660
6656 x_set_dnd_targets (target_atoms, ntargets); 6661 x_set_dnd_targets (target_atoms, ntargets);
6657 lval = x_dnd_begin_drag_and_drop (f, FRAME_DISPLAY_INFO (f)->last_user_time, 6662 lval = x_dnd_begin_drag_and_drop (f, FRAME_DISPLAY_INFO (f)->last_user_time,
6658 xaction); 6663 xaction, !NILP (return_frame));
6659 6664
6660 return lval; 6665 return lval;
6661} 6666}
diff --git a/src/xterm.c b/src/xterm.c
index 8a4344f2a4f..a3d20a9d226 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -771,6 +771,15 @@ static void x_scroll_bar_end_update (struct x_display_info *, struct scroll_bar
771#endif 771#endif
772 772
773static bool x_dnd_in_progress; 773static bool x_dnd_in_progress;
774
775/* Whether or not to return a frame from `x_dnd_begin_drag_and_drop'.
776
777 0 means to do nothing. 1 means to wait for the mouse to first exit
778 `x_dnd_frame'. 2 means to wait for the mouse to move onto a frame,
779 and 3 means to `x_dnd_return_frame_object'. */
780static int x_dnd_return_frame;
781static struct frame *x_dnd_return_frame_object;
782
774static Window x_dnd_last_seen_window; 783static Window x_dnd_last_seen_window;
775static int x_dnd_last_protocol_version; 784static int x_dnd_last_protocol_version;
776static Time x_dnd_selection_timestamp; 785static Time x_dnd_selection_timestamp;
@@ -1025,7 +1034,8 @@ x_set_dnd_targets (Atom *targets, int ntargets)
1025} 1034}
1026 1035
1027Lisp_Object 1036Lisp_Object
1028x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction) 1037x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
1038 bool return_frame_p)
1029{ 1039{
1030 XEvent next_event; 1040 XEvent next_event;
1031 struct input_event hold_quit; 1041 struct input_event hold_quit;
@@ -1054,6 +1064,10 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction)
1054 x_dnd_mouse_rect_target = None; 1064 x_dnd_mouse_rect_target = None;
1055 x_dnd_action = None; 1065 x_dnd_action = None;
1056 x_dnd_wanted_action = xaction; 1066 x_dnd_wanted_action = xaction;
1067 x_dnd_return_frame = 0;
1068
1069 if (return_frame_p)
1070 x_dnd_return_frame = 1;
1057 1071
1058 while (x_dnd_in_progress) 1072 while (x_dnd_in_progress)
1059 { 1073 {
@@ -1085,6 +1099,14 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction)
1085 } 1099 }
1086 } 1100 }
1087 1101
1102 if (x_dnd_return_frame == 3)
1103 {
1104 x_dnd_return_frame_object->mouse_moved = true;
1105
1106 XSETFRAME (action, x_dnd_return_frame_object);
1107 return action;
1108 }
1109
1088 FRAME_DISPLAY_INFO (f)->grabbed = 0; 1110 FRAME_DISPLAY_INFO (f)->grabbed = 0;
1089 1111
1090 if (x_dnd_wanted_action != None) 1112 if (x_dnd_wanted_action != None)
@@ -11606,6 +11628,19 @@ handle_one_xevent (struct x_display_info *dpyinfo,
11606 && x_dnd_last_seen_window != FRAME_X_WINDOW (x_dnd_frame)) 11628 && x_dnd_last_seen_window != FRAME_X_WINDOW (x_dnd_frame))
11607 x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window); 11629 x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
11608 11630
11631 if (x_dnd_last_seen_window == FRAME_X_WINDOW (x_dnd_frame)
11632 && x_dnd_return_frame == 1)
11633 x_dnd_return_frame = 2;
11634
11635 if (x_dnd_return_frame == 2
11636 && x_window_to_frame (dpyinfo, target))
11637 {
11638 x_dnd_in_progress = false;
11639 x_dnd_return_frame_object
11640 = x_window_to_frame (dpyinfo, target);
11641 x_dnd_return_frame = 3;
11642 }
11643
11609 x_dnd_wanted_action = None; 11644 x_dnd_wanted_action = None;
11610 x_dnd_last_seen_window = target; 11645 x_dnd_last_seen_window = target;
11611 x_dnd_last_protocol_version 11646 x_dnd_last_protocol_version
@@ -12825,6 +12860,19 @@ handle_one_xevent (struct x_display_info *dpyinfo,
12825 && x_dnd_last_seen_window != FRAME_X_WINDOW (x_dnd_frame)) 12860 && x_dnd_last_seen_window != FRAME_X_WINDOW (x_dnd_frame))
12826 x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window); 12861 x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
12827 12862
12863 if (x_dnd_last_seen_window == FRAME_X_WINDOW (x_dnd_frame)
12864 && x_dnd_return_frame == 1)
12865 x_dnd_return_frame = 2;
12866
12867 if (x_dnd_return_frame == 2
12868 && x_window_to_frame (dpyinfo, target))
12869 {
12870 x_dnd_in_progress = false;
12871 x_dnd_return_frame_object
12872 = x_window_to_frame (dpyinfo, target);
12873 x_dnd_return_frame = 3;
12874 }
12875
12828 x_dnd_last_seen_window = target; 12876 x_dnd_last_seen_window = target;
12829 x_dnd_last_protocol_version 12877 x_dnd_last_protocol_version
12830 = x_dnd_get_window_proto (dpyinfo, target); 12878 = x_dnd_get_window_proto (dpyinfo, target);
diff --git a/src/xterm.h b/src/xterm.h
index 225aaf4cad5..9665e92a9fb 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -1367,7 +1367,8 @@ extern void x_scroll_bar_configure (GdkEvent *);
1367 1367
1368extern void x_display_set_last_user_time (struct x_display_info *, Time); 1368extern void x_display_set_last_user_time (struct x_display_info *, Time);
1369 1369
1370extern Lisp_Object x_dnd_begin_drag_and_drop (struct frame *, Time, Atom); 1370extern Lisp_Object x_dnd_begin_drag_and_drop (struct frame *, Time, Atom,
1371 bool);
1371extern void x_set_dnd_targets (Atom *, int); 1372extern void x_set_dnd_targets (Atom *, int);
1372 1373
1373INLINE int 1374INLINE int