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