1 /* NeXT/Open/GNUstep / MacOSX Cocoa selection processing for emacs.
2 Copyright (C) 1993, 1994, 2005, 2006, 2008
3 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
21 Originally by Carl Edman
22 Updated by Christian Limpach (chris@nice.ch)
23 OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com)
24 MacOSX/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
25 GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
31 #include "termhooks.h"
33 #define CUT_BUFFER_SUPPORT
35 Lisp_Object QPRIMARY, QSECONDARY, QTEXT, QFILE_NAME;
37 static Lisp_Object Vns_sent_selection_hooks;
38 static Lisp_Object Vns_lost_selection_hooks;
39 static Lisp_Object Vselection_alist;
40 static Lisp_Object Vselection_converter_alist;
43 static Lisp_Object Qforeign_selection;
45 NSString *NXSecondaryPboard;
49 /* ==========================================================================
51 Internal utility functions
53 ========================================================================== */
57 symbol_to_nsstring (Lisp_Object sym)
60 if (EQ (sym, QPRIMARY)) return NSGeneralPboard;
61 if (EQ (sym, QSECONDARY)) return NXSecondaryPboard;
62 if (EQ (sym, QTEXT)) return NSStringPboardType;
63 return [NSString stringWithUTF8String: XSTRING (XSYMBOL (sym)->xname)->data];
68 ns_string_to_symbol (NSString *t)
70 if ([t isEqualToString: NSGeneralPboard])
72 if ([t isEqualToString: NXSecondaryPboard])
74 if ([t isEqualToString: NSStringPboardType])
76 if ([t isEqualToString: NSFilenamesPboardType])
78 if ([t isEqualToString: NSTabularTextPboardType])
80 return intern ([t UTF8String]);
85 clean_local_selection_data (Lisp_Object obj)
88 && INTEGERP (XCAR (obj))
90 && INTEGERP (XCAR (XCDR (obj)))
91 && NILP (XCDR (XCDR (obj))))
92 obj = Fcons (XCAR (obj), XCDR (obj));
95 && INTEGERP (XCAR (obj))
96 && INTEGERP (XCDR (obj)))
98 if (XINT (XCAR (obj)) == 0)
100 if (XINT (XCAR (obj)) == -1)
101 return make_number (- XINT (XCDR (obj)));
107 int size = ASIZE (obj);
111 return clean_local_selection_data (AREF (obj, 0));
112 copy = Fmake_vector (make_number (size), Qnil);
113 for (i = 0; i < size; i++)
114 AREF (copy, i) = clean_local_selection_data (AREF (obj, i));
123 ns_declare_pasteboard (id pb)
125 [pb declareTypes: ns_send_types owner: NSApp];
130 ns_undeclare_pasteboard (id pb)
132 [pb declareTypes: [NSArray array] owner: nil];
137 ns_string_to_pasteboard_internal (id pb, Lisp_Object str, NSString *gtype)
141 [pb declareTypes: [NSArray array] owner: nil];
146 NSString *type, *nsStr;
151 utfStr = XSTRING (str)->data;
152 nsStr = [NSString stringWithUTF8String: utfStr];
156 [pb declareTypes: ns_send_types owner: nil];
157 tenum = [ns_send_types objectEnumerator];
158 while ( (type = [tenum nextObject]) )
159 [pb setString: nsStr forType: type];
163 [pb setString: nsStr forType: gtype];
170 ns_get_local_selection (Lisp_Object selection_name,
171 Lisp_Object target_type)
173 Lisp_Object local_value;
174 Lisp_Object handler_fn, value, type, check;
177 local_value = assq_no_quit (selection_name, Vselection_alist);
179 if (NILP (local_value)) return Qnil;
181 count = specpdl_ptr - specpdl;
182 specbind (Qinhibit_quit, Qt);
183 CHECK_SYMBOL (target_type);
184 handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist));
185 if (!NILP (handler_fn))
186 value = call3 (handler_fn, selection_name, target_type,
187 XCAR (XCDR (local_value)));
190 unbind_to (count, Qnil);
193 if (CONSP (value) && SYMBOLP (XCAR (value)))
196 check = XCDR (value);
199 if (STRINGP (check) || VECTORP (check) || SYMBOLP (check)
200 || INTEGERP (check) || NILP (value))
204 && INTEGERP (XCAR (check))
205 && (INTEGERP (XCDR (check))||
206 (CONSP (XCDR (check))
207 && INTEGERP (XCAR (XCDR (check)))
208 && NILP (XCDR (XCDR (check))))))
211 // FIXME: Why `quit' rather than `error'?
212 Fsignal (Qquit, Fcons (build_string (
213 "invalid data returned by selection-conversion function"),
214 Fcons (handler_fn, Fcons (value, Qnil))));
215 // FIXME: Beware, `quit' can return!!
221 ns_get_foreign_selection (Lisp_Object symbol, Lisp_Object target)
224 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (symbol)];
225 return ns_string_from_pasteboard (pb);
230 ns_handle_selection_request (struct input_event *event)
232 // FIXME: BIG UGLY HACK!!!
233 id pb = (id)*(EMACS_INT*)&(event->x);
234 NSString *type = (NSString *)*(EMACS_INT*)&(event->y);
235 Lisp_Object selection_name, selection_data, target_symbol, data;
236 Lisp_Object successful_p, rest;
238 selection_name = ns_string_to_symbol ([(NSPasteboard *)pb name]);
239 target_symbol = ns_string_to_symbol (type);
240 selection_data = assq_no_quit (selection_name, Vselection_alist);
243 if (!NILP (selection_data))
245 data = ns_get_local_selection (selection_name, target_symbol);
249 ns_string_to_pasteboard_internal (pb, data, type);
254 if (!EQ (Vns_sent_selection_hooks, Qunbound))
256 for (rest = Vns_sent_selection_hooks; CONSP (rest); rest = Fcdr (rest))
257 call3 (Fcar (rest), selection_name, target_symbol, successful_p);
263 ns_handle_selection_clear (struct input_event *event)
265 id pb = (id)*(EMACS_INT*)&(event->x);
266 Lisp_Object selection_name, selection_data, rest;
268 selection_name = ns_string_to_symbol ([(NSPasteboard *)pb name]);
269 selection_data = assq_no_quit (selection_name, Vselection_alist);
270 if (NILP (selection_data)) return;
272 if (EQ (selection_data, Fcar (Vselection_alist)))
273 Vselection_alist = Fcdr (Vselection_alist);
276 for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest))
277 if (EQ (selection_data, Fcar (Fcdr (rest))))
278 Fsetcdr (rest, Fcdr (Fcdr (rest)));
281 if (!EQ (Vns_lost_selection_hooks, Qunbound))
283 for (rest = Vns_lost_selection_hooks;CONSP (rest); rest = Fcdr (rest))
284 call1 (Fcar (rest), selection_name);
290 /* ==========================================================================
292 Functions used externally
294 ========================================================================== */
298 ns_string_from_pasteboard (id pb)
300 NSString *type, *str;
303 type = [pb availableTypeFromArray: ns_return_types];
307 Fcons (build_string ("empty or unsupported pasteboard type"),
313 if (! (str = [pb stringForType: type]))
315 NSData *data = [pb dataForType: type];
317 str = [[NSString alloc] initWithData: data
318 encoding: NSUTF8StringEncoding];
326 Fcons (build_string ("pasteboard doesn't contain valid data"),
335 /* EOL conversion: PENDING- is this too simple? */
336 NSMutableString *mstr = [[str mutableCopy] autorelease];
337 [mstr replaceOccurrencesOfString: @"\r\n" withString: @"\n"
338 options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
339 [mstr replaceOccurrencesOfString: @"\r" withString: @"\n"
340 options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
342 utfStr = [mstr UTF8String];
344 utfStr = [mstr cString];
348 message1 ("ns_string_from_pasteboard: UTF8String failed\n");
349 utfStr = [str lossyCString];
353 return build_string (utfStr);
358 ns_string_to_pasteboard (id pb, Lisp_Object str)
360 ns_string_to_pasteboard_internal (pb, str, nil);
365 /* ==========================================================================
369 ========================================================================== */
372 DEFUN ("ns-own-selection-internal", Fns_own_selection_internal,
373 Sns_own_selection_internal, 2, 2, 0,
374 doc: /* Assert a selection.
375 SELECTION-NAME is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
376 VALUE is typically a string, or a cons of two markers, but may be
377 anything that the functions on `selection-converter-alist' know about. */)
378 (selection_name, selection_value)
379 Lisp_Object selection_name, selection_value;
382 Lisp_Object old_value, new_value;
385 CHECK_SYMBOL (selection_name);
386 if (NILP (selection_value))
387 error ("selection-value may not be nil.");
388 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection_name)];
389 ns_declare_pasteboard (pb);
390 old_value = assq_no_quit (selection_name, Vselection_alist);
391 new_value = Fcons (selection_name, Fcons (selection_value, Qnil));
392 if (NILP (old_value))
393 Vselection_alist = Fcons (new_value, Vselection_alist);
395 Fsetcdr (old_value, Fcdr (new_value));
396 /* XXX An evil hack, but a necessary one I fear XXX */
398 struct input_event ev;
399 ev.kind = SELECTION_REQUEST_EVENT;
402 *(EMACS_INT*)(&(ev.x)) = (EMACS_INT)pb; // FIXME: BIG UGLY HACK!!
403 *(EMACS_INT*)(&(ev.y)) = (EMACS_INT)NSStringPboardType;
404 ns_handle_selection_request (&ev);
406 return selection_value;
410 DEFUN ("x-disown-selection-internal", Fx_disown_selection_internal,
411 Sx_disown_selection_internal, 1, 2, 0,
412 doc: /* If we own the selection SELECTION, disown it. */)
413 (selection_name, time)
414 Lisp_Object selection_name, time;
418 CHECK_SYMBOL (selection_name);
419 if (NILP (assq_no_quit (selection_name, Vselection_alist))) return Qnil;
421 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection_name)];
422 ns_undeclare_pasteboard (pb);
427 DEFUN ("ns-selection-exists-p", Fns_selection_exists_p, Sns_selection_exists_p,
428 0, 1, 0, doc: /* Whether there is an owner for the given selection.
429 The arg should be the name of the selection in question, typically one of
430 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
431 \(Those are literal upper-case symbol names.)
432 For convenience, the symbol nil is the same as `PRIMARY',
433 and t is the same as `SECONDARY'.) */)
435 Lisp_Object selection;
441 CHECK_SYMBOL (selection);
442 if (EQ (selection, Qnil)) selection = QPRIMARY;
443 if (EQ (selection, Qt)) selection = QSECONDARY;
444 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection)];
446 return ([types count] == 0) ? Qnil : Qt;
450 DEFUN ("ns-selection-owner-p", Fns_selection_owner_p, Sns_selection_owner_p,
452 doc: /* Whether the current Emacs process owns the given selection.
453 The arg should be the name of the selection in question, typically one of
454 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
455 \(Those are literal upper-case symbol names.)
456 For convenience, the symbol nil is the same as `PRIMARY',
457 and t is the same as `SECONDARY'.) */)
459 Lisp_Object selection;
462 CHECK_SYMBOL (selection);
463 if (EQ (selection, Qnil)) selection = QPRIMARY;
464 if (EQ (selection, Qt)) selection = QSECONDARY;
465 return (NILP (Fassq (selection, Vselection_alist))) ? Qnil : Qt;
469 DEFUN ("x-get-selection-internal", Fx_get_selection_internal,
470 Sx_get_selection_internal, 2, 2, 0,
471 doc: /* Return text selected from some pasteboard.
472 SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
473 \(Those are literal upper-case symbol names.)
474 TYPE is the type of data desired, typically `STRING'. */)
475 (selection_name, target_type)
476 Lisp_Object selection_name, target_type;
481 CHECK_SYMBOL (selection_name);
482 CHECK_SYMBOL (target_type);
483 val = ns_get_local_selection (selection_name, target_type);
485 val = ns_get_foreign_selection (selection_name, target_type);
486 if (CONSP (val) && SYMBOLP (Fcar (val)))
489 if (CONSP (val) && NILP (Fcdr (val)))
492 val = clean_local_selection_data (val);
497 #ifdef CUT_BUFFER_SUPPORT
498 DEFUN ("ns-get-cut-buffer-internal", Fns_get_cut_buffer_internal,
499 Sns_get_cut_buffer_internal, 1, 1, 0,
500 doc: /* Returns the value of the named cut buffer. */)
506 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (buffer)];
507 return ns_string_from_pasteboard (pb);
511 DEFUN ("ns-rotate-cut-buffers-internal", Fns_rotate_cut_buffers_internal,
512 Sns_rotate_cut_buffers_internal, 1, 1, 0,
513 doc: /* Rotate the values of the cut buffers by N steps.
514 Positive N means move values forward, negative means
515 backward. CURRENTLY NOT IMPLEMENTED UNDER NEXTSTEP. */ )
519 /* XXX This function is unimplemented under NeXTstep XXX */
520 Fsignal (Qquit, Fcons (build_string (
521 "Warning: ns-rotate-cut-buffers-internal not implemented\n"), Qnil));
526 DEFUN ("ns-store-cut-buffer-internal", Fns_store_cut_buffer_internal,
527 Sns_store_cut_buffer_internal, 2, 2, 0,
528 doc: /* Sets the value of the named cut buffer (typically CUT_BUFFER0). */)
530 Lisp_Object buffer, string;
534 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (buffer)];
535 ns_string_to_pasteboard (pb, string);
542 nxatoms_of_nsselect (void)
544 NXSecondaryPboard = @"Selection";
548 syms_of_nsselect (void)
550 QPRIMARY = intern ("PRIMARY"); staticpro (&QPRIMARY);
551 QSECONDARY = intern ("SECONDARY"); staticpro (&QSECONDARY);
552 QTEXT = intern ("TEXT"); staticpro (&QTEXT);
553 QFILE_NAME = intern ("FILE_NAME"); staticpro (&QFILE_NAME);
555 defsubr (&Sx_disown_selection_internal);
556 defsubr (&Sx_get_selection_internal);
557 defsubr (&Sns_own_selection_internal);
558 defsubr (&Sns_selection_exists_p);
559 defsubr (&Sns_selection_owner_p);
560 #ifdef CUT_BUFFER_SUPPORT
561 defsubr (&Sns_get_cut_buffer_internal);
562 defsubr (&Sns_rotate_cut_buffers_internal);
563 defsubr (&Sns_store_cut_buffer_internal);
566 Vselection_alist = Qnil;
567 staticpro (&Vselection_alist);
569 DEFVAR_LISP ("ns-sent-selection-hooks", &Vns_sent_selection_hooks,
570 "A list of functions to be called when Emacs answers a selection request.\n\
571 The functions are called with four arguments:\n\
572 - the selection name (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
573 - the selection-type which Emacs was asked to convert the\n\
574 selection into before sending (for example, `STRING' or `LENGTH');\n\
575 - a flag indicating success or failure for responding to the request.\n\
576 We might have failed (and declined the request) for any number of reasons,\n\
577 including being asked for a selection that we no longer own, or being asked\n\
578 to convert into a type that we don't know about or that is inappropriate.\n\
579 This hook doesn't let you change the behavior of Emacs's selection replies,\n\
580 it merely informs you that they have happened.");
581 Vns_sent_selection_hooks = Qnil;
583 DEFVAR_LISP ("selection-converter-alist", &Vselection_converter_alist,
584 "An alist associating X Windows selection-types with functions.\n\
585 These functions are called to convert the selection, with three args:\n\
586 the name of the selection (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
587 a desired type to which the selection should be converted;\n\
588 and the local selection value (whatever was given to `x-own-selection').\n\
590 The function should return the value to send to the X server\n\
591 \(typically a string). A return value of nil\n\
592 means that the conversion could not be done.\n\
593 A return value which is the symbol `NULL'\n\
594 means that a side-effect was executed,\n\
595 and there is no meaningful selection value.");
596 Vselection_converter_alist = Qnil;
598 DEFVAR_LISP ("ns-lost-selection-hooks", &Vns_lost_selection_hooks,
599 "A list of functions to be called when Emacs loses an X selection.\n\
600 \(This happens when some other X client makes its own selection\n\
601 or when a Lisp program explicitly clears the selection.)\n\
602 The functions are called with one argument, the selection type\n\
603 \(a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD').");
604 Vns_lost_selection_hooks = Qnil;
607 Qforeign_selection = intern ("foreign-selection");
608 staticpro (&Qforeign_selection);
613 // arch-tag: 39d1dde7-06a6-49ff-95a7-0e7af12d2218