(ns-handle-switch): Simplify. Handle the numeric case.
[bpt/emacs.git] / src / nsselect.m
CommitLineData
edfda783 1/* NeXT/Open/GNUstep / MacOSX Cocoa selection processing for emacs.
32d235f8
GM
2 Copyright (C) 1993, 1994, 2005, 2006, 2008
3 Free Software Foundation, Inc.
edfda783
AR
4
5This file is part of GNU Emacs.
6
32d235f8 7GNU Emacs is free software: you can redistribute it and/or modify
edfda783 8it under the terms of the GNU General Public License as published by
32d235f8
GM
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
edfda783
AR
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
32d235f8 18along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
edfda783 19
32d235f8 20/*
edfda783
AR
21Originally by Carl Edman
22Updated by Christian Limpach (chris@nice.ch)
23OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com)
24MacOSX/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
25GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
edfda783
AR
26*/
27
28#include "config.h"
29#include "lisp.h"
30#include "nsterm.h"
31#include "termhooks.h"
32
33#define CUT_BUFFER_SUPPORT
34
35Lisp_Object QPRIMARY, QSECONDARY, QTEXT, QFILE_NAME;
36
37static Lisp_Object Vns_sent_selection_hooks;
38static Lisp_Object Vns_lost_selection_hooks;
39static Lisp_Object Vselection_alist;
40static Lisp_Object Vselection_converter_alist;
41
42/* 23: new */
43/* Coding system for communicating with other programs. */
44static Lisp_Object Vselection_coding_system;
45/* Coding system for the next communicating with other programs. */
46static Lisp_Object Vnext_selection_coding_system;
47static Lisp_Object Qforeign_selection;
48
49NSString *NXSecondaryPboard;
50
51
52
53/* ==========================================================================
54
55 Internal utility functions
56
57 ========================================================================== */
58
59
60static NSString *
61symbol_to_nsstring (Lisp_Object sym)
62{
63 CHECK_SYMBOL (sym);
64 if (EQ (sym, QPRIMARY)) return NSGeneralPboard;
65 if (EQ (sym, QSECONDARY)) return NXSecondaryPboard;
66 if (EQ (sym, QTEXT)) return NSStringPboardType;
67 return [NSString stringWithUTF8String: XSTRING (XSYMBOL (sym)->xname)->data];
68}
69
70
71static Lisp_Object
72ns_string_to_symbol (NSString *t)
73{
74 if ([t isEqualToString: NSGeneralPboard])
75 return QPRIMARY;
76 if ([t isEqualToString: NXSecondaryPboard])
77 return QSECONDARY;
78 if ([t isEqualToString: NSStringPboardType])
79 return QTEXT;
80 if ([t isEqualToString: NSFilenamesPboardType])
81 return QFILE_NAME;
82 if ([t isEqualToString: NSTabularTextPboardType])
83 return QTEXT;
84 return intern ([t UTF8String]);
85}
86
87
88static Lisp_Object
89clean_local_selection_data (Lisp_Object obj)
90{
91 if (CONSP (obj)
92 && INTEGERP (XCAR (obj))
93 && CONSP (XCDR (obj))
94 && INTEGERP (XCAR (XCDR (obj)))
95 && NILP (XCDR (XCDR (obj))))
96 obj = Fcons (XCAR (obj), XCDR (obj));
97
98 if (CONSP (obj)
99 && INTEGERP (XCAR (obj))
100 && INTEGERP (XCDR (obj)))
101 {
102 if (XINT (XCAR (obj)) == 0)
103 return XCDR (obj);
104 if (XINT (XCAR (obj)) == -1)
105 return make_number (- XINT (XCDR (obj)));
106 }
107
108 if (VECTORP (obj))
109 {
110 int i;
111 int size = XVECTOR (obj)->size;
112 Lisp_Object copy;
113
114 if (size == 1)
115 return clean_local_selection_data (XVECTOR (obj)->contents [0]);
116 copy = Fmake_vector (size, Qnil);
117 for (i = 0; i < size; i++)
118 XVECTOR (copy)->contents [i]
119 = clean_local_selection_data (XVECTOR (obj)->contents [i]);
120 return copy;
121 }
122
123 return obj;
124}
125
126
127static void
128ns_declare_pasteboard (id pb)
129{
130 [pb declareTypes: ns_send_types owner: NSApp];
131}
132
133
134static void
135ns_undeclare_pasteboard (id pb)
136{
137 [pb declareTypes: [NSArray array] owner: nil];
138}
139
140
141static void
142ns_string_to_pasteboard_internal (id pb, Lisp_Object str, NSString *gtype)
143{
144 if (EQ (str, Qnil))
145 {
146 [pb declareTypes: [NSArray array] owner: nil];
147 }
148 else
149 {
150 char *utfStr;
151 NSString *type, *nsStr;
152 NSEnumerator *tenum;
153
154 CHECK_STRING (str);
155
156 utfStr = XSTRING (str)->data;
157 nsStr = [NSString stringWithUTF8String: utfStr];
158
159 if (gtype == nil)
160 {
161 [pb declareTypes: ns_send_types owner: nil];
162 tenum = [ns_send_types objectEnumerator];
163 while ( (type = [tenum nextObject]) )
164 [pb setString: nsStr forType: type];
165 }
166 else
167 {
168 [pb setString: nsStr forType: gtype];
169 }
170 }
171}
172
173
174static Lisp_Object
175ns_get_local_selection (Lisp_Object selection_name,
176 Lisp_Object target_type)
177{
178 Lisp_Object local_value;
179 Lisp_Object handler_fn, value, type, check;
180 int count;
181
182 local_value = assq_no_quit (selection_name, Vselection_alist);
183
184 if (NILP (local_value)) return Qnil;
185
186 count = specpdl_ptr - specpdl;
187 specbind (Qinhibit_quit, Qt);
188 CHECK_SYMBOL (target_type);
189 handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist));
190 if (!NILP (handler_fn))
191 value =call3 (handler_fn, selection_name, target_type,
192 XCAR (XCDR (local_value)));
193 else
194 value =Qnil;
195 unbind_to (count, Qnil);
196
197 check =value;
198 if (CONSP (value) && SYMBOLP (XCAR (value)))
199 {
200 type = XCAR (value);
201 check = XCDR (value);
202 }
203
204 if (STRINGP (check) || VECTORP (check) || SYMBOLP (check)
205 || INTEGERP (check) || NILP (value))
206 return value;
207
208 if (CONSP (check)
209 && INTEGERP (XCAR (check))
210 && (INTEGERP (XCDR (check))||
211 (CONSP (XCDR (check))
212 && INTEGERP (XCAR (XCDR (check)))
213 && NILP (XCDR (XCDR (check))))))
214 return value;
215
216 Fsignal (Qquit, Fcons (build_string (
217 "invalid data returned by selection-conversion function"),
218 Fcons (handler_fn, Fcons (value, Qnil))));
219}
220
221
222static Lisp_Object
223ns_get_foreign_selection (Lisp_Object symbol, Lisp_Object target)
224{
225 id pb;
226 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (symbol)];
227 return ns_string_from_pasteboard (pb);
228}
229
230
231static void
232ns_handle_selection_request (struct input_event *event)
233{
234 id pb =(id)event->x;
235 NSString *type =(NSString *)event->y;
236 Lisp_Object selection_name, selection_data, target_symbol, data;
237 Lisp_Object successful_p, rest;
238
239 selection_name =ns_string_to_symbol ([(NSPasteboard *)pb name]);
240 target_symbol =ns_string_to_symbol (type);
241 selection_data = assq_no_quit (selection_name, Vselection_alist);
242 successful_p =Qnil;
243
244 if (!NILP (selection_data))
245 {
246 data = ns_get_local_selection (selection_name, target_symbol);
247 if (!NILP (data))
248 {
249 if (STRINGP (data))
250 ns_string_to_pasteboard_internal (pb, data, type);
251 successful_p =Qt;
252 }
253 }
254
255 if (!EQ (Vns_sent_selection_hooks, Qunbound))
256 {
257 for (rest =Vns_sent_selection_hooks;CONSP (rest); rest =Fcdr (rest))
258 call3 (Fcar (rest), selection_name, target_symbol, successful_p);
259 }
260}
261
262
263static void
264ns_handle_selection_clear (struct input_event *event)
265{
266 id pb = (id)event->x;
267 Lisp_Object selection_name, selection_data, rest;
268
269 selection_name =ns_string_to_symbol ([(NSPasteboard *)pb name]);
270 selection_data =assq_no_quit (selection_name, Vselection_alist);
271 if (NILP (selection_data)) return;
272
273 if (EQ (selection_data, Fcar (Vselection_alist)))
274 Vselection_alist = Fcdr (Vselection_alist);
275 else
276 {
277 for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest))
278 if (EQ (selection_data, Fcar (Fcdr (rest))))
279 Fsetcdr (rest, Fcdr (Fcdr (rest)));
280 }
281
282 if (!EQ (Vns_lost_selection_hooks, Qunbound))
283 {
284 for (rest =Vns_lost_selection_hooks;CONSP (rest); rest =Fcdr (rest))
285 call1 (Fcar (rest), selection_name);
286 }
287}
288
289
290
291/* ==========================================================================
292
293 Functions used externally
294
295 ========================================================================== */
296
297
298Lisp_Object
299ns_string_from_pasteboard (id pb)
300{
301 NSString *type, *str;
302 const char *utfStr;
303
304 type = [pb availableTypeFromArray: ns_return_types];
305 if (type == nil)
306 {
307 Fsignal (Qquit,
308 Fcons (build_string ("empty or unsupported pasteboard type"),
309 Qnil));
310 return Qnil;
311 }
312
313 /* get the string */
314 if (! (str = [pb stringForType: type]))
315 {
316 NSData *data = [pb dataForType: type];
317 if (data != nil)
318 str = [[NSString alloc] initWithData: data
319 encoding: NSUTF8StringEncoding];
320 if (str != nil)
321 {
322 [str autorelease];
323 }
324 else
325 {
326 Fsignal (Qquit,
327 Fcons (build_string ("pasteboard doesn't contain valid data"),
328 Qnil));
329 return Qnil;
330 }
331 }
332
333 /* assume UTF8 */
334 NS_DURING
335 {
336 /* EOL conversion: PENDING- is this too simple? */
337 NSMutableString *mstr = [[str mutableCopy] autorelease];
338 [mstr replaceOccurrencesOfString: @"\r\n" withString: @"\n"
339 options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
340 [mstr replaceOccurrencesOfString: @"\r" withString: @"\n"
341 options: NSLiteralSearch range: NSMakeRange (0, [mstr length])];
342
343 utfStr = [mstr UTF8String];
344 if (!utfStr)
345 utfStr = [mstr cString];
346 }
347 NS_HANDLER
348 {
349 message1 ("ns_string_from_pasteboard: UTF8String failed\n");
350 utfStr = [str lossyCString];
351 }
352 NS_ENDHANDLER
353
354 return build_string (utfStr);
355}
356
357
358void
359ns_string_to_pasteboard (id pb, Lisp_Object str)
360{
361 ns_string_to_pasteboard_internal (pb, str, nil);
362}
363
364
365
366/* ==========================================================================
367
368 Lisp Defuns
369
370 ========================================================================== */
371
372
373DEFUN ("ns-own-selection-internal", Fns_own_selection_internal,
374 Sns_own_selection_internal, 2, 2, 0, "Assert a selection.")
375 (selection_name, selection_value)
376 Lisp_Object selection_name, selection_value;
377{
378 id pb;
379 Lisp_Object old_value, new_value;
380
381 check_ns ();
382 CHECK_SYMBOL (selection_name);
383 if (NILP (selection_value))
384 error ("selection-value may not be nil.");
385 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection_name)];
386 ns_declare_pasteboard (pb);
387 old_value =assq_no_quit (selection_name, Vselection_alist);
388 new_value = Fcons (selection_name, Fcons (selection_value, Qnil));
389 if (NILP (old_value))
390 Vselection_alist =Fcons (new_value, Vselection_alist);
391 else
392 Fsetcdr (old_value, Fcdr (new_value));
393 /* XXX An evil hack, but a necessary one I fear XXX */
394 {
395 struct input_event ev;
396 ev.kind = SELECTION_REQUEST_EVENT;
397 ev.modifiers = 0;
398 ev.code = 0;
399 ev.x = (int)pb;
400 ev.y = (int)NSStringPboardType;
401 ns_handle_selection_request (&ev);
402 }
403 return selection_value;
404}
405
406
407DEFUN ("ns-disown-selection-internal", Fns_disown_selection_internal,
408 Sns_disown_selection_internal, 1, 2, 0,
409 "If we own the selection SELECTION, disown it.")
410 (selection_name, time)
411 Lisp_Object selection_name, time;
412{
413 id pb;
414 check_ns ();
415 CHECK_SYMBOL (selection_name);
416 if (NILP (assq_no_quit (selection_name, Vselection_alist))) return Qnil;
417
418 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection_name)];
419 ns_undeclare_pasteboard (pb);
420 return Qt;
421}
422
423
424DEFUN ("ns-selection-exists-p", Fns_selection_exists_p, Sns_selection_exists_p,
425 0, 1, 0, "Whether there is an owner for the given selection.\n\
426The arg should be the name of the selection in question, typically one of\n\
427the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.\n\
428\(Those are literal upper-case symbol names.)\n\
429For convenience, the symbol nil is the same as `PRIMARY',\n\
430and t is the same as `SECONDARY'.)")
431 (selection)
432 Lisp_Object selection;
433{
434 id pb;
435 NSArray *types;
436
437 check_ns ();
438 CHECK_SYMBOL (selection);
439 if (EQ (selection, Qnil)) selection = QPRIMARY;
440 if (EQ (selection, Qt)) selection = QSECONDARY;
441 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (selection)];
442 types =[pb types];
443 return ([types count] == 0) ? Qnil : Qt;
444}
445
446
447DEFUN ("ns-selection-owner-p", Fns_selection_owner_p, Sns_selection_owner_p,
448 0, 1, 0,
449 "Whether the current Emacs process owns the given selection.\n\
450The arg should be the name of the selection in question, typically one of\n\
451the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.\n\
452\(Those are literal upper-case symbol names.)\n\
453For convenience, the symbol nil is the same as `PRIMARY',\n\
454and t is the same as `SECONDARY'.)")
455 (selection)
456 Lisp_Object selection;
457{
458 check_ns ();
459 CHECK_SYMBOL (selection);
460 if (EQ (selection, Qnil)) selection = QPRIMARY;
461 if (EQ (selection, Qt)) selection = QSECONDARY;
462 return (NILP (Fassq (selection, Vselection_alist))) ? Qnil : Qt;
463}
464
465
466DEFUN ("ns-get-selection-internal", Fns_get_selection_internal,
467 Sns_get_selection_internal, 2, 2, 0,
468 "Return text selected from some pasteboard.\n\
469SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.\n\
470\(Those are literal upper-case symbol names.)\n\
471TYPE is the type of data desired, typically `STRING'.")
472 (selection_name, target_type)
473 Lisp_Object selection_name, target_type;
474{
475 Lisp_Object val;
476
477 check_ns ();
478 CHECK_SYMBOL (selection_name);
479 CHECK_SYMBOL (target_type);
480 val = ns_get_local_selection (selection_name, target_type);
481 if (NILP (val))
482 val = ns_get_foreign_selection (selection_name, target_type);
483 if (CONSP (val) && SYMBOLP (Fcar (val)))
484 {
485 val = Fcdr (val);
486 if (CONSP (val) && NILP (Fcdr (val)))
487 val = Fcar (val);
488 }
489 val = clean_local_selection_data (val);
490 return val;
491}
492
493
494#ifdef CUT_BUFFER_SUPPORT
495DEFUN ("ns-get-cut-buffer-internal", Fns_get_cut_buffer_internal,
496 Sns_get_cut_buffer_internal, 1, 1, 0,
497 "Returns the value of the named cut buffer.")
498 (buffer)
499 Lisp_Object buffer;
500{
501 id pb;
502 check_ns ();
503 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (buffer)];
504 return ns_string_from_pasteboard (pb);
505}
506
507
508DEFUN ("ns-rotate-cut-buffers-internal", Fns_rotate_cut_buffers_internal,
509 Sns_rotate_cut_buffers_internal, 1, 1, 0,
510 "Rotate the values of the cut buffers by the given number of steps;\n\
511 positive means move values forward, negative means backward. CURRENTLY NOT IMPLEMENTED UNDER NeXTstep.")
512 (n)
513 Lisp_Object n;
514{
515 /* XXX This function is unimplemented under NeXTstep XXX */
516 Fsignal (Qquit, Fcons (build_string (
517 "Warning: ns-rotate-cut-buffers-internal not implemented\n"), Qnil));
518 return Qnil;
519}
520
521
522DEFUN ("ns-store-cut-buffer-internal", Fns_store_cut_buffer_internal,
523 Sns_store_cut_buffer_internal, 2, 2, 0,
524 "Sets the value of the named cut buffer (typically CUT_BUFFER0).")
525 (buffer, string)
526 Lisp_Object buffer, string;
527{
528 id pb;
529 check_ns ();
530 pb =[NSPasteboard pasteboardWithName: symbol_to_nsstring (buffer)];
531 ns_string_to_pasteboard (pb, string);
532 return Qnil;
533}
534#endif
535
536
537void
538nxatoms_of_nsselect (void)
539{
540 NXSecondaryPboard = @"Selection";
541}
542
543void
544syms_of_nsselect (void)
545{
546 QPRIMARY = intern ("PRIMARY"); staticpro (&QPRIMARY);
547 QSECONDARY = intern ("SECONDARY"); staticpro (&QSECONDARY);
548 QTEXT = intern ("TEXT"); staticpro (&QTEXT);
549 QFILE_NAME = intern ("FILE_NAME"); staticpro (&QFILE_NAME);
550
551 defsubr (&Sns_disown_selection_internal);
552 defsubr (&Sns_get_selection_internal);
553 defsubr (&Sns_own_selection_internal);
554 defsubr (&Sns_selection_exists_p);
555 defsubr (&Sns_selection_owner_p);
556#ifdef CUT_BUFFER_SUPPORT
557 defsubr (&Sns_get_cut_buffer_internal);
558 defsubr (&Sns_rotate_cut_buffers_internal);
559 defsubr (&Sns_store_cut_buffer_internal);
560#endif
561
562 Vselection_alist = Qnil;
563 staticpro (&Vselection_alist);
564
565 DEFVAR_LISP ("ns-sent-selection-hooks", &Vns_sent_selection_hooks,
566 "A list of functions to be called when Emacs answers a selection request.\n\
567The functions are called with four arguments:\n\
568 - the selection name (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
569 - the selection-type which Emacs was asked to convert the\n\
570 selection into before sending (for example, `STRING' or `LENGTH');\n\
571 - a flag indicating success or failure for responding to the request.\n\
572We might have failed (and declined the request) for any number of reasons,\n\
573including being asked for a selection that we no longer own, or being asked\n\
574to convert into a type that we don't know about or that is inappropriate.\n\
575This hook doesn't let you change the behavior of Emacs's selection replies,\n\
576it merely informs you that they have happened.");
577 Vns_sent_selection_hooks = Qnil;
578
579 DEFVAR_LISP ("selection-converter-alist", &Vselection_converter_alist,
580 "An alist associating X Windows selection-types with functions.\n\
581These functions are called to convert the selection, with three args:\n\
582the name of the selection (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
583a desired type to which the selection should be converted;\n\
584and the local selection value (whatever was given to `x-own-selection').\n\
585\n\
586The function should return the value to send to the X server\n\
587\(typically a string). A return value of nil\n\
588means that the conversion could not be done.\n\
589A return value which is the symbol `NULL'\n\
590means that a side-effect was executed,\n\
591and there is no meaningful selection value.");
592 Vselection_converter_alist = Qnil;
593
594 DEFVAR_LISP ("ns-lost-selection-hooks", &Vns_lost_selection_hooks,
595 "A list of functions to be called when Emacs loses an X selection.\n\
596\(This happens when some other X client makes its own selection\n\
597or when a Lisp program explicitly clears the selection.)\n\
598The functions are called with one argument, the selection type\n\
599\(a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD').");
600 Vns_lost_selection_hooks = Qnil;
601
602/* 23: { */
603 DEFVAR_LISP ("selection-coding-system", &Vselection_coding_system,
604 doc: /* Coding system for communicating with other programs.
605When sending or receiving text via cut_buffer, selection, and clipboard,
606the text is encoded or decoded by this coding system.
607The default value is determined by the system script code. */);
608 Vselection_coding_system = Qnil;
609
610 DEFVAR_LISP ("next-selection-coding-system", &Vnext_selection_coding_system,
611 doc: /* Coding system for the next communication with other programs.
612Usually, `selection-coding-system' is used for communicating with
613other programs. But, if this variable is set, it is used for the
614next communication only. After the communication, this variable is
615set to nil. */);
616 Vnext_selection_coding_system = Qnil;
617
618 Qforeign_selection = intern ("foreign-selection");
619 staticpro (&Qforeign_selection);
620/* } */
621
622}
0ae1e5e5 623
2f8e74bb 624// arch-tag: 39d1dde7-06a6-49ff-95a7-0e7af12d2218