ChangeLog fix: standardize some bug refs.
[emacs.git] / src / nsselect.m
blobbcf2ac1fe63e2bd3e78366d749676d0e6d285200
1 /* NeXT/Open/GNUstep / MacOSX Cocoa selection processing for emacs.
2    Copyright (C) 1993-1994, 2005-2006, 2008-2014 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 static Lisp_Object QCLIPBOARD, QSECONDARY, QTEXT, QFILE_NAME;
39 static Lisp_Object Vselection_alist;
41 /* NSGeneralPboard is pretty much analogous to X11 CLIPBOARD */
42 static NSString *NXPrimaryPboard;
43 static NSString *NXSecondaryPboard;
46 static NSMutableDictionary *pasteboard_changecount;
48 /* ==========================================================================
50     Internal utility functions
52    ========================================================================== */
55 static NSString *
56 symbol_to_nsstring (Lisp_Object sym)
58   CHECK_SYMBOL (sym);
59   if (EQ (sym, QCLIPBOARD))   return NSGeneralPboard;
60   if (EQ (sym, QPRIMARY))     return NXPrimaryPboard;
61   if (EQ (sym, QSECONDARY))   return NXSecondaryPboard;
62   if (EQ (sym, QTEXT))        return NSStringPboardType;
63   return [NSString stringWithUTF8String: SSDATA (SYMBOL_NAME (sym))];
66 static NSPasteboard *
67 ns_symbol_to_pb (Lisp_Object symbol)
69   return [NSPasteboard pasteboardWithName: symbol_to_nsstring (symbol)];
72 static Lisp_Object
73 ns_string_to_symbol (NSString *t)
75   if ([t isEqualToString: NSGeneralPboard])
76     return QCLIPBOARD;
77   if ([t isEqualToString: NXPrimaryPboard])
78     return QPRIMARY;
79   if ([t isEqualToString: NXSecondaryPboard])
80     return QSECONDARY;
81   if ([t isEqualToString: NSStringPboardType])
82     return QTEXT;
83   if ([t isEqualToString: NSFilenamesPboardType])
84     return QFILE_NAME;
85   if ([t isEqualToString: NSTabularTextPboardType])
86     return QTEXT;
87   return intern ([t UTF8String]);
91 static Lisp_Object
92 clean_local_selection_data (Lisp_Object obj)
94   if (CONSP (obj)
95       && INTEGERP (XCAR (obj))
96       && CONSP (XCDR (obj))
97       && INTEGERP (XCAR (XCDR (obj)))
98       && NILP (XCDR (XCDR (obj))))
99     obj = Fcons (XCAR (obj), XCDR (obj));
101   if (CONSP (obj)
102       && INTEGERP (XCAR (obj))
103       && INTEGERP (XCDR (obj)))
104     {
105       if (XINT (XCAR (obj)) == 0)
106         return XCDR (obj);
107       if (XINT (XCAR (obj)) == -1)
108         return make_number (- XINT (XCDR (obj)));
109     }
111   if (VECTORP (obj))
112     {
113       ptrdiff_t i;
114       ptrdiff_t size = ASIZE (obj);
115       Lisp_Object copy;
117       if (size == 1)
118         return clean_local_selection_data (AREF (obj, 0));
119       copy = make_uninit_vector (size);
120       for (i = 0; i < size; i++)
121         ASET (copy, i, clean_local_selection_data (AREF (obj, i)));
122       return copy;
123     }
125   return obj;
129 static void
130 ns_declare_pasteboard (id pb)
132   [pb declareTypes: ns_send_types owner: NSApp];
136 static void
137 ns_undeclare_pasteboard (id pb)
139   [pb declareTypes: [NSArray array] owner: nil];
142 static void
143 ns_store_pb_change_count (id pb)
145   [pasteboard_changecount
146         setObject: [NSNumber numberWithLong: [pb changeCount]]
147            forKey: [pb name]];
150 static NSInteger
151 ns_get_pb_change_count (Lisp_Object selection)
153   id pb = ns_symbol_to_pb (selection);
154   return pb != nil ? [pb changeCount] : -1;
157 static NSInteger
158 ns_get_our_change_count_for (Lisp_Object selection)
160   NSNumber *num = [pasteboard_changecount
161                     objectForKey: symbol_to_nsstring (selection)];
162   return num != nil ? (NSInteger)[num longValue] : -1;
166 static void
167 ns_string_to_pasteboard_internal (id pb, Lisp_Object str, NSString *gtype)
169   if (EQ (str, Qnil))
170     {
171       [pb declareTypes: [NSArray array] owner: nil];
172     }
173   else
174     {
175       char *utfStr;
176       NSString *type, *nsStr;
177       NSEnumerator *tenum;
179       CHECK_STRING (str);
181       utfStr = SSDATA (str);
182       nsStr = [[NSString alloc] initWithBytesNoCopy: utfStr
183                                              length: SBYTES (str)
184                                            encoding: NSUTF8StringEncoding
185                                        freeWhenDone: NO];
186       // FIXME: Why those 2 different code paths?
187       if (gtype == nil)
188         {
189           // Used for ns_string_to_pasteboard
190           [pb declareTypes: ns_send_types owner: nil];
191           tenum = [ns_send_types objectEnumerator];
192           while ( (type = [tenum nextObject]) )
193             [pb setString: nsStr forType: type];
194         }
195       else
196         {
197           // Used for ns-own-selection-internal.
198           eassert (gtype == NSStringPboardType);
199           [pb setString: nsStr forType: gtype];
200         }
201       [nsStr release];
202       ns_store_pb_change_count (pb);
203     }
207 Lisp_Object
208 ns_get_local_selection (Lisp_Object selection_name,
209                         Lisp_Object target_type)
211   Lisp_Object local_value;
212   local_value = assq_no_quit (selection_name, Vselection_alist);
213   return local_value;
217 static Lisp_Object
218 ns_get_foreign_selection (Lisp_Object symbol, Lisp_Object target)
220   id pb;
221   pb = ns_symbol_to_pb (symbol);
222   return pb != nil ? ns_string_from_pasteboard (pb) : Qnil;
228 /* ==========================================================================
230     Functions used externally
232    ========================================================================== */
235 Lisp_Object
236 ns_string_from_pasteboard (id pb)
238   NSString *type, *str;
239   const char *utfStr;
240   int length;
242   type = [pb availableTypeFromArray: ns_return_types];
243   if (type == nil)
244     {
245       return Qnil;
246     }
248   /* get the string */
249   if (! (str = [pb stringForType: type]))
250     {
251       NSData *data = [pb dataForType: type];
252       if (data != nil)
253         str = [[NSString alloc] initWithData: data
254                                     encoding: NSUTF8StringEncoding];
255       if (str != nil)
256         {
257           [str autorelease];
258         }
259       else
260         {
261           return Qnil;
262         }
263     }
265   /* assume UTF8 */
266   NS_DURING
267     {
268       /* EOL conversion: PENDING- is this too simple? */
269       NSMutableString *mstr = [[str mutableCopy] autorelease];
270       [mstr replaceOccurrencesOfString: @"\r\n" withString: @"\n"
271             options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
272       [mstr replaceOccurrencesOfString: @"\r" withString: @"\n"
273             options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
275       utfStr = [mstr UTF8String];
276       length = [mstr lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
278 #if ! defined (NS_IMPL_COCOA)
279       if (!utfStr)
280         {
281           utfStr = [mstr cString];
282           length = strlen (utfStr);
283         }
284 #endif
285     }
286   NS_HANDLER
287     {
288       message1 ("ns_string_from_pasteboard: UTF8String failed\n");
289 #if defined (NS_IMPL_COCOA)
290       utfStr = "Conversion failed";
291 #else
292       utfStr = [str lossyCString];
293 #endif
294       length = strlen (utfStr);
295     }
296   NS_ENDHANDLER
298     return make_string (utfStr, length);
302 void
303 ns_string_to_pasteboard (id pb, Lisp_Object str)
305   ns_string_to_pasteboard_internal (pb, str, nil);
310 /* ==========================================================================
312     Lisp Defuns
314    ========================================================================== */
317 DEFUN ("ns-own-selection-internal", Fns_own_selection_internal,
318        Sns_own_selection_internal, 2, 2, 0,
319        doc: /* Assert an X selection of type SELECTION and value VALUE.
320 SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
321 \(Those are literal upper-case symbol names, since that's what X expects.)
322 VALUE is typically a string, or a cons of two markers, but may be
323 anything that the functions on `selection-converter-alist' know about.  */)
324      (Lisp_Object selection, Lisp_Object value)
326   id pb;
327   NSString *type;
328   Lisp_Object successful_p = Qnil, rest;
329   Lisp_Object target_symbol;
331   check_window_system (NULL);
332   CHECK_SYMBOL (selection);
333   if (NILP (value))
334     error ("Selection value may not be nil");
335   pb = ns_symbol_to_pb (selection);
336   if (pb == nil) return Qnil;
338   ns_declare_pasteboard (pb);
339   {
340     Lisp_Object old_value = assq_no_quit (selection, Vselection_alist);
341     Lisp_Object new_value = list2 (selection, value);
343     if (NILP (old_value))
344       Vselection_alist = Fcons (new_value, Vselection_alist);
345     else
346       Fsetcdr (old_value, Fcdr (new_value));
347   }
349   /* We only support copy of text.  */
350   type = NSStringPboardType;
351   target_symbol = ns_string_to_symbol (type);
352   if (STRINGP (value))
353     {
354       ns_string_to_pasteboard_internal (pb, value, type);
355       successful_p = Qt;
356     }
358   if (!EQ (Vns_sent_selection_hooks, Qunbound))
359     {
360       /* FIXME: Use run-hook-with-args!  */
361       for (rest = Vns_sent_selection_hooks; CONSP (rest); rest = Fcdr (rest))
362         call3 (Fcar (rest), selection, target_symbol, successful_p);
363     }
365   return value;
369 DEFUN ("ns-disown-selection-internal", Fns_disown_selection_internal,
370        Sns_disown_selection_internal, 1, 1, 0,
371        doc: /* If we own the selection SELECTION, disown it.
372 Disowning it means there is no such selection.  */)
373   (Lisp_Object selection)
375   id pb;
376   check_window_system (NULL);
377   CHECK_SYMBOL (selection);
379   if (ns_get_pb_change_count (selection)
380       != ns_get_our_change_count_for (selection))
381       return Qnil;
383   pb = ns_symbol_to_pb (selection);
384   if (pb != nil) ns_undeclare_pasteboard (pb);
385   return Qt;
389 DEFUN ("ns-selection-exists-p", Fns_selection_exists_p, Sns_selection_exists_p,
390        0, 2, 0, doc: /* Whether there is an owner for the given X selection.
391 SELECTION should be the name of the selection in question, typically
392 one of the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.  (X expects
393 these literal upper-case names.)  The symbol nil is the same as
394 `PRIMARY', and t is the same as `SECONDARY'.
396 TERMINAL should be a terminal object or a frame specifying the X
397 server to query.  If omitted or nil, that stands for the selected
398 frame's display, or the first available X display.
400 On Nextstep, TERMINAL is unused.  */)
401      (Lisp_Object selection, Lisp_Object terminal)
403   id pb;
404   NSArray *types;
406   if (!window_system_available (NULL))
407     return Qnil;
409   CHECK_SYMBOL (selection);
410   if (EQ (selection, Qnil)) selection = QPRIMARY;
411   if (EQ (selection, Qt)) selection = QSECONDARY;
412   pb = ns_symbol_to_pb (selection);
413   if (pb == nil) return Qnil;
415   types = [pb types];
416   return ([types count] == 0) ? Qnil : Qt;
420 DEFUN ("ns-selection-owner-p", Fns_selection_owner_p, Sns_selection_owner_p,
421        0, 2, 0,
422        doc: /* Whether the current Emacs process owns the given X Selection.
423 The arg should be the name of the selection in question, typically one of
424 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
425 \(Those are literal upper-case symbol names, since that's what X expects.)
426 For convenience, the symbol nil is the same as `PRIMARY',
427 and t is the same as `SECONDARY'.
429 TERMINAL should be a terminal object or a frame specifying the X
430 server to query.  If omitted or nil, that stands for the selected
431 frame's display, or the first available X display.
433 On Nextstep, TERMINAL is unused.  */)
434      (Lisp_Object selection, Lisp_Object terminal)
436   check_window_system (NULL);
437   CHECK_SYMBOL (selection);
438   if (EQ (selection, Qnil)) selection = QPRIMARY;
439   if (EQ (selection, Qt)) selection = QSECONDARY;
440   return ns_get_pb_change_count (selection)
441     == ns_get_our_change_count_for (selection);
445 DEFUN ("ns-get-selection", Fns_get_selection,
446        Sns_get_selection, 2, 4, 0,
447        doc: /* Return text selected from some X window.
448 SELECTION-SYMBOL is typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
449 \(Those are literal upper-case symbol names, since that's what X expects.)
450 TARGET-TYPE is the type of data desired, typically `STRING'.
452 TIME-STAMP is the time to use in the XConvertSelection call for foreign
453 selections.  If omitted, defaults to the time for the last event.
455 TERMINAL should be a terminal object or a frame specifying the X
456 server to query.  If omitted or nil, that stands for the selected
457 frame's display, or the first available X display.
459 On Nextstep, TIME-STAMP and TERMINAL are unused.  */)
460      (Lisp_Object selection_name, Lisp_Object target_type,
461       Lisp_Object time_stamp, Lisp_Object terminal)
463   Lisp_Object val = Qnil;
465   check_window_system (NULL);
466   CHECK_SYMBOL (selection_name);
467   CHECK_SYMBOL (target_type);
469   if (ns_get_pb_change_count (selection_name)
470       == ns_get_our_change_count_for (selection_name))
471       val = ns_get_local_selection (selection_name, target_type);
472   if (NILP (val))
473     val = ns_get_foreign_selection (selection_name, target_type);
474   if (CONSP (val) && SYMBOLP (Fcar (val)))
475     {
476       val = Fcdr (val);
477       if (CONSP (val) && NILP (Fcdr (val)))
478         val = Fcar (val);
479     }
480   val = clean_local_selection_data (val);
481   return val;
485 void
486 nxatoms_of_nsselect (void)
488   NXPrimaryPboard = @"Selection";
489   NXSecondaryPboard = @"Secondary";
491   // This is a memory loss, never released.
492   pasteboard_changecount =
493     [[NSMutableDictionary
494        dictionaryWithObjectsAndKeys:
495             [NSNumber numberWithLong:0], NSGeneralPboard,
496             [NSNumber numberWithLong:0], NXPrimaryPboard,
497             [NSNumber numberWithLong:0], NXSecondaryPboard,
498             [NSNumber numberWithLong:0], NSStringPboardType,
499             [NSNumber numberWithLong:0], NSFilenamesPboardType,
500             [NSNumber numberWithLong:0], NSTabularTextPboardType,
501        nil] retain];
504 void
505 syms_of_nsselect (void)
507   QCLIPBOARD = intern_c_string ("CLIPBOARD");   staticpro (&QCLIPBOARD);
508   QSECONDARY = intern_c_string ("SECONDARY");   staticpro (&QSECONDARY);
509   QTEXT      = intern_c_string ("TEXT");        staticpro (&QTEXT);
510   QFILE_NAME = intern_c_string ("FILE_NAME");   staticpro (&QFILE_NAME);
512   defsubr (&Sns_disown_selection_internal);
513   defsubr (&Sns_get_selection);
514   defsubr (&Sns_own_selection_internal);
515   defsubr (&Sns_selection_exists_p);
516   defsubr (&Sns_selection_owner_p);
518   Vselection_alist = Qnil;
519   staticpro (&Vselection_alist);
521   DEFVAR_LISP ("ns-sent-selection-hooks", Vns_sent_selection_hooks,
522                "A list of functions to be called when Emacs answers a selection request.\n\
523 The functions are called with four arguments:\n\
524   - the selection name (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
525   - the selection-type which Emacs was asked to convert the\n\
526     selection into before sending (for example, `STRING' or `LENGTH');\n\
527   - a flag indicating success or failure for responding to the request.\n\
528 We might have failed (and declined the request) for any number of reasons,\n\
529 including being asked for a selection that we no longer own, or being asked\n\
530 to convert into a type that we don't know about or that is inappropriate.\n\
531 This hook doesn't let you change the behavior of Emacs's selection replies,\n\
532 it merely informs you that they have happened.");
533   Vns_sent_selection_hooks = Qnil;