Bump version number to 23.0.95.
[emacs.git] / src / nsselect.m
blob665b8cfd5ef7aab3cb3a24111091cf5ac358941c
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. */
30 #include "config.h"
32 #include "lisp.h"
33 #include "nsterm.h"
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    ========================================================================== */
58 static NSString *
59 symbol_to_nsstring (Lisp_Object sym)
61   CHECK_SYMBOL (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)];
69 static Lisp_Object
70 ns_string_to_symbol (NSString *t)
72   if ([t isEqualToString: NSGeneralPboard])
73     return QPRIMARY;
74   if ([t isEqualToString: NXSecondaryPboard])
75     return QSECONDARY;
76   if ([t isEqualToString: NSStringPboardType])
77     return QTEXT;
78   if ([t isEqualToString: NSFilenamesPboardType])
79     return QFILE_NAME;
80   if ([t isEqualToString: NSTabularTextPboardType])
81     return QTEXT;
82   return intern ([t UTF8String]);
86 static Lisp_Object
87 clean_local_selection_data (Lisp_Object obj)
89   if (CONSP (obj)
90       && INTEGERP (XCAR (obj))
91       && CONSP (XCDR (obj))
92       && INTEGERP (XCAR (XCDR (obj)))
93       && NILP (XCDR (XCDR (obj))))
94     obj = Fcons (XCAR (obj), XCDR (obj));
96   if (CONSP (obj)
97       && INTEGERP (XCAR (obj))
98       && INTEGERP (XCDR (obj)))
99     {
100       if (XINT (XCAR (obj)) == 0)
101         return XCDR (obj);
102       if (XINT (XCAR (obj)) == -1)
103         return make_number (- XINT (XCDR (obj)));
104     }
106   if (VECTORP (obj))
107     {
108       int i;
109       int size = ASIZE (obj);
110       Lisp_Object copy;
112       if (size == 1)
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)));
117       return copy;
118     }
120   return obj;
124 static void
125 ns_declare_pasteboard (id pb)
127   [pb declareTypes: ns_send_types owner: NSApp];
131 static void
132 ns_undeclare_pasteboard (id pb)
134   [pb declareTypes: [NSArray array] owner: nil];
138 static void
139 ns_string_to_pasteboard_internal (id pb, Lisp_Object str, NSString *gtype)
141   if (EQ (str, Qnil))
142     {
143       [pb declareTypes: [NSArray array] owner: nil];
144     }
145   else
146     {
147       char *utfStr;
148       NSString *type, *nsStr;
149       NSEnumerator *tenum;
151       CHECK_STRING (str);
153       utfStr = SDATA (str);
154       nsStr = [NSString stringWithUTF8String: utfStr];
156       if (gtype == nil)
157         {
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];
162         }
163       else
164         {
165           [pb setString: nsStr forType: gtype];
166         }
167     }
171 static Lisp_Object
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;
177   int count;
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)));
190   else
191     value = Qnil;
192   unbind_to (count, Qnil);
194   check = value;
195   if (CONSP (value) && SYMBOLP (XCAR (value)))
196     {
197       type = XCAR (value);
198       check = XCDR (value);
199     }
201   if (STRINGP (check) || VECTORP (check) || SYMBOLP (check)
202       || INTEGERP (check) || NILP (value))
203     return value;
205   if (CONSP (check)
206       && INTEGERP (XCAR (check))
207       && (INTEGERP (XCDR (check))||
208           (CONSP (XCDR (check))
209            && INTEGERP (XCAR (XCDR (check)))
210            && NILP (XCDR (XCDR (check))))))
211     return value;
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!!
218   return Qnil;
222 static Lisp_Object
223 ns_get_foreign_selection (Lisp_Object symbol, Lisp_Object target)
225   id pb;
226   pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (symbol)];
227   return ns_string_from_pasteboard (pb);
231 static void
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);
243   successful_p = Qnil;
245   if (!NILP (selection_data))
246     {
247       data = ns_get_local_selection (selection_name, target_symbol);
248       if (!NILP (data))
249         {
250           if (STRINGP (data))
251             ns_string_to_pasteboard_internal (pb, data, type);
252           successful_p = Qt;
253         }
254     }
256   if (!EQ (Vns_sent_selection_hooks, Qunbound))
257     {
258       for (rest = Vns_sent_selection_hooks; CONSP (rest); rest = Fcdr (rest))
259         call3 (Fcar (rest), selection_name, target_symbol, successful_p);
260     }
264 static void
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);
276   else
277     {
278       for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest))
279         if (EQ (selection_data, Fcar (Fcdr (rest))))
280           Fsetcdr (rest, Fcdr (Fcdr (rest)));
281     }
283   if (!EQ (Vns_lost_selection_hooks, Qunbound))
284     {
285       for (rest = Vns_lost_selection_hooks;CONSP (rest); rest = Fcdr (rest))
286         call1 (Fcar (rest), selection_name);
287     }
292 /* ==========================================================================
294     Functions used externally
296    ========================================================================== */
299 Lisp_Object
300 ns_string_from_pasteboard (id pb)
302   NSString *type, *str;
303   const char *utfStr;
305   type = [pb availableTypeFromArray: ns_return_types];
306   if (type == nil)
307     {
308       Fsignal (Qquit,
309               Fcons (build_string ("empty or unsupported pasteboard type"),
310                     Qnil));
311     return Qnil;
312     }
314   /* get the string */
315   if (! (str = [pb stringForType: type]))
316     {
317       NSData *data = [pb dataForType: type];
318       if (data != nil)
319         str = [[NSString alloc] initWithData: data
320                                     encoding: NSUTF8StringEncoding];
321       if (str != nil)
322         {
323           [str autorelease];
324         }
325       else
326         {
327           Fsignal (Qquit,
328                   Fcons (build_string ("pasteboard doesn't contain valid data"),
329                         Qnil));
330           return Qnil;
331         }
332     }
334   /* assume UTF8 */
335   NS_DURING
336     {
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];
345       if (!utfStr)
346         utfStr = [mstr cString];
347     }
348   NS_HANDLER
349     {
350       message1 ("ns_string_from_pasteboard: UTF8String failed\n");
351       utfStr = [str lossyCString];
352     }
353   NS_ENDHANDLER
355   return build_string (utfStr);
359 void
360 ns_string_to_pasteboard (id pb, Lisp_Object str)
362   ns_string_to_pasteboard_internal (pb, str, nil);
367 /* ==========================================================================
369     Lisp Defuns
371    ========================================================================== */
374 DEFUN ("x-own-selection-internal", Fx_own_selection_internal,
375        Sx_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;
383   id pb;
384   Lisp_Object old_value, new_value;
386   check_ns ();
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);
396   else
397     Fsetcdr (old_value, Fcdr (new_value));
398   /* XXX An evil hack, but a necessary one I fear XXX */
399   {
400     struct input_event ev;
401     ev.kind = SELECTION_REQUEST_EVENT;
402     ev.modifiers = 0;
403     ev.code = 0;
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);
407   }
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;
418   id pb;
419   check_ns ();
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);
425   return Qt;
429 DEFUN ("x-selection-exists-p", Fx_selection_exists_p, Sx_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'.)  */)
436      (selection)
437      Lisp_Object selection;
439   id pb;
440   NSArray *types;
442   check_ns ();
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)];
447   types =[pb types];
448   return ([types count] == 0) ? Qnil : Qt;
452 DEFUN ("x-selection-owner-p", Fx_selection_owner_p, Sx_selection_owner_p,
453        0, 1, 0,
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'.)  */)
460      (selection)
461      Lisp_Object selection;
463   check_ns ();
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;
480   Lisp_Object val;
482   check_ns ();
483   CHECK_SYMBOL (selection_name);
484   CHECK_SYMBOL (target_type);
485   val = ns_get_local_selection (selection_name, target_type);
486   if (NILP (val))
487     val = ns_get_foreign_selection (selection_name, target_type);
488   if (CONSP (val) && SYMBOLP (Fcar (val)))
489     {
490       val = Fcdr (val);
491       if (CONSP (val) && NILP (Fcdr (val)))
492         val = Fcar (val);
493     }
494   val = clean_local_selection_data (val);
495   return 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.  */)
503      (buffer)
504      Lisp_Object buffer;
506   id pb;
507   check_ns ();
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. */ )
518      (n)
519      Lisp_Object n;
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));
524   return 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).  */)
531      (buffer, string)
532      Lisp_Object buffer, string;
534   id pb;
535   check_ns ();
536   pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (buffer)];
537   ns_string_to_pasteboard (pb, string);
538   return Qnil;
540 #endif
543 void
544 nxatoms_of_nsselect (void)
546   NXSecondaryPboard = @"Selection";
549 void
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 (&Sx_own_selection_internal);
560   defsubr (&Sx_selection_exists_p);
561   defsubr (&Sx_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);
566 #endif
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