1 /* NeXT/Open/GNUstep / MacOSX Cocoa selection processing for emacs.
2 Copyright (C) 1993, 1994, 2005, 2006, 2008, 2009
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)
28 /* This should be the first include, as it may set up #defines affecting
29 interpretation of even the system includes. */
34 #include "termhooks.h"
36 #define CUT_BUFFER_SUPPORT
38 Lisp_Object QPRIMARY, QSECONDARY, QTEXT, QFILE_NAME;
40 static Lisp_Object Vns_sent_selection_hooks;
41 static Lisp_Object Vns_lost_selection_hooks;
42 static Lisp_Object Vselection_alist;
43 static Lisp_Object Vselection_converter_alist;
45 static Lisp_Object Qforeign_selection;
47 NSString *NXSecondaryPboard;
51 /* ==========================================================================
53 Internal utility functions
55 ========================================================================== */
59 symbol_to_nsstring (Lisp_Object sym)
62 if (EQ (sym, QPRIMARY)) return NSGeneralPboard;
63 if (EQ (sym, QSECONDARY)) return NXSecondaryPboard;
64 if (EQ (sym, QTEXT)) return NSStringPboardType;
65 return [NSString stringWithUTF8String: SDATA (XSYMBOL (sym)->xname)];
70 ns_string_to_symbol (NSString *t)
72 if ([t isEqualToString: NSGeneralPboard])
74 if ([t isEqualToString: NXSecondaryPboard])
76 if ([t isEqualToString: NSStringPboardType])
78 if ([t isEqualToString: NSFilenamesPboardType])
80 if ([t isEqualToString: NSTabularTextPboardType])
82 return intern ([t UTF8String]);
87 clean_local_selection_data (Lisp_Object obj)
90 && INTEGERP (XCAR (obj))
92 && INTEGERP (XCAR (XCDR (obj)))
93 && NILP (XCDR (XCDR (obj))))
94 obj = Fcons (XCAR (obj), XCDR (obj));
97 && INTEGERP (XCAR (obj))
98 && INTEGERP (XCDR (obj)))
100 if (XINT (XCAR (obj)) == 0)
102 if (XINT (XCAR (obj)) == -1)
103 return make_number (- XINT (XCDR (obj)));
109 int size = ASIZE (obj);
113 return clean_local_selection_data (AREF (obj, 0));
114 copy = Fmake_vector (make_number (size), Qnil);
115 for (i = 0; i < size; i++)
116 ASET (copy, i, clean_local_selection_data (AREF (obj, i)));
125 ns_declare_pasteboard (id pb)
127 [pb declareTypes: ns_send_types owner: NSApp];
132 ns_undeclare_pasteboard (id pb)
134 [pb declareTypes: [NSArray array] owner: nil];
139 ns_string_to_pasteboard_internal (id pb, Lisp_Object str, NSString *gtype)
143 [pb declareTypes: [NSArray array] owner: nil];
148 NSString *type, *nsStr;
153 utfStr = SDATA (str);
154 nsStr = [NSString stringWithUTF8String: utfStr];
158 [pb declareTypes: ns_send_types owner: nil];
159 tenum = [ns_send_types objectEnumerator];
160 while ( (type = [tenum nextObject]) )
161 [pb setString: nsStr forType: type];
165 [pb setString: nsStr forType: gtype];
172 ns_get_local_selection (Lisp_Object selection_name,
173 Lisp_Object target_type)
175 Lisp_Object local_value;
176 Lisp_Object handler_fn, value, type, check;
179 local_value = assq_no_quit (selection_name, Vselection_alist);
181 if (NILP (local_value)) return Qnil;
183 count = specpdl_ptr - specpdl;
184 specbind (Qinhibit_quit, Qt);
185 CHECK_SYMBOL (target_type);
186 handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist));
187 if (!NILP (handler_fn))
188 value = call3 (handler_fn, selection_name, target_type,
189 XCAR (XCDR (local_value)));
192 unbind_to (count, Qnil);
195 if (CONSP (value) && SYMBOLP (XCAR (value)))
198 check = XCDR (value);
201 if (STRINGP (check) || VECTORP (check) || SYMBOLP (check)
202 || INTEGERP (check) || NILP (value))
206 && INTEGERP (XCAR (check))
207 && (INTEGERP (XCDR (check))||
208 (CONSP (XCDR (check))
209 && INTEGERP (XCAR (XCDR (check)))
210 && NILP (XCDR (XCDR (check))))))
213 // FIXME: Why `quit' rather than `error'?
214 Fsignal (Qquit, Fcons (build_string (
215 "invalid data returned by selection-conversion function"),
216 Fcons (handler_fn, Fcons (value, Qnil))));
217 // FIXME: Beware, `quit' can return!!
223 ns_get_foreign_selection (Lisp_Object symbol, Lisp_Object target)
226 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (symbol)];
227 return ns_string_from_pasteboard (pb);
232 ns_handle_selection_request (struct input_event *event)
234 // FIXME: BIG UGLY HACK!!!
235 id pb = (id)*(EMACS_INT*)&(event->x);
236 NSString *type = (NSString *)*(EMACS_INT*)&(event->y);
237 Lisp_Object selection_name, selection_data, target_symbol, data;
238 Lisp_Object successful_p, rest;
240 selection_name = ns_string_to_symbol ([(NSPasteboard *)pb name]);
241 target_symbol = ns_string_to_symbol (type);
242 selection_data = assq_no_quit (selection_name, Vselection_alist);
245 if (!NILP (selection_data))
247 data = ns_get_local_selection (selection_name, target_symbol);
251 ns_string_to_pasteboard_internal (pb, data, type);
256 if (!EQ (Vns_sent_selection_hooks, Qunbound))
258 for (rest = Vns_sent_selection_hooks; CONSP (rest); rest = Fcdr (rest))
259 call3 (Fcar (rest), selection_name, target_symbol, successful_p);
265 ns_handle_selection_clear (struct input_event *event)
267 id pb = (id)*(EMACS_INT*)&(event->x);
268 Lisp_Object selection_name, selection_data, rest;
270 selection_name = ns_string_to_symbol ([(NSPasteboard *)pb name]);
271 selection_data = assq_no_quit (selection_name, Vselection_alist);
272 if (NILP (selection_data)) return;
274 if (EQ (selection_data, Fcar (Vselection_alist)))
275 Vselection_alist = Fcdr (Vselection_alist);
278 for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest))
279 if (EQ (selection_data, Fcar (Fcdr (rest))))
280 Fsetcdr (rest, Fcdr (Fcdr (rest)));
283 if (!EQ (Vns_lost_selection_hooks, Qunbound))
285 for (rest = Vns_lost_selection_hooks;CONSP (rest); rest = Fcdr (rest))
286 call1 (Fcar (rest), selection_name);
292 /* ==========================================================================
294 Functions used externally
296 ========================================================================== */
300 ns_string_from_pasteboard (id pb)
302 NSString *type, *str;
305 type = [pb availableTypeFromArray: ns_return_types];
309 Fcons (build_string ("empty or unsupported pasteboard type"),
315 if (! (str = [pb stringForType: type]))
317 NSData *data = [pb dataForType: type];
319 str = [[NSString alloc] initWithData: data
320 encoding: NSUTF8StringEncoding];
328 Fcons (build_string ("pasteboard doesn't contain valid data"),
337 /* EOL conversion: PENDING- is this too simple? */
338 NSMutableString *mstr = [[str mutableCopy] autorelease];
339 [mstr replaceOccurrencesOfString: @"\r\n" withString: @"\n"
340 options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
341 [mstr replaceOccurrencesOfString: @"\r" withString: @"\n"
342 options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
344 utfStr = [mstr UTF8String];
346 utfStr = [mstr cString];
350 message1 ("ns_string_from_pasteboard: UTF8String failed\n");
351 utfStr = [str lossyCString];
355 return build_string (utfStr);
360 ns_string_to_pasteboard (id pb, Lisp_Object str)
362 ns_string_to_pasteboard_internal (pb, str, nil);
367 /* ==========================================================================
371 ========================================================================== */
374 DEFUN ("ns-own-selection-internal", Fns_own_selection_internal,
375 Sns_own_selection_internal, 2, 2, 0,
376 doc: /* Assert a selection.
377 SELECTION-NAME is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
378 VALUE is typically a string, or a cons of two markers, but may be
379 anything that the functions on `selection-converter-alist' know about. */)
380 (selection_name, selection_value)
381 Lisp_Object selection_name, selection_value;
384 Lisp_Object old_value, new_value;
387 CHECK_SYMBOL (selection_name);
388 if (NILP (selection_value))
389 error ("selection-value may not be nil.");
390 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection_name)];
391 ns_declare_pasteboard (pb);
392 old_value = assq_no_quit (selection_name, Vselection_alist);
393 new_value = Fcons (selection_name, Fcons (selection_value, Qnil));
394 if (NILP (old_value))
395 Vselection_alist = Fcons (new_value, Vselection_alist);
397 Fsetcdr (old_value, Fcdr (new_value));
398 /* XXX An evil hack, but a necessary one I fear XXX */
400 struct input_event ev;
401 ev.kind = SELECTION_REQUEST_EVENT;
404 *(EMACS_INT*)(&(ev.x)) = (EMACS_INT)pb; // FIXME: BIG UGLY HACK!!
405 *(EMACS_INT*)(&(ev.y)) = (EMACS_INT)NSStringPboardType;
406 ns_handle_selection_request (&ev);
408 return selection_value;
412 DEFUN ("x-disown-selection-internal", Fx_disown_selection_internal,
413 Sx_disown_selection_internal, 1, 2, 0,
414 doc: /* If we own the selection SELECTION, disown it. */)
415 (selection_name, time)
416 Lisp_Object selection_name, time;
420 CHECK_SYMBOL (selection_name);
421 if (NILP (assq_no_quit (selection_name, Vselection_alist))) return Qnil;
423 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection_name)];
424 ns_undeclare_pasteboard (pb);
429 DEFUN ("ns-selection-exists-p", Fns_selection_exists_p, Sns_selection_exists_p,
430 0, 1, 0, doc: /* Whether there is an owner for the given selection.
431 The arg should be the name of the selection in question, typically one of
432 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
433 \(Those are literal upper-case symbol names.)
434 For convenience, the symbol nil is the same as `PRIMARY',
435 and t is the same as `SECONDARY'.) */)
437 Lisp_Object selection;
443 CHECK_SYMBOL (selection);
444 if (EQ (selection, Qnil)) selection = QPRIMARY;
445 if (EQ (selection, Qt)) selection = QSECONDARY;
446 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection)];
448 return ([types count] == 0) ? Qnil : Qt;
452 DEFUN ("ns-selection-owner-p", Fns_selection_owner_p, Sns_selection_owner_p,
454 doc: /* Whether the current Emacs process owns the given selection.
455 The arg should be the name of the selection in question, typically one of
456 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
457 \(Those are literal upper-case symbol names.)
458 For convenience, the symbol nil is the same as `PRIMARY',
459 and t is the same as `SECONDARY'.) */)
461 Lisp_Object selection;
464 CHECK_SYMBOL (selection);
465 if (EQ (selection, Qnil)) selection = QPRIMARY;
466 if (EQ (selection, Qt)) selection = QSECONDARY;
467 return (NILP (Fassq (selection, Vselection_alist))) ? Qnil : Qt;
471 DEFUN ("x-get-selection-internal", Fx_get_selection_internal,
472 Sx_get_selection_internal, 2, 2, 0,
473 doc: /* Return text selected from some pasteboard.
474 SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
475 \(Those are literal upper-case symbol names.)
476 TYPE is the type of data desired, typically `STRING'. */)
477 (selection_name, target_type)
478 Lisp_Object selection_name, target_type;
483 CHECK_SYMBOL (selection_name);
484 CHECK_SYMBOL (target_type);
485 val = ns_get_local_selection (selection_name, target_type);
487 val = ns_get_foreign_selection (selection_name, target_type);
488 if (CONSP (val) && SYMBOLP (Fcar (val)))
491 if (CONSP (val) && NILP (Fcdr (val)))
494 val = clean_local_selection_data (val);
499 #ifdef CUT_BUFFER_SUPPORT
500 DEFUN ("ns-get-cut-buffer-internal", Fns_get_cut_buffer_internal,
501 Sns_get_cut_buffer_internal, 1, 1, 0,
502 doc: /* Returns the value of the named cut buffer. */)
508 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (buffer)];
509 return ns_string_from_pasteboard (pb);
513 DEFUN ("ns-rotate-cut-buffers-internal", Fns_rotate_cut_buffers_internal,
514 Sns_rotate_cut_buffers_internal, 1, 1, 0,
515 doc: /* Rotate the values of the cut buffers by N steps.
516 Positive N means move values forward, negative means
517 backward. CURRENTLY NOT IMPLEMENTED UNDER NEXTSTEP. */ )
521 /* XXX This function is unimplemented under NeXTstep XXX */
522 Fsignal (Qquit, Fcons (build_string (
523 "Warning: ns-rotate-cut-buffers-internal not implemented\n"), Qnil));
528 DEFUN ("ns-store-cut-buffer-internal", Fns_store_cut_buffer_internal,
529 Sns_store_cut_buffer_internal, 2, 2, 0,
530 doc: /* Sets the value of the named cut buffer (typically CUT_BUFFER0). */)
532 Lisp_Object buffer, string;
536 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (buffer)];
537 ns_string_to_pasteboard (pb, string);
544 nxatoms_of_nsselect (void)
546 NXSecondaryPboard = @"Selection";
550 syms_of_nsselect (void)
552 QPRIMARY = intern ("PRIMARY"); staticpro (&QPRIMARY);
553 QSECONDARY = intern ("SECONDARY"); staticpro (&QSECONDARY);
554 QTEXT = intern ("TEXT"); staticpro (&QTEXT);
555 QFILE_NAME = intern ("FILE_NAME"); staticpro (&QFILE_NAME);
557 defsubr (&Sx_disown_selection_internal);
558 defsubr (&Sx_get_selection_internal);
559 defsubr (&Sns_own_selection_internal);
560 defsubr (&Sns_selection_exists_p);
561 defsubr (&Sns_selection_owner_p);
562 #ifdef CUT_BUFFER_SUPPORT
563 defsubr (&Sns_get_cut_buffer_internal);
564 defsubr (&Sns_rotate_cut_buffers_internal);
565 defsubr (&Sns_store_cut_buffer_internal);
568 Vselection_alist = Qnil;
569 staticpro (&Vselection_alist);
571 DEFVAR_LISP ("ns-sent-selection-hooks", &Vns_sent_selection_hooks,
572 "A list of functions to be called when Emacs answers a selection request.\n\
573 The functions are called with four arguments:\n\
574 - the selection name (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
575 - the selection-type which Emacs was asked to convert the\n\
576 selection into before sending (for example, `STRING' or `LENGTH');\n\
577 - a flag indicating success or failure for responding to the request.\n\
578 We might have failed (and declined the request) for any number of reasons,\n\
579 including being asked for a selection that we no longer own, or being asked\n\
580 to convert into a type that we don't know about or that is inappropriate.\n\
581 This hook doesn't let you change the behavior of Emacs's selection replies,\n\
582 it merely informs you that they have happened.");
583 Vns_sent_selection_hooks = Qnil;
585 DEFVAR_LISP ("selection-converter-alist", &Vselection_converter_alist,
586 "An alist associating X Windows selection-types with functions.\n\
587 These functions are called to convert the selection, with three args:\n\
588 the name of the selection (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
589 a desired type to which the selection should be converted;\n\
590 and the local selection value (whatever was given to `x-own-selection').\n\
592 The function should return the value to send to the X server\n\
593 \(typically a string). A return value of nil\n\
594 means that the conversion could not be done.\n\
595 A return value which is the symbol `NULL'\n\
596 means that a side-effect was executed,\n\
597 and there is no meaningful selection value.");
598 Vselection_converter_alist = Qnil;
600 DEFVAR_LISP ("ns-lost-selection-hooks", &Vns_lost_selection_hooks,
601 "A list of functions to be called when Emacs loses an X selection.\n\
602 \(This happens when some other X client makes its own selection\n\
603 or when a Lisp program explicitly clears the selection.)\n\
604 The functions are called with one argument, the selection type\n\
605 \(a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD').");
606 Vns_lost_selection_hooks = Qnil;
608 Qforeign_selection = intern ("foreign-selection");
609 staticpro (&Qforeign_selection);
612 // arch-tag: 39d1dde7-06a6-49ff-95a7-0e7af12d2218