1 /* NeXT/Open/GNUstep / MacOSX Cocoa selection processing for emacs.
2 Copyright (C) 1993-1994, 2005-2006, 2008-2015 Free Software
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"
37 static Lisp_Object Vselection_alist;
39 /* NSGeneralPboard is pretty much analogous to X11 CLIPBOARD */
40 static NSString *NXPrimaryPboard;
41 static NSString *NXSecondaryPboard;
44 static NSMutableDictionary *pasteboard_changecount;
46 /* ==========================================================================
48 Internal utility functions
50 ========================================================================== */
54 symbol_to_nsstring (Lisp_Object sym)
57 if (EQ (sym, QCLIPBOARD)) return NSGeneralPboard;
58 if (EQ (sym, QPRIMARY)) return NXPrimaryPboard;
59 if (EQ (sym, QSECONDARY)) return NXSecondaryPboard;
60 if (EQ (sym, QTEXT)) return NSStringPboardType;
61 return [NSString stringWithUTF8String: SSDATA (SYMBOL_NAME (sym))];
65 ns_symbol_to_pb (Lisp_Object symbol)
67 return [NSPasteboard pasteboardWithName: symbol_to_nsstring (symbol)];
71 ns_string_to_symbol (NSString *t)
73 if ([t isEqualToString: NSGeneralPboard])
75 if ([t isEqualToString: NXPrimaryPboard])
77 if ([t isEqualToString: NXSecondaryPboard])
79 if ([t isEqualToString: NSStringPboardType])
81 if ([t isEqualToString: NSFilenamesPboardType])
83 if ([t isEqualToString: NSTabularTextPboardType])
85 return intern ([t UTF8String]);
90 clean_local_selection_data (Lisp_Object obj)
93 && INTEGERP (XCAR (obj))
95 && INTEGERP (XCAR (XCDR (obj)))
96 && NILP (XCDR (XCDR (obj))))
97 obj = Fcons (XCAR (obj), XCDR (obj));
100 && INTEGERP (XCAR (obj))
101 && INTEGERP (XCDR (obj)))
103 if (XINT (XCAR (obj)) == 0)
105 if (XINT (XCAR (obj)) == -1)
106 return make_number (- XINT (XCDR (obj)));
112 ptrdiff_t size = ASIZE (obj);
116 return clean_local_selection_data (AREF (obj, 0));
117 copy = make_uninit_vector (size);
118 for (i = 0; i < size; i++)
119 ASET (copy, i, clean_local_selection_data (AREF (obj, i)));
128 ns_declare_pasteboard (id pb)
130 [pb declareTypes: ns_send_types owner: NSApp];
135 ns_undeclare_pasteboard (id pb)
137 [pb declareTypes: [NSArray array] owner: nil];
141 ns_store_pb_change_count (id pb)
143 [pasteboard_changecount
144 setObject: [NSNumber numberWithLong: [pb changeCount]]
149 ns_get_pb_change_count (Lisp_Object selection)
151 id pb = ns_symbol_to_pb (selection);
152 return pb != nil ? [pb changeCount] : -1;
156 ns_get_our_change_count_for (Lisp_Object selection)
158 NSNumber *num = [pasteboard_changecount
159 objectForKey: symbol_to_nsstring (selection)];
160 return num != nil ? (NSInteger)[num longValue] : -1;
165 ns_string_to_pasteboard_internal (id pb, Lisp_Object str, NSString *gtype)
169 [pb declareTypes: [NSArray array] owner: nil];
174 NSString *type, *nsStr;
179 utfStr = SSDATA (str);
180 nsStr = [[NSString alloc] initWithBytesNoCopy: utfStr
182 encoding: NSUTF8StringEncoding
184 // FIXME: Why those 2 different code paths?
187 // Used for ns_string_to_pasteboard
188 [pb declareTypes: ns_send_types owner: nil];
189 tenum = [ns_send_types objectEnumerator];
190 while ( (type = [tenum nextObject]) )
191 [pb setString: nsStr forType: type];
195 // Used for ns-own-selection-internal.
196 eassert (gtype == NSStringPboardType);
197 [pb setString: nsStr forType: gtype];
200 ns_store_pb_change_count (pb);
206 ns_get_local_selection (Lisp_Object selection_name,
207 Lisp_Object target_type)
209 Lisp_Object local_value;
210 local_value = assq_no_quit (selection_name, Vselection_alist);
216 ns_get_foreign_selection (Lisp_Object symbol, Lisp_Object target)
219 pb = ns_symbol_to_pb (symbol);
220 return pb != nil ? ns_string_from_pasteboard (pb) : Qnil;
226 /* ==========================================================================
228 Functions used externally
230 ========================================================================== */
234 ns_string_from_pasteboard (id pb)
236 NSString *type, *str;
240 type = [pb availableTypeFromArray: ns_return_types];
247 if (! (str = [pb stringForType: type]))
249 NSData *data = [pb dataForType: type];
251 str = [[NSString alloc] initWithData: data
252 encoding: NSUTF8StringEncoding];
266 /* EOL conversion: PENDING- is this too simple? */
267 NSMutableString *mstr = [[str mutableCopy] autorelease];
268 [mstr replaceOccurrencesOfString: @"\r\n" withString: @"\n"
269 options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
270 [mstr replaceOccurrencesOfString: @"\r" withString: @"\n"
271 options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
273 utfStr = [mstr UTF8String];
274 length = [mstr lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
276 #if ! defined (NS_IMPL_COCOA)
279 utfStr = [mstr cString];
280 length = strlen (utfStr);
286 message1 ("ns_string_from_pasteboard: UTF8String failed\n");
287 #if defined (NS_IMPL_COCOA)
288 utfStr = "Conversion failed";
290 utfStr = [str lossyCString];
292 length = strlen (utfStr);
296 return make_string (utfStr, length);
301 ns_string_to_pasteboard (id pb, Lisp_Object str)
303 ns_string_to_pasteboard_internal (pb, str, nil);
308 /* ==========================================================================
312 ========================================================================== */
315 DEFUN ("ns-own-selection-internal", Fns_own_selection_internal,
316 Sns_own_selection_internal, 2, 2, 0,
317 doc: /* Assert an X selection of type SELECTION and value VALUE.
318 SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
319 \(Those are literal upper-case symbol names, since that's what X expects.)
320 VALUE is typically a string, or a cons of two markers, but may be
321 anything that the functions on `selection-converter-alist' know about. */)
322 (Lisp_Object selection, Lisp_Object value)
326 Lisp_Object successful_p = Qnil, rest;
327 Lisp_Object target_symbol;
329 check_window_system (NULL);
330 CHECK_SYMBOL (selection);
332 error ("Selection value may not be nil");
333 pb = ns_symbol_to_pb (selection);
334 if (pb == nil) return Qnil;
336 ns_declare_pasteboard (pb);
338 Lisp_Object old_value = assq_no_quit (selection, Vselection_alist);
339 Lisp_Object new_value = list2 (selection, value);
341 if (NILP (old_value))
342 Vselection_alist = Fcons (new_value, Vselection_alist);
344 Fsetcdr (old_value, Fcdr (new_value));
347 /* We only support copy of text. */
348 type = NSStringPboardType;
349 target_symbol = ns_string_to_symbol (type);
352 ns_string_to_pasteboard_internal (pb, value, type);
356 if (!EQ (Vns_sent_selection_hooks, Qunbound))
358 /* FIXME: Use run-hook-with-args! */
359 for (rest = Vns_sent_selection_hooks; CONSP (rest); rest = Fcdr (rest))
360 call3 (Fcar (rest), selection, target_symbol, successful_p);
367 DEFUN ("ns-disown-selection-internal", Fns_disown_selection_internal,
368 Sns_disown_selection_internal, 1, 1, 0,
369 doc: /* If we own the selection SELECTION, disown it.
370 Disowning it means there is no such selection. */)
371 (Lisp_Object selection)
374 check_window_system (NULL);
375 CHECK_SYMBOL (selection);
377 if (ns_get_pb_change_count (selection)
378 != ns_get_our_change_count_for (selection))
381 pb = ns_symbol_to_pb (selection);
382 if (pb != nil) ns_undeclare_pasteboard (pb);
387 DEFUN ("ns-selection-exists-p", Fns_selection_exists_p, Sns_selection_exists_p,
388 0, 2, 0, doc: /* Whether there is an owner for the given X selection.
389 SELECTION should be the name of the selection in question, typically
390 one of the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'. (X expects
391 these literal upper-case names.) The symbol nil is the same as
392 `PRIMARY', and t is the same as `SECONDARY'.
394 TERMINAL should be a terminal object or a frame specifying the X
395 server to query. If omitted or nil, that stands for the selected
396 frame's display, or the first available X display.
398 On Nextstep, TERMINAL is unused. */)
399 (Lisp_Object selection, Lisp_Object terminal)
404 if (!window_system_available (NULL))
407 CHECK_SYMBOL (selection);
408 if (EQ (selection, Qnil)) selection = QPRIMARY;
409 if (EQ (selection, Qt)) selection = QSECONDARY;
410 pb = ns_symbol_to_pb (selection);
411 if (pb == nil) return Qnil;
414 return ([types count] == 0) ? Qnil : Qt;
418 DEFUN ("ns-selection-owner-p", Fns_selection_owner_p, Sns_selection_owner_p,
420 doc: /* Whether the current Emacs process owns the given X Selection.
421 The arg should be the name of the selection in question, typically one of
422 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
423 \(Those are literal upper-case symbol names, since that's what X expects.)
424 For convenience, the symbol nil is the same as `PRIMARY',
425 and t is the same as `SECONDARY'.
427 TERMINAL should be a terminal object or a frame specifying the X
428 server to query. If omitted or nil, that stands for the selected
429 frame's display, or the first available X display.
431 On Nextstep, TERMINAL is unused. */)
432 (Lisp_Object selection, Lisp_Object terminal)
434 check_window_system (NULL);
435 CHECK_SYMBOL (selection);
436 if (EQ (selection, Qnil)) selection = QPRIMARY;
437 if (EQ (selection, Qt)) selection = QSECONDARY;
438 return ns_get_pb_change_count (selection)
439 == ns_get_our_change_count_for (selection)
444 DEFUN ("ns-get-selection", Fns_get_selection,
445 Sns_get_selection, 2, 4, 0,
446 doc: /* Return text selected from some X window.
447 SELECTION-SYMBOL is typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
448 \(Those are literal upper-case symbol names, since that's what X expects.)
449 TARGET-TYPE is the type of data desired, typically `STRING'.
451 TIME-STAMP is the time to use in the XConvertSelection call for foreign
452 selections. If omitted, defaults to the time for the last event.
454 TERMINAL should be a terminal object or a frame specifying the X
455 server to query. If omitted or nil, that stands for the selected
456 frame's display, or the first available X display.
458 On Nextstep, TIME-STAMP and TERMINAL are unused. */)
459 (Lisp_Object selection_name, Lisp_Object target_type,
460 Lisp_Object time_stamp, Lisp_Object terminal)
462 Lisp_Object val = Qnil;
464 check_window_system (NULL);
465 CHECK_SYMBOL (selection_name);
466 CHECK_SYMBOL (target_type);
468 if (ns_get_pb_change_count (selection_name)
469 == ns_get_our_change_count_for (selection_name))
470 val = ns_get_local_selection (selection_name, target_type);
472 val = ns_get_foreign_selection (selection_name, target_type);
473 if (CONSP (val) && SYMBOLP (Fcar (val)))
476 if (CONSP (val) && NILP (Fcdr (val)))
479 val = clean_local_selection_data (val);
485 nxatoms_of_nsselect (void)
487 NXPrimaryPboard = @"Selection";
488 NXSecondaryPboard = @"Secondary";
490 // This is a memory loss, never released.
491 pasteboard_changecount =
492 [[NSMutableDictionary
493 dictionaryWithObjectsAndKeys:
494 [NSNumber numberWithLong:0], NSGeneralPboard,
495 [NSNumber numberWithLong:0], NXPrimaryPboard,
496 [NSNumber numberWithLong:0], NXSecondaryPboard,
497 [NSNumber numberWithLong:0], NSStringPboardType,
498 [NSNumber numberWithLong:0], NSFilenamesPboardType,
499 [NSNumber numberWithLong:0], NSTabularTextPboardType,
504 syms_of_nsselect (void)
506 DEFSYM (QCLIPBOARD, "CLIPBOARD");
507 DEFSYM (QSECONDARY, "SECONDARY");
508 DEFSYM (QTEXT, "TEXT");
509 DEFSYM (QFILE_NAME, "FILE_NAME");
511 defsubr (&Sns_disown_selection_internal);
512 defsubr (&Sns_get_selection);
513 defsubr (&Sns_own_selection_internal);
514 defsubr (&Sns_selection_exists_p);
515 defsubr (&Sns_selection_owner_p);
517 Vselection_alist = Qnil;
518 staticpro (&Vselection_alist);
520 DEFVAR_LISP ("ns-sent-selection-hooks", Vns_sent_selection_hooks,
521 "A list of functions to be called when Emacs answers a selection request.\n\
522 The functions are called with four arguments:\n\
523 - the selection name (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
524 - the selection-type which Emacs was asked to convert the\n\
525 selection into before sending (for example, `STRING' or `LENGTH');\n\
526 - a flag indicating success or failure for responding to the request.\n\
527 We might have failed (and declined the request) for any number of reasons,\n\
528 including being asked for a selection that we no longer own, or being asked\n\
529 to convert into a type that we don't know about or that is inappropriate.\n\
530 This hook doesn't let you change the behavior of Emacs's selection replies,\n\
531 it merely informs you that they have happened.");
532 Vns_sent_selection_hooks = Qnil;