* todo-mode.el: Offer to convert legacy file. Update commentary.
[emacs.git] / src / nsselect.m
blob6053ee9ceb23496091eb0ba4963a6fe940cf6a5e
1 /* NeXT/Open/GNUstep / MacOSX Cocoa selection processing for emacs.
2    Copyright (C) 1993-1994, 2005-2006, 2008-2013 Free Software
3    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. */
30 #include <config.h>
32 #include "lisp.h"
33 #include "nsterm.h"
34 #include "termhooks.h"
35 #include "keyboard.h"
37 Lisp_Object QCLIPBOARD, QSECONDARY, QTEXT, QFILE_NAME;
39 static Lisp_Object Vselection_alist;
41 static Lisp_Object Qforeign_selection;
43 /* NSGeneralPboard is pretty much analogous to X11 CLIPBOARD */
44 NSString *NXPrimaryPboard;
45 NSString *NXSecondaryPboard;
49 /* ==========================================================================
51     Internal utility functions
53    ========================================================================== */
56 static NSString *
57 symbol_to_nsstring (Lisp_Object sym)
59   CHECK_SYMBOL (sym);
60   if (EQ (sym, QCLIPBOARD))   return NSGeneralPboard;
61   if (EQ (sym, QPRIMARY))     return NXPrimaryPboard;
62   if (EQ (sym, QSECONDARY))   return NXSecondaryPboard;
63   if (EQ (sym, QTEXT))        return NSStringPboardType;
64   return [NSString stringWithUTF8String: SSDATA (SYMBOL_NAME (sym))];
67 static NSPasteboard *
68 ns_symbol_to_pb (Lisp_Object symbol)
70   return [NSPasteboard pasteboardWithName: symbol_to_nsstring (symbol)];
73 static Lisp_Object
74 ns_string_to_symbol (NSString *t)
76   if ([t isEqualToString: NSGeneralPboard])
77     return QCLIPBOARD;
78   if ([t isEqualToString: NXPrimaryPboard])
79     return QPRIMARY;
80   if ([t isEqualToString: NXSecondaryPboard])
81     return QSECONDARY;
82   if ([t isEqualToString: NSStringPboardType])
83     return QTEXT;
84   if ([t isEqualToString: NSFilenamesPboardType])
85     return QFILE_NAME;
86   if ([t isEqualToString: NSTabularTextPboardType])
87     return QTEXT;
88   return intern ([t UTF8String]);
92 static Lisp_Object
93 clean_local_selection_data (Lisp_Object obj)
95   if (CONSP (obj)
96       && INTEGERP (XCAR (obj))
97       && CONSP (XCDR (obj))
98       && INTEGERP (XCAR (XCDR (obj)))
99       && NILP (XCDR (XCDR (obj))))
100     obj = Fcons (XCAR (obj), XCDR (obj));
102   if (CONSP (obj)
103       && INTEGERP (XCAR (obj))
104       && INTEGERP (XCDR (obj)))
105     {
106       if (XINT (XCAR (obj)) == 0)
107         return XCDR (obj);
108       if (XINT (XCAR (obj)) == -1)
109         return make_number (- XINT (XCDR (obj)));
110     }
112   if (VECTORP (obj))
113     {
114       ptrdiff_t i;
115       ptrdiff_t size = ASIZE (obj);
116       Lisp_Object copy;
118       if (size == 1)
119         return clean_local_selection_data (AREF (obj, 0));
120       copy = make_uninit_vector (size);
121       for (i = 0; i < size; i++)
122         ASET (copy, i, clean_local_selection_data (AREF (obj, i)));
123       return copy;
124     }
126   return obj;
130 static void
131 ns_declare_pasteboard (id pb)
133   [pb declareTypes: ns_send_types owner: NSApp];
137 static void
138 ns_undeclare_pasteboard (id pb)
140   [pb declareTypes: [NSArray array] owner: nil];
144 static void
145 ns_string_to_pasteboard_internal (id pb, Lisp_Object str, NSString *gtype)
147   if (EQ (str, Qnil))
148     {
149       [pb declareTypes: [NSArray array] owner: nil];
150     }
151   else
152     {
153       char *utfStr;
154       NSString *type, *nsStr;
155       NSEnumerator *tenum;
157       CHECK_STRING (str);
159       utfStr = SSDATA (str);
160       nsStr = [[NSString alloc] initWithBytesNoCopy: utfStr
161                                              length: SBYTES (str)
162                                            encoding: NSUTF8StringEncoding
163                                        freeWhenDone: NO];
164       if (gtype == nil)
165         {
166           [pb declareTypes: ns_send_types owner: nil];
167           tenum = [ns_send_types objectEnumerator];
168           while ( (type = [tenum nextObject]) )
169             [pb setString: nsStr forType: type];
170         }
171       else
172         {
173           [pb setString: nsStr forType: gtype];
174         }
175       [nsStr release];
176     }
180 Lisp_Object
181 ns_get_local_selection (Lisp_Object selection_name,
182                        Lisp_Object target_type)
184   Lisp_Object local_value;
185   Lisp_Object handler_fn, value, check;
186   ptrdiff_t count;
188   local_value = assq_no_quit (selection_name, Vselection_alist);
190   if (NILP (local_value)) return Qnil;
192   count = specpdl_ptr - specpdl;
193   specbind (Qinhibit_quit, Qt);
194   CHECK_SYMBOL (target_type);
195   handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist));
196   if (!NILP (handler_fn))
197     value = call3 (handler_fn, selection_name, target_type,
198                 XCAR (XCDR (local_value)));
199   else
200     value = Qnil;
201   unbind_to (count, Qnil);
203   check = value;
204   if (CONSP (value) && SYMBOLP (XCAR (value)))
205     {
206       check = XCDR (value);
207     }
209   if (STRINGP (check) || VECTORP (check) || SYMBOLP (check)
210       || INTEGERP (check) || NILP (value))
211     return value;
213   if (CONSP (check)
214       && INTEGERP (XCAR (check))
215       && (INTEGERP (XCDR (check))||
216           (CONSP (XCDR (check))
217            && INTEGERP (XCAR (XCDR (check)))
218            && NILP (XCDR (XCDR (check))))))
219     return value;
221   // FIXME: Why `quit' rather than `error'?
222   Fsignal (Qquit, Fcons (build_string (
223       "invalid data returned by selection-conversion function"),
224                         Fcons (handler_fn, Fcons (value, Qnil))));
225   // FIXME: Beware, `quit' can return!!
226   return Qnil;
230 static Lisp_Object
231 ns_get_foreign_selection (Lisp_Object symbol, Lisp_Object target)
233   id pb;
234   pb = ns_symbol_to_pb (symbol);
235   return pb != nil ? ns_string_from_pasteboard (pb) : Qnil;
241 /* ==========================================================================
243     Functions used externally
245    ========================================================================== */
248 Lisp_Object
249 ns_string_from_pasteboard (id pb)
251   NSString *type, *str;
252   const char *utfStr;
253   int length;
255   type = [pb availableTypeFromArray: ns_return_types];
256   if (type == nil)
257     {
258       Fsignal (Qquit,
259               Fcons (build_string ("empty or unsupported pasteboard type"),
260                     Qnil));
261     return Qnil;
262     }
264   /* get the string */
265   if (! (str = [pb stringForType: type]))
266     {
267       NSData *data = [pb dataForType: type];
268       if (data != nil)
269         str = [[NSString alloc] initWithData: data
270                                     encoding: NSUTF8StringEncoding];
271       if (str != nil)
272         {
273           [str autorelease];
274         }
275       else
276         {
277           Fsignal (Qquit,
278                   Fcons (build_string ("pasteboard doesn't contain valid data"),
279                         Qnil));
280           return Qnil;
281         }
282     }
284   /* assume UTF8 */
285   NS_DURING
286     {
287       /* EOL conversion: PENDING- is this too simple? */
288       NSMutableString *mstr = [[str mutableCopy] autorelease];
289       [mstr replaceOccurrencesOfString: @"\r\n" withString: @"\n"
290             options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
291       [mstr replaceOccurrencesOfString: @"\r" withString: @"\n"
292             options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
294       utfStr = [mstr UTF8String];
295       length = [mstr lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
297 #if ! defined (NS_IMPL_COCOA)
298       if (!utfStr)
299         {
300           utfStr = [mstr cString];
301           length = strlen (utfStr);
302         }
303 #endif
304     }
305   NS_HANDLER
306     {
307       message1 ("ns_string_from_pasteboard: UTF8String failed\n");
308 #if defined (NS_IMPL_COCOA)
309       utfStr = "Conversion failed";
310 #else
311       utfStr = [str lossyCString];
312 #endif
313       length = strlen (utfStr);
314     }
315   NS_ENDHANDLER
317     return make_string (utfStr, length);
321 void
322 ns_string_to_pasteboard (id pb, Lisp_Object str)
324   ns_string_to_pasteboard_internal (pb, str, nil);
329 /* ==========================================================================
331     Lisp Defuns
333    ========================================================================== */
336 DEFUN ("x-own-selection-internal", Fx_own_selection_internal,
337        Sx_own_selection_internal, 2, 3, 0,
338        doc: /* Assert an X selection of type SELECTION and value VALUE.
339 SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
340 \(Those are literal upper-case symbol names, since that's what X expects.)
341 VALUE is typically a string, or a cons of two markers, but may be
342 anything that the functions on `selection-converter-alist' know about.
344 FRAME should be a frame that should own the selection.  If omitted or
345 nil, it defaults to the selected frame.
347 On Nextstep, FRAME is unused.  */)
348      (Lisp_Object selection, Lisp_Object value, Lisp_Object frame)
350   id pb;
351   Lisp_Object old_value, new_value;
352   NSString *type;
353   Lisp_Object successful_p = Qnil, rest;
354   Lisp_Object target_symbol, data;
356   check_window_system (NULL);
357   CHECK_SYMBOL (selection);
358   if (NILP (value))
359       error ("selection value may not be nil.");
360   pb = ns_symbol_to_pb (selection);
361   if (pb == nil) return Qnil;
363   ns_declare_pasteboard (pb);
364   old_value = assq_no_quit (selection, Vselection_alist);
365   new_value = Fcons (selection, Fcons (value, Qnil));
367   if (NILP (old_value))
368     Vselection_alist = Fcons (new_value, Vselection_alist);
369   else
370     Fsetcdr (old_value, Fcdr (new_value));
372   /* We only support copy of text.  */
373   type = NSStringPboardType;
374   target_symbol = ns_string_to_symbol (type);
375   data = ns_get_local_selection (selection, target_symbol);
376   if (!NILP (data))
377     {
378       if (STRINGP (data))
379         ns_string_to_pasteboard_internal (pb, data, type);
380       successful_p = Qt;
381     }
383   if (!EQ (Vns_sent_selection_hooks, Qunbound))
384     {
385       for (rest = Vns_sent_selection_hooks; CONSP (rest); rest = Fcdr (rest))
386         call3 (Fcar (rest), selection, target_symbol, successful_p);
387     }
389   return value;
393 DEFUN ("x-disown-selection-internal", Fx_disown_selection_internal,
394        Sx_disown_selection_internal, 1, 3, 0,
395        doc: /* If we own the selection SELECTION, disown it.
396 Disowning it means there is no such selection.
398 Sets the last-change time for the selection to TIME-OBJECT (by default
399 the time of the last event).
401 TERMINAL should be a terminal object or a frame specifying the X
402 server to query.  If omitted or nil, that stands for the selected
403 frame's display, or the first available X display.
405 On Nextstep, the TIME-OBJECT and TERMINAL arguments are unused.
406 On MS-DOS, all this does is return non-nil if we own the selection.  */)
407   (Lisp_Object selection, Lisp_Object time_object, Lisp_Object terminal)
409   id pb;
410   check_window_system (NULL);
411   CHECK_SYMBOL (selection);
412   if (NILP (assq_no_quit (selection, Vselection_alist))) return Qnil;
414   pb = ns_symbol_to_pb (selection);
415   if (pb != nil) ns_undeclare_pasteboard (pb);
416   return Qt;
420 DEFUN ("x-selection-exists-p", Fx_selection_exists_p, Sx_selection_exists_p,
421        0, 2, 0, doc: /* Whether there is an owner for the given X selection.
422 SELECTION should be the name of the selection in question, typically
423 one of the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.  (X expects
424 these literal upper-case names.)  The symbol nil is the same as
425 `PRIMARY', 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   id pb;
435   NSArray *types;
437   check_window_system (NULL);
438   CHECK_SYMBOL (selection);
439   if (EQ (selection, Qnil)) selection = QPRIMARY;
440   if (EQ (selection, Qt)) selection = QSECONDARY;
441   pb = ns_symbol_to_pb (selection);
442   if (pb == nil) return Qnil;
444   types = [pb types];
445   return ([types count] == 0) ? Qnil : Qt;
449 DEFUN ("x-selection-owner-p", Fx_selection_owner_p, Sx_selection_owner_p,
450        0, 2, 0,
451        doc: /* Whether the current Emacs process owns the given X Selection.
452 The arg should be the name of the selection in question, typically one of
453 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
454 \(Those are literal upper-case symbol names, since that's what X expects.)
455 For convenience, the symbol nil is the same as `PRIMARY',
456 and t is the same as `SECONDARY'.
458 TERMINAL should be a terminal object or a frame specifying the X
459 server to query.  If omitted or nil, that stands for the selected
460 frame's display, or the first available X display.
462 On Nextstep, TERMINAL is unused.  */)
463      (Lisp_Object selection, Lisp_Object terminal)
465   check_window_system (NULL);
466   CHECK_SYMBOL (selection);
467   if (EQ (selection, Qnil)) selection = QPRIMARY;
468   if (EQ (selection, Qt)) selection = QSECONDARY;
469   return (NILP (Fassq (selection, Vselection_alist))) ? Qnil : Qt;
473 DEFUN ("x-get-selection-internal", Fx_get_selection_internal,
474        Sx_get_selection_internal, 2, 4, 0,
475        doc: /* Return text selected from some X window.
476 SELECTION-SYMBOL is typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
477 \(Those are literal upper-case symbol names, since that's what X expects.)
478 TARGET-TYPE is the type of data desired, typically `STRING'.
480 TIME-STAMP is the time to use in the XConvertSelection call for foreign
481 selections.  If omitted, defaults to the time for the last event.
483 TERMINAL should be a terminal object or a frame specifying the X
484 server to query.  If omitted or nil, that stands for the selected
485 frame's display, or the first available X display.
487 On Nextstep, TIME-STAMP and TERMINAL are unused.  */)
488      (Lisp_Object selection_name, Lisp_Object target_type,
489       Lisp_Object time_stamp, Lisp_Object terminal)
491   Lisp_Object val;
493   check_window_system (NULL);
494   CHECK_SYMBOL (selection_name);
495   CHECK_SYMBOL (target_type);
496   val = ns_get_local_selection (selection_name, target_type);
497   if (NILP (val))
498     val = ns_get_foreign_selection (selection_name, target_type);
499   if (CONSP (val) && SYMBOLP (Fcar (val)))
500     {
501       val = Fcdr (val);
502       if (CONSP (val) && NILP (Fcdr (val)))
503         val = Fcar (val);
504     }
505   val = clean_local_selection_data (val);
506   return val;
510 DEFUN ("ns-get-selection-internal", Fns_get_selection_internal,
511        Sns_get_selection_internal, 1, 1, 0,
512        doc: /* Returns the value of SELECTION as a string.
513 SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'. */)
514      (Lisp_Object selection)
516   id pb;
517   check_window_system (NULL);
518   pb = ns_symbol_to_pb (selection);
519   return pb != nil ? ns_string_from_pasteboard (pb) : Qnil;
523 DEFUN ("ns-store-selection-internal", Fns_store_selection_internal,
524        Sns_store_selection_internal, 2, 2, 0,
525        doc: /* Sets the string value of SELECTION.
526 SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'. */)
527      (Lisp_Object selection, Lisp_Object string)
529   id pb;
530   check_window_system (NULL);
531   pb = ns_symbol_to_pb (selection);
532   if (pb != nil) ns_string_to_pasteboard (pb, string);
533   return Qnil;
537 void
538 nxatoms_of_nsselect (void)
540   NXPrimaryPboard = @"Selection";
541   NXSecondaryPboard = @"Secondary";
544 void
545 syms_of_nsselect (void)
547   QCLIPBOARD = intern_c_string ("CLIPBOARD");   staticpro (&QCLIPBOARD);
548   QSECONDARY = intern_c_string ("SECONDARY");   staticpro (&QSECONDARY);
549   QTEXT      = intern_c_string ("TEXT");        staticpro (&QTEXT);
550   QFILE_NAME = intern_c_string ("FILE_NAME");   staticpro (&QFILE_NAME);
552   defsubr (&Sx_disown_selection_internal);
553   defsubr (&Sx_get_selection_internal);
554   defsubr (&Sx_own_selection_internal);
555   defsubr (&Sx_selection_exists_p);
556   defsubr (&Sx_selection_owner_p);
557   defsubr (&Sns_get_selection_internal);
558   defsubr (&Sns_store_selection_internal);
560   Vselection_alist = Qnil;
561   staticpro (&Vselection_alist);
563   DEFVAR_LISP ("ns-sent-selection-hooks", Vns_sent_selection_hooks,
564                "A list of functions to be called when Emacs answers a selection request.\n\
565 The functions are called with four arguments:\n\
566   - the selection name (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
567   - the selection-type which Emacs was asked to convert the\n\
568     selection into before sending (for example, `STRING' or `LENGTH');\n\
569   - a flag indicating success or failure for responding to the request.\n\
570 We might have failed (and declined the request) for any number of reasons,\n\
571 including being asked for a selection that we no longer own, or being asked\n\
572 to convert into a type that we don't know about or that is inappropriate.\n\
573 This hook doesn't let you change the behavior of Emacs's selection replies,\n\
574 it merely informs you that they have happened.");
575   Vns_sent_selection_hooks = Qnil;
577   DEFVAR_LISP ("selection-converter-alist", Vselection_converter_alist,
578                "An alist associating X Windows selection-types with functions.\n\
579 These functions are called to convert the selection, with three args:\n\
580 the name of the selection (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
581 a desired type to which the selection should be converted;\n\
582 and the local selection value (whatever was given to `x-own-selection').\n\
584 The function should return the value to send to the X server\n\
585 \(typically a string).  A return value of nil\n\
586 means that the conversion could not be done.\n\
587 A return value which is the symbol `NULL'\n\
588 means that a side-effect was executed,\n\
589 and there is no meaningful selection value.");
590   Vselection_converter_alist = Qnil;
592   DEFVAR_LISP ("ns-lost-selection-hooks", Vns_lost_selection_hooks,
593                "A list of functions to be called when Emacs loses an X selection.\n\
594 \(This happens when some other X client makes its own selection\n\
595 or when a Lisp program explicitly clears the selection.)\n\
596 The functions are called with one argument, the selection type\n\
597 \(a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD').");
598   Vns_lost_selection_hooks = Qnil;
600   Qforeign_selection = intern_c_string ("foreign-selection");
601   staticpro (&Qforeign_selection);