1/** <title>NSPasteboard</title>
2
3   <abstract>Implementation of class for communicating with the
4			pasteboard server.</abstract>
5
6   Copyright (C) 1997,1999,2003 Free Software Foundation, Inc.
7
8   Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
9   Date: 1997
10
11   This file is part of the GNUstep GUI Library.
12
13   This library is free software; you can redistribute it and/or
14   modify it under the terms of the GNU Lesser General Public
15   License as published by the Free Software Foundation; either
16   version 2 of the License, or (at your option) any later version.
17
18   This library is distributed in the hope that it will be useful,
19   but WITHOUT ANY WARRANTY; without even the implied warranty of
20   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
21   Lesser General Public License for more details.
22
23   You should have received a copy of the GNU Lesser General Public
24   License along with this library; see the file COPYING.LIB.
25   If not, see <http://www.gnu.org/licenses/> or write to the
26   Free Software Foundation, 51 Franklin Street, Fifth Floor,
27   Boston, MA 02110-1301, USA.
28
29
30<chapter>
31  <heading>The pasteboard system</heading>
32  <p>
33    The pasteboard system is the core of OpenStep inter-application
34    communications.  This chapter is concerned with the use of the system,
35    for detailed reference see the [NSPasteboard] class.<br />
36    For non-standard services provided by applications (ie those which
37    do not fit the general <em>services</em> mechanism described below),
38    you generally use the Distributed Objects system (see [NSConnection])
39    directly, and some hints about that are provided at the end of this
40    chapter.
41  </p>
42  <section>
43    <heading>Cut and Paste</heading>
44    <p>
45      The most obvious use of the pasteboard system is to support cut and
46      paste of text and other data, permitting the user to take selected
47      information from a document open in an application, and move it
48      around in the same document, or to another document open in the same
49      application, or to a document open in another application entirely.
50    </p>
51    <p>
52      While some objects (eg instances of [NSText]) will handle cut and
53      paste for you automatically, you may often need to do this yourself
54      in your own classes.  The mechanism for this is quite simple, and
55      should be done in a method called when the user selects the
56      <em>Cut</em> or <em>Copy</em> item on the <em>Edit</em> menu.<br />
57      The methods to do this should be called <em>cut:</em> and <em>copy:</em>
58      respectively, and will be called automatically when the menu items
59      are selected.
60    </p>
61    <list>
62      <item>
63        <strong>Select a pasteboard to use</strong><br />
64        There some standard pasteboards, or you can obtain or create other
65	ones with the [NSPasteboard+pasteboardWithName:] method.<br />
66	Usually you will want to use a standard pasteboard such as
67        the one returned by the [NSPasteboard+generalPasteboard] method.
68<example>
69  NSPasteboard *pb = [NSPasteboard generalPasteboard];
70</example>
71      </item>
72      <item>
73	<strong>Declare ownership and types</strong><br />
74	When you are going to supply data for pasting, you must take
75	ownership of the pasteboard and specify what types of data can
76        be provided.  If you are going to place the data on the pasteboard
77        immediately, you don't need to set a pasteboard owner, but if
78        you plan to supply the data <em>lazily</em> (ie on-demand), you
79        need to specify an object which the system can ask to provide
80        the data when it needs it.  In either case, you need to say what
81        kinds of data the pasteboard will supply, and you use the
82	[NSPasteboard-declareTypes:owner:] method to do this.
83<example>
84  // Provide string data immediately.
85  [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType]
86	     owner: nil];
87  [pb setString: myString forType: NSStringPboardType];
88</example>
89      </item>
90      <item>
91	<strong>Provide data for pasting</strong><br />
92	If you decided to provide data lazily (recommended) then the
93	pasteboard owner you declared will be asked to provide the
94	data when it is needed for pasting.
95<example>
96- (void) pasteboard: (NSPasteboard*)pb provideDataForType: (NSString*)type
97{
98  // Place the data needed for pasting onto the pasteboard.
99  [pb setData: data forType: type];
100}
101</example>
102      </item>
103      <item>
104	<strong>Support multiple types</strong><br />
105	Normally, it is best to support pasting of multiple types of data
106	so that the object into which the data is being pasted can handle
107        the pasted information readily.  To do this it is conventional to
108        supply data in the <em>richest</em> possible format in the cut:
109	or copy: method, and supply other forms of data lazily.
110<example>
111// Supply RTF data to the pasteboard system.
112- (id) copy: (id)sender
113{
114  NSPasteboard *pb = [NSPasteboard generalPasteboard];
115  [pb declareTypes: [NSArray arrayWithObjects: NSRTFPboardType,
116    NSStringPboardType, nil]
117	     owner: nil];
118  [pb setData: myData forType: NSRTFPboardType];
119}
120</example>
121	The providing object can retrieve the data initially stored in the
122        pasteboard, and set the type of data actually needed.
123<example>
124- (void) pasteboard: (NSPasteboard*)pb provideDataForType: (NSString*)type
125{
126  if ([type isEqualToString: NSStringPboardType] == YES)
127    {
128      NSData *d = [pb dataForType: NSRTFPboardType];
129      NSString *s = [self convertToString: d];
130      [pb setString: s forType: NSStringPboardType];
131    }
132  else
133    {
134      // Unsupported type ... should not happen
135      [pb setData: nil forType: type];
136    }
137}
138</example>
139      </item>
140    </list>
141    <p>
142      Similarly, when the user selects the <em>Paste</em> item on the
143      <em>Edit</em> menu, the <em>paste:</em> method in your code will
144      be called, and this method should retrieve data from the pasteboard
145      and insert it into your custom object so that the user can see it.
146    </p>
147    <list>
148      <item>
149	<strong>Retrieve data from pasteboard</strong>
150	<example>
151- (id) paste: (id)sender
152{
153  NSPasteboard *pb = [NSPasteboard generalPasteboard];
154  NSString *info = [pb stringForType: NSStringPboardType];
155  // Now make use of info
156  return self;
157}
158	</example>
159      </item>
160    </list>
161  </section>
162  <section>
163    <heading>Drag and Drop</heading>
164    <p>
165      The drag and drop system for transferring data is in essence a simple
166      extension of copy and paste, where the data being dragged is a
167      copy of some initially selected data, and the location to which it is
168      pasted depends on where it is dropped.<br />
169      To support drag and drop, you use a few standard methods to interact
170      with pasteboards, but you need to extend this with DnD specific methods
171      to handle the drag and drop process.
172    </p>
173  </section>
174  <section>
175    <heading>Services</heading>
176    <p>
177      The services system provides a standardised mechanism for an application
178      to provide services to other applications.  Like cut and paste, or
179      drag and drop, the use of an application service is normally initiated
180      by the user selecting some data to work with.  The user then goes to
181      the services menu, and selects a service listed there.  The selection
182      of a menu item causes the data to be placed on a pasteboard and
183      transferred to the service providing application, where the action of
184      the service is performed on it, and resulting data transferred back
185      to the original system via the pasteboard system again.
186    </p>
187    <p>
188      To make use of a service then, you typically need to make <em>no</em>
189      changes to your application, making the services facility supremely
190      easy to deal with!<br />
191      If however, you wish to make use of a service programmatically (rather
192      than from the services menu), you can use the NSPerformService()
193      function to invoke the service directly ...
194    </p>
195    <example>
196  // Create a pasteboard and store a string in it.
197  NSPasteboard *pb = [NSPasteboard pasteboardWithUniqueName];
198  [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType]
199	     owner: nil];
200  [pb setString: myString forType: NSStringPboardType];
201  // Invoke a service which takes string input and produces data output.
202  if (NSPerformService(@"TheServiceName", pb) == YES)
203    {
204      result = [pb dataForType: NSGeneralPboardType];
205    }
206    </example>
207    <p>
208      Providing a service is a bit trickier, it involves implementing a
209      method to perform the service (usually in your [NSApplication-delegate]
210      object) and specifying information about your service in the Info.plist
211      file for your application.<br />
212      When your application is installed in one of the standard locations,
213      and the <em>make_services</em> tool is run to update the cache of
214      services information, your service automatically becomes available
215      on the services menu of every application you run.<br />
216      At runtime, you use [NSApplication-setServicesProvider:] to specify
217      the object which implements the method to perform the service,
218      or, if you are providing the service from a process other than a
219      GUI application, you use the NSRegisterServicesProvider() function.
220    </p>
221    <p>
222      Your Info.plist should contain an array named <code>NSServices</code>
223      listing all the services your application provides.  Each service
224      definition should be a dictionary containing the following information -
225    </p>
226    <deflist>
227      <term>NSSendTypes</term>
228      <desc>
229	This is an array containing the string values of the types of
230	data that the service provider can handle (ie the types of data
231	the application requesting the service may send).<br />
232	The string values are the same as the standard constant names
233	for these types, so the string "NSStringPboardType" would match
234	the use of the <code>NSStringPboardType</code> in your code.<br />
235	Similarly, the functions NSCreateFileContentsPboardType() and
236	NSCreateFilenamePboardType() return types whose string values
237	are found by appending the filename extension concerned to the
238	strings "NSTypedFileContentsPboardType:" and
239	"NSTypedFilenamesPboardType:" respectively.
240      </desc>
241      <term>NSReturnTypes</term>
242      <desc>
243	These are the types of data that the service provider may return
244	and are specified in the same way as the NSSendTypes.<br />
245	NB. A service must handle at least one send type or one return type,
246	but it is OK to have a service which expects no input data or one
247	which produces no output data.
248      </desc>
249      <term>NSMessage</term>
250      <desc>
251	This mandatory string value is the interesting part of
252	the message which is sent to your service provider in
253	order to perform the service.<br />
254	The method in your application which does the work, must take three
255	arguments and have a name formed of this value followed by
256	<code>:userData:error:</code>
257<example>
258// If NSMessage=encryptData
259- (void) encryptString: (NSPasteboard*)pboard
260	      userData: (NSString*)userData
261		 error: (NSString**)error;
262</example>
263	This method will be pass the pasteboard to use and an optional
264        user data string, and must return results in the pasteboard, or
265        an error message in the error argument.
266      </desc>
267      <term>NSPortName</term>
268      <desc>
269	This specifies the name of the Distributed Objects port
270	(see [NSConnection] and [NSPort]) on which the service provider
271	will be listening for messages.  While its value depends on how
272	you register the service, it is normally the name of the application
273	providing the service.  This information is required in order for
274	other applications to know how to contact the service provider.
275      </desc>
276      <term>NSUserData</term>
277      <desc>
278	This is an optional arbitrary string which (if present) is passed
279	as the userData argument to the method implementing the service.
280	This permits a service provider to implement a single method to
281	handle a variety of similar services, whose exact characteristics
282	are determined by this parameter.
283      </desc>
284      <term>NSMenuItem</term>
285      <desc>
286	This is a dictionary containing language names and the text to
287	appear in the services menu for each language.  It may contain
288	an entry where the language name is <code>default</code> and
289	this entry will be used where none of the specific languages
290	listed are found in the application user's preferences.<br />
291	These text items may contain a single slash ('/') character,
292	and if this is present, the text after the slash will appear
293	in a submenu of the services menu, with the text before the
294	slash being the name of that submenu.  This is very useful
295	where a single application provides a variety of services and
296	wishes to group them together.
297      </desc>
298      <term>NSKeyEquivalent</term>
299      <desc>
300	This is an optional dictionary specifying the key equivalents to
301	select the menu items listed in the NSMenuItem specification.
302      </desc>
303      <term>NSTimeout</term>
304      <desc>
305	This is an optional timeout (in milliseconds) specifying how long
306	the system should wait for the service provider to perform the
307	service.  If omitted, it defaults to 30000 (30 seconds).
308      </desc>
309      <term>NSExecutable</term>
310      <desc>
311	This is an optional path to the executable binary of the program
312	which performs the service .. it's used to launch the program if
313	it is not already running.  Normally, for an application, this is
314	not necessary, as the system knows how to launch any applications
315	found installed in standard locations.
316      </desc>
317      <term>NSHost</term>
318      <desc>
319	Not yet implemented ... this provides for the system to launch the
320	executable for this service on a different host on the network.
321      </desc>
322    </deflist>
323    <p>
324      The actual code to implement a service is very simple, even with
325      error checking added -
326    </p>
327    <example>
328- (void) encryptString: (NSPasteboard*)pboard
329	      userData: (NSString*)userData
330		 error: (NSString**)error
331{
332  NSString	*d;
333
334  if ([pboard types] containsObject: NSStringPboardType] == NO)
335    {
336      *error = @"Bad types for encrypt service ... no string data";
337      return;
338    }
339  s = [pboard stringForType: NSStringPboardType];
340  if ([d length] == 0)
341    {
342      *error = @"No data supplied for encrypt service";
343      return;
344    }
345  s = [self encryptString: s];	// Do the real work
346  [pboard declareTypes: [NSArray arrayWithObject: NSStringPboardType
347		 owner: nil];
348  [pboard setString: s forType: NSStringPboardType];
349  return;
350}
351    </example>
352  </section>
353  <section>
354    <heading>Filter services</heading>
355    <p>
356      A filter service is a special case of an inter-application service.
357      Its action is to take data of one type and convert it to another
358      type.  Unlike general services, this is not directly initiated by
359      user action clicking on an item in the services menu (indeed, filter
360      services do not appear on the services menu), but is instead performed
361      transparently when the application asks the pasteboard system for
362      data of a particular type, but the pasteboard only contains data of
363      some other type.
364    </p>
365    <p>
366      A filter service definition in the Info.plist file differs from that
367      of a standard service in that the <em>NSMessage</em> entry is replaced
368      by an <em>NSFilter</em> entry, the <em>NSMenuItem</em> and
369      <em>NSKeyEquivalent</em> entries are omitted, and a few other entries
370      may be added -
371    </p>
372    <deflist>
373      <term>NSFilter</term>
374      <desc>
375	This is the first part of the message name for the method
376	which actually implements the filter service ... just like
377	the NSMessage entry in a normal service.
378      </desc>
379      <term>NSInputMechanism</term>
380      <desc>
381	This (optional) entry is a string value specifying an alternative
382	mechanism for performing the filer service (instead of sending a
383	message to an application to ask it to do it).<br />
384	Possible values are -
385	<deflist>
386	  <term>NSIdentity</term>
387	  <desc>
388	    The data to be filtered is simply placed upon the pasteboard
389	    without any transformation.
390	  </desc>
391	  <term>NSMapFile</term>
392	  <desc>
393	    The data to be filtered is the name of a file, which is
394	    loaded into memory and placed on the pasteboard without
395	    any transformation.<br />
396	    If the data to be filtered contains multiple file names,
397	    only the first is used.
398	  </desc>
399	  <term>NSUnixStdio</term>
400	  <desc>
401	    The data to be filtered is the name of a file, which is
402	    passed as the argument to a unix command-line program,
403	    and the standard output of that program is captured and
404	    placed on the pasteboard.  The program is run each time
405	    data is requested, so this is inefficient in comparison
406	    to a filter implemented using the standard method (of
407	    sending a message to a running application).<br />
408	    If the data to be filtered contains multiple file names,
409	    only the first is used.
410	  </desc>
411	</deflist>
412      </desc>
413    </deflist>
414    <p>
415      Filter services are used implicitly whenever you get a pasteboard
416      by using one of the methods +pasteboardByFilteringData:ofType:,
417      +pasteboardByFilteringFile: or +pasteboardByFilteringTypesInPasteboard:
418      as the pasteboard system will automatically invoke any available
419      filter to convert the data in the pasteboard to any required
420      type as long as a conversion can be done using a single filter.
421    </p>
422  </section>
423  <section>
424    <heading>Distributed Objects services</heading>
425    <p>
426      While the general <em>services</em> mechanism described above
427      covers most eventualities, there are some circumstances where
428      you might want your application to offer more complex services
429      which require the client application to have been written to
430      make use of those services and where the interaction between
431      the two is much trickier.
432    </p>
433    <p>
434      In most cases, such situations are handled by server processes
435      rather than GUI applications, thus avoiding all the overheads
436      of a GUI application ... linking with the GUI library and
437      using the windowing system etc.  On occasion you may actually
438      want the services to use facilities from the GUI library
439      (such as the [NSPasteboard] or [NSWorkspace] class).
440    </p>
441    <p>
442      Traditionally, NeXTstep and GNUstep applications permit you to
443      connect to an application using the standard [NSConnection]
444      mechanisms, with the name of the port you connect to being
445      (by convention) the name of the application.  The root proxy
446      of the NSConnection obtained this way would be the
447      [NSApplication-delegate] object, and any messages sent to
448      this object would be handled by the application delegate.
449    </p>
450    <p>
451      In the interests of security, GNUstep provides a mechanism to
452      ensure that <em>only</em> those methods you explicitly want to
453      be available to remote processes are actually available.<br />
454      Those methods are assumed to be any of the standard application
455      methods, and any methods implementing the standard <em>services</em>
456      mechanism (ie. methods whose names begin <code>application:</code>
457      or end with <code>:userData:error:</code>), plus any methods
458      listed in the array returned by the
459      <code>GSPermittedMessages</code> user default.<br />
460      If your application wishes to make non-standard methods available,
461      it should use [NSUserDefaults-registerDefaults:] to set a standard
462      value for GSPermittedMessages.  Users of the application can then
463      use the defaults system to override that standard setting for the
464      application in order to reduce or increase the list of messages
465      available to remote processes.
466    </p>
467    <p>
468      To make use of a service, you need to check to ensure that the
469      application providing the service is running, connect to it,
470      and then send messages to it.  You should take care to catch
471      exceptions and deal with a loss of connection to the server
472      application.<br />
473      As an aid to using the services, GNUstep provides a helper function
474      (GSContactApplication()) which encapsulates the process of
475      establishing a connection and
476      launching the server application if necessary.
477    </p>
478<example>
479  id	proxy = GSContactApplication(@"pathToApp", nil, nil);
480  if (proxy != nil)
481    {
482      NS_EXCEPTION
483	{
484	  id result = [proxy performTask: taskName withArgument: anArgument];
485
486	  if (result == nil)
487	    {
488	      // handle error
489	    }
490	  else
491	    {
492	      // Use result
493	    }
494	}
495      NS_HANDLER
496        // Handle exception
497      NS_ENDHANDLER
498    }
499</example>
500    <p>
501      If we want to send repeated messages, we may store the proxy to
502      server application, and might want to keep track of the state of
503      the connection to be sure that the proxy is still valid.
504    </p>
505<example>
506  ASSIGN(remote, proxy);
507  // We want to keep hold of the proxy for use later, so we need to know
508  // if the connection dies ... we ask for a notification to call our
509  // connectionBecameInvalid: method when the connection dies ... in that
510  // method we can release the proxy.
511  [[NSNotificationCenter defaultCenter]
512    addObserver: self
513       selector: @selector(connectionBecameInvalid:)
514	   name: NSConnectionDidDieNotification
515	 object: [remote connectionForProxy]];
516</example>
517  </section>
518</chapter>
519*/
520
521#include "config.h"
522#import <Foundation/NSArray.h>
523#import <Foundation/NSData.h>
524#import <Foundation/NSDebug.h>
525#import <Foundation/NSHost.h>
526#import <Foundation/NSDictionary.h>
527#import <Foundation/NSEnumerator.h>
528#import <Foundation/NSConnection.h>
529#import <Foundation/NSDistantObject.h>
530#import <Foundation/NSDistributedNotificationCenter.h>
531#import <Foundation/NSFileManager.h>
532#import <Foundation/NSMapTable.h>
533#import <Foundation/NSNotification.h>
534#import <Foundation/NSException.h>
535#import <Foundation/NSInvocation.h>
536#import <Foundation/NSLock.h>
537#import <Foundation/NSPathUtilities.h>
538#import <Foundation/NSPortCoder.h>
539#import <Foundation/NSPortNameServer.h>
540#import <Foundation/NSProcessInfo.h>
541#import <Foundation/NSSerialization.h>
542#import <Foundation/NSUserDefaults.h>
543#import <Foundation/NSMethodSignature.h>
544#import <Foundation/NSRunLoop.h>
545#import <Foundation/NSSet.h>
546#import <Foundation/NSTask.h>
547#import <Foundation/NSTimer.h>
548#import <GNUstepBase/NSTask+GNUstepBase.h>
549#import "AppKit/NSPasteboard.h"
550#import "AppKit/NSApplication.h"
551#import "AppKit/NSWorkspace.h"
552#import "AppKit/NSFileWrapper.h"
553#import "GNUstepGUI/GSServicesManager.h"
554#import "GNUstepGUI/GSPasteboardServer.h"
555
556/*
557 * FIXME
558 * We should learn to handle
559 * NSPasteboardTypePNG
560 * NSPasteboardTypeSound
561 * NSPasteboardTypeMultipleTextSelection
562 * NSPasteboardTypeTextFinderOptions
563 */
564
565static NSString	*contentsPrefix = @"NSTypedFileContentsPboardType:";
566static NSString	*namePrefix = @"NSTypedFilenamesPboardType:";
567
568/* This is a proxy used to send objects over DO by reference when they
569 * would normally be copied.
570 * The idea is to use such a proxy when a filter sends data to a pasteboard.
571 * Since the filtered data will only be used in the process which sets up
572 * the filter, there's no point sending it on a round trip to the
573 * pasteboard server and back ... instead we encode a GSByrefObject, which
574 * appears as a proxy/reference on the remote system (pasteboard server)
575 * but when the pasteboard server sends it back it decodes locally as the
576 * original data object.
577 */
578@interface GSByrefObject : NSObject
579{
580  NSObject	*target;
581}
582+ (id) byrefWithObject: (NSObject*)object;
583@end
584
585@implementation	GSByrefObject
586
587+ (id) byrefWithObject: (NSObject*)object
588{
589  GSByrefObject	*b = [GSByrefObject new];
590  b->target = [object retain];
591  return [b autorelease];
592}
593
594- (void) dealloc
595{
596  [target release];
597  [super dealloc];
598}
599
600- (void) forwardInvocation: (NSInvocation*)anInvocation
601{
602  [anInvocation invokeWithTarget: target];
603}
604
605- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
606{
607  if (class_respondsToSelector(object_getClass(self), aSelector))
608    {
609      return [super methodSignatureForSelector: aSelector];
610    }
611  return [target methodSignatureForSelector: aSelector];
612}
613
614/* Encode this proxy as a reference to its target.
615 * That way when the reference is passed back to this process it
616 * will decode as the original target object rather than the proxy.
617 */
618- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
619{
620  return [NSDistantObject proxyWithLocal: target
621			      connection: [aCoder connection]];
622}
623
624@end
625
626/*
627 * A pasteboard class for lazily filtering data
628 */
629@interface GSFiltered : NSPasteboard
630{
631@public
632  NSArray	*originalTypes;
633  NSString	*file;
634  NSData	*data;
635  NSPasteboard	*pboard;
636}
637@end
638
639@implementation	GSFiltered
640
641/**
642 * Given an array of types, produce an array of all the types we can
643 * make from that using a single filter.
644 */
645+ (NSArray*) _typesFilterableFrom: (NSArray*)from
646{
647  NSMutableSet	*types = [NSMutableSet setWithCapacity: 8];
648  NSArray	*filters = [[GSServicesManager manager] filters];
649  unsigned	c = [filters count];
650  unsigned 	i;
651
652  for (i = 0; i < [from count]; i++)
653    {
654      NSString	*type = [from objectAtIndex: i];
655      unsigned 	j;
656
657      [types addObject: type];	// Always include original type
658
659      for (j = 0; j < c; j++)
660	{
661	  NSDictionary	*info = [filters objectAtIndex: j];
662	  NSArray	*sendTypes = [info objectForKey: @"NSSendTypes"];
663
664	  if ([sendTypes containsObject: type] == YES)
665	    {
666	      NSArray	*returnTypes = [info objectForKey: @"NSReturnTypes"];
667
668	      [types addObjectsFromArray: returnTypes];
669	    }
670	}
671    }
672  return [types allObjects];
673}
674
675- (void) dealloc
676{
677  DESTROY(originalTypes);
678  DESTROY(file);
679  DESTROY(data);
680  DESTROY(pboard);
681  [super dealloc];
682}
683
684/**
685 * This method actually performs any filtering required.
686 */
687- (void) pasteboard: (NSPasteboard*)sender
688 provideDataForType: (NSString*)type
689{
690  NSDictionary	*info;
691  NSString	*fromType = nil;
692  NSString	*mechanism;
693
694  NSAssert(sender == self, NSInvalidArgumentException);
695
696  /*
697   * If the requested type is the same as one of the original types,
698   * no filtering is required ... and we can just write what we have.
699   */
700  if ([originalTypes containsObject: type] == YES)
701    {
702      info = [NSDictionary dictionaryWithObjectsAndKeys:
703	@"NSIdentity", @"NSInputMechanism",
704        nil];
705    }
706  else
707    {
708      NSArray	*filters;
709      unsigned	count;
710      unsigned	filterNumber = 0;
711
712      /*
713       * Locate the filter information needed, including the type we are
714       * converting from and the name of the filter to use.
715       */
716      info = nil;
717      filters = [[GSServicesManager manager] filters];
718      count = [filters count];
719      while (fromType == nil && filterNumber < count)
720	{
721	  NSArray	*returnTypes;
722
723	  info = [filters objectAtIndex: filterNumber++];
724	  returnTypes = [info objectForKey: @"NSReturnTypes"];
725
726	  if ([returnTypes containsObject: type] == YES)
727	    {
728	      NSArray	*sendTypes = [info objectForKey: @"NSSendTypes"];
729	      unsigned 	i;
730
731	      for (i = 0; i < [originalTypes count]; i++)
732		{
733		  fromType = [originalTypes objectAtIndex: i];
734		  if ([sendTypes containsObject: fromType] == YES)
735		    {
736		      break;
737		    }
738		  fromType = nil;
739		}
740	    }
741	}
742      if (!info)
743	{
744	  NSWarnMLog(@"Unable to provide data of type '%@'.", type);
745	  return;
746	}
747    }
748
749  mechanism = [info objectForKey: @"NSInputMechanism"];
750
751  if ([mechanism isEqualToString: @"NSUnixStdio"] == YES)
752    {
753      NSMutableData	*m = [NSMutableData dataWithCapacity: 1023];
754      NSString		*filename;
755      NSString		*path;
756      NSData		*d;
757      NSPipe		*p;
758      NSFileHandle	*h;
759      NSTask		*t;
760      id		o;
761
762      /*
763       * The data for an NSUnixStdio filter must be one or more filenames
764       */
765      if ([fromType isEqualToString: NSStringPboardType] == NO
766	&& [fromType isEqualToString: NSFilenamesPboardType] == NO
767	&& [fromType hasPrefix: namePrefix] == NO)
768	{
769	  [sender setData: [NSData data] forType: type];
770	  return;	// Not the name of a file to filter.
771	}
772
773      if (file != nil)
774	{
775	  filename = file;
776	}
777      else
778        {
779          if (data != nil)
780            {
781              d = data;
782            }
783          else
784            {
785              d = [pboard dataForType: fromType];
786            }
787
788          o = [NSDeserializer deserializePropertyListFromData: d
789                                            mutableContainers: NO];
790          if ([o isKindOfClass: [NSString class]] == YES)
791            {
792              filename = o;
793            }
794          else if ([o isKindOfClass: [NSArray class]] == YES
795	    && [o count] > 0
796	    && [[o objectAtIndex: 0] isKindOfClass: [NSString class]] == YES)
797            {
798              filename = [o objectAtIndex: 0];
799            }
800          else
801            {
802              [sender setData: [NSData data] forType: type];
803              return;	// Not the name of a file to filter.
804            }
805        }
806
807      /*
808       * Set up and launch task to filter the named file.
809       */
810      t = [NSTask new];
811      path = [info objectForKey: @"NSExecutable"];
812      if ([path length] == 0)
813	{
814	  path = [info objectForKey: @"NSPortName"];
815	}
816      [t setLaunchPath: path];
817      [t setArguments: [NSArray arrayWithObject: filename]];
818      p = [NSPipe pipe];
819      [t setStandardOutput: p];
820      [t launch];
821
822      /*
823       * Read all the data that the task writes.
824       */
825      h = [p fileHandleForReading];
826      while ([(d = [h availableData]) length] > 0)
827	{
828	  [m appendData: d];
829	}
830      [t waitUntilExit];
831      RELEASE(t);
832
833      /*
834       * And send it on.
835       */
836      [sender setData: [GSByrefObject byrefWithObject: m] forType: type];
837    }
838  else if ([mechanism isEqualToString: @"NSMapFile"] == YES)
839    {
840      NSString	*filename;
841      NSData	*d;
842      id	o;
843
844      if ([fromType isEqualToString: NSStringPboardType] == NO
845	&& [fromType isEqualToString: NSFilenamesPboardType] == NO
846	&& [fromType hasPrefix: namePrefix] == NO)
847	{
848	  [sender setData: [NSData data] forType: type];
849	  return;	// Not the name of a file to filter.
850	}
851
852      /* TODO: d used to be used here before being initialized. Set it to
853      nil and warn instead of crashing for now. */
854      d = nil;
855      NSWarnMLog(@"NSMapFile handling is broken.");
856
857      o = [NSDeserializer deserializePropertyListFromData: d
858					mutableContainers: NO];
859      if ([o isKindOfClass: [NSString class]] == YES)
860	{
861	  filename = o;
862	}
863      else if ([o isKindOfClass: [NSArray class]] == YES
864	&& [o count] > 0
865	&& [[o objectAtIndex: 0] isKindOfClass: [NSString class]] == YES)
866	{
867	  filename = [o objectAtIndex: 0];
868	}
869      else
870	{
871	  [sender setData: [NSData data] forType: type];
872	  return;	// Not the name of a file to filter.
873	}
874
875      d = [NSData dataWithContentsOfFile: filename];
876
877      [sender setData: [GSByrefObject byrefWithObject: d] forType: type];
878    }
879  else if ([mechanism isEqualToString: @"NSIdentity"] == YES)
880    {
881      /*
882       * An 'identity' filter simply places the required data on the
883       * pasteboard.
884       */
885      if (data != nil)
886	{
887	  [sender setData: data forType: type];
888	}
889      else if (file != nil)
890	{
891	  [sender writeFileContents: file];
892	}
893      else
894	{
895	  NSData	*d = [pboard dataForType: type];
896
897	  [sender setData: [GSByrefObject byrefWithObject: d] forType: type];
898	}
899    }
900  else
901    {
902      NSPasteboard	*tmp;
903      NSString		*port;
904      NSString		*timeout;
905      double		seconds;
906      NSDate		*finishBy;
907      NSString		*appPath;
908      id		provider;
909      NSString		*message;
910      NSString		*selName;
911      NSString		*userData;
912      NSString		*error = nil;
913
914      /*
915       * Put data onto a pasteboard that can be used by the service provider.
916       */
917      if (data != nil)
918	{
919	  tmp = [NSPasteboard pasteboardWithUniqueName];
920	  [tmp declareTypes: [NSArray arrayWithObject: fromType] owner: nil];
921	  [tmp setData: data forType: fromType];
922	}
923      else if (file != nil)
924	{
925	  tmp = [NSPasteboard pasteboardWithUniqueName];
926	  [tmp declareTypes: [NSArray arrayWithObject: fromType] owner: nil];
927	  [tmp writeFileContents: file];
928	}
929      else
930	{
931	  tmp = pboard;		// Already in a pasteboard.
932	}
933
934      port = [info objectForKey: @"NSPortName"];
935      timeout = [info objectForKey: @"NSTimeout"];
936      if (timeout && [timeout floatValue] > 100)
937	{
938	  seconds = [timeout floatValue] / 1000.0;
939	}
940      else
941	{
942	  seconds = 30.0;
943	}
944      finishBy = [NSDate dateWithTimeIntervalSinceNow: seconds];
945      appPath = [info objectForKey: @"NSExecutable"];
946      if ([appPath length] > 0)
947	{
948	  /*
949	   * A relative path for NSExecutable is relative to the bundle.
950	   */
951	  if ([appPath isAbsolutePath] == NO)
952	    {
953	      NSString	*bundlePath = [info objectForKey: @"ServicePath"];
954
955	      appPath = [bundlePath stringByAppendingPathComponent: appPath];
956	    }
957	}
958      else
959	{
960	  appPath = [info objectForKey: @"ServicePath"];
961	}
962      userData = [info objectForKey: @"NSUserData"];
963      message = [info objectForKey: @"NSFilter"];
964      selName = [message stringByAppendingString: @":userData:error:"];
965
966      /*
967       * Locate the service provider ... this will be a proxy to the remote
968       * object, or a local object (if we provide the service ourself)
969       */
970      provider = GSContactApplication(appPath, port, finishBy);
971      if (provider == nil)
972	{
973	  NSLog(@"Failed to contact service provider at '%@' '%@'",
974	    appPath, port);
975	  return;
976	}
977
978      /*
979       * If the service provider is a remote object, we can set timeouts on
980       * the NSConnection so we don't hang waiting for it to reply.
981       */
982      if ([provider isProxy] == YES)
983	{
984	  NSConnection	*connection;
985
986	  connection = [(NSDistantObject*)provider connectionForProxy];
987	  [connection enableMultipleThreads];
988	  seconds = [finishBy timeIntervalSinceNow];
989	  [connection setRequestTimeout: seconds];
990	  [connection setReplyTimeout: seconds];
991	}
992
993      /*
994       * At last, we ask for the service to be performed.
995       */
996      NS_DURING
997	{
998	  SEL			sel;
999	  NSMethodSignature	*sig;
1000
1001	  sel = NSSelectorFromString(selName);
1002	  sig = [provider methodSignatureForSelector: sel];
1003	  if (sig != nil)
1004	    {
1005	      NSInvocation	*inv;
1006	      NSString		**errPtr = &error;
1007
1008	      inv = [NSInvocation invocationWithMethodSignature: sig];
1009	      [inv setTarget: provider];
1010	      [inv setSelector: sel];
1011	      [inv setArgument: (void*)&tmp atIndex: 2];
1012	      [inv setArgument: (void*)&userData atIndex: 3];
1013	      [inv setArgument: (void*)&errPtr atIndex: 4];
1014	      [inv invoke];
1015	    }
1016	  else
1017	    {
1018	      error = @"No remote object to handle filter";
1019	    }
1020	}
1021      NS_HANDLER
1022	{
1023	  error = [NSString stringWithFormat: @"%@", [localException reason]];
1024	}
1025      NS_ENDHANDLER
1026
1027      if (error != nil)
1028	{
1029	  NSLog(@"Failed to contact service provider for '%@': %@",
1030	    appPath, error);
1031	  return;
1032	}
1033
1034      /*
1035       * Finally, make it available.
1036       */
1037      [sender setData: [GSByrefObject byrefWithObject: [tmp dataForType: type]]
1038	      forType: type];
1039    }
1040}
1041
1042@end
1043
1044
1045
1046@interface NSPasteboard (Private)
1047+ (void) _localServer: (id<GSPasteboardSvr>)s;
1048+ (id) _lostServer: (NSNotification*)notification;
1049+ (id<GSPasteboardSvr>) _pbs;
1050+ (NSPasteboard*) _pasteboardWithTarget: (id<GSPasteboardObj>)aTarget
1051				   name: (NSString*)aName;
1052- (id) _target;
1053@end
1054
1055/**
1056 * <p>The pasteboard system is the primary mechanism for data exchange
1057 * between OpenStep applications.  It is used for cut and paste of data,
1058 * as the exchange mechanism for <em>services</em> (as listed on the
1059 * services menu), for communicating with a spelling server in order to
1060 * perform spell checking, and for <em>filter services</em> which convert
1061 * data of one type to another transparently.
1062 * </p>
1063 * <p>Pasteboards are identified by names, some of which are standard
1064 * and are intended to exist permanently and be shared between all
1065 * applications, others are temporary or private and are used to handle
1066 * specific services.
1067 * </p>
1068 * <p>All data transferred to/from pasteboards is <em>typed</em>.  Mostly
1069 * using one of several standard types for common data or using standardised
1070 * names which identify particular kinds of files and their contents
1071 * (see the NSCreateFileContentsPboardType() an
1072 * NSCreateFilenamePboardType() functions for details).  It is also possible
1073 * for cooperating applications to use their own private types ... any string
1074 * value will do.
1075 * </p>
1076 * <p>Each pasteboard has an <em>owner</em> ... an object which declares the
1077 * types of data it can provide.  Unless versions of the pasteboard data
1078 * corresponding to all the declared types are written to the pasteboard,
1079 * the owner is responsible for producing the data for the pasteboard when
1080 * it is called for (lazy provision of data).<br />
1081 * The pasteboard owner needs to implement the methods of the
1082 * NSPasteboardOwner informal protocol in order to do this.
1083 * </p>
1084 */
1085@implementation NSPasteboard
1086
1087static	NSRecursiveLock		*dictionary_lock = nil;
1088static	NSMapTable		*pasteboards = 0;
1089static	id<GSPasteboardSvr>	the_server = nil;
1090static  NSMapTable              *mimeMap = NULL;
1091
1092/**
1093 * Returns the general pasteboard found by calling +pasteboardWithName:
1094 * with NSGeneralPboard as the name.
1095 */
1096+ (NSPasteboard*) generalPasteboard
1097{
1098  static NSPasteboard *generalPboard = nil;
1099  NSPasteboard *currentGeneralPboard;
1100
1101  // call pasteboardWithName: every time, to update server connection if needed
1102  currentGeneralPboard = [self pasteboardWithName: NSGeneralPboard];
1103  if (currentGeneralPboard != generalPboard)
1104    {
1105      ASSIGN(generalPboard, currentGeneralPboard);
1106    }
1107  return generalPboard;
1108}
1109
1110+ (void) initialize
1111{
1112  if (self == [NSPasteboard class])
1113    {
1114      // Initial version
1115      [self setVersion: 1];
1116      dictionary_lock = [[NSRecursiveLock alloc] init];
1117      pasteboards = NSCreateMapTable (NSObjectMapKeyCallBacks,
1118	NSNonRetainedObjectMapValueCallBacks, 0);
1119    }
1120}
1121
1122/**
1123 * <p>Creates and returns a pasteboard from which the data in the named
1124 * file can be read in all the types to which it can be converted by
1125 * filter services.<br />
1126 * The type of data in the file is inferred from the file extension.
1127 * </p>
1128 * <p>No filtering is actually performed until some object asks the
1129 * pasteboard for the data, so calling this method is quite inexpensive.
1130 * </p>
1131 */
1132+ (NSPasteboard*) pasteboardByFilteringData: (NSData*)data
1133				     ofType: (NSString*)type
1134{
1135  GSFiltered	*p;
1136  NSArray	*types;
1137  NSArray	*originalTypes;
1138
1139  originalTypes = [NSArray arrayWithObject: type];
1140  types = [GSFiltered _typesFilterableFrom: originalTypes];
1141  p = (GSFiltered*)[GSFiltered pasteboardWithUniqueName];
1142  p->originalTypes = [originalTypes copy];
1143  p->data = [data copy];
1144  [p declareTypes: types owner: p];
1145  return p;
1146}
1147
1148/**
1149 * <p>Creates and returns a pasteboard from which the data in the named
1150 * file can be read in all the types to which it can be converted by
1151 * filter services.<br />
1152 * The type of data in the file is inferred from the file extension.
1153 * </p>
1154 */
1155+ (NSPasteboard*) pasteboardByFilteringFile: (NSString*)filename
1156{
1157  GSFiltered	*p;
1158  NSString	*ext = [filename pathExtension];
1159  NSArray	*types;
1160  NSArray	*originalTypes;
1161
1162  if ([ext length] > 0)
1163    {
1164      originalTypes = [NSArray arrayWithObjects:
1165	NSCreateFilenamePboardType(ext),
1166	NSFilenamesPboardType,
1167	nil];
1168    }
1169  else
1170    {
1171      originalTypes = [NSArray arrayWithObject: NSFilenamesPboardType];
1172    }
1173  types = [GSFiltered _typesFilterableFrom: originalTypes];
1174  p = (GSFiltered*)[GSFiltered pasteboardWithUniqueName];
1175  p->originalTypes = [originalTypes copy];
1176  p->file = [filename copy];
1177  [p declareTypes: types owner: p];
1178  return p;
1179}
1180
1181/**
1182 * <p>Creates and returns a pasteboard where the data contained in pboard
1183 * is available for reading in as many types as it can be converted to by
1184 * available filter services.  This normally expands on the range of types
1185 * available in pboard.
1186 * </p>
1187 * <p>NB. This only permits a single level of filtering ... if pboard was
1188 * previously returned by another filtering method, it is returned instead
1189 * of a new pasteboard.
1190 * </p>
1191 */
1192+ (NSPasteboard*) pasteboardByFilteringTypesInPasteboard: (NSPasteboard*)pboard
1193{
1194  GSFiltered	*p;
1195  NSArray		*types;
1196  NSArray		*originalTypes;
1197
1198  if ([pboard isKindOfClass: [GSFiltered class]] == YES)
1199    {
1200      return pboard;
1201    }
1202  originalTypes = [pboard types];
1203  types = [GSFiltered _typesFilterableFrom: originalTypes];
1204  p = (GSFiltered*)[GSFiltered pasteboardWithUniqueName];
1205  p->originalTypes = [originalTypes copy];
1206  p->pboard = RETAIN(pboard);
1207  [p declareTypes: types owner: p];
1208  return p;
1209}
1210
1211/**
1212 * <p>Returns the pasteboard for the specified name.  Creates a new pasteboard
1213 * if (and only if) one with the given name does not exist.
1214 * </p>
1215 * Standard pasteboard names are -
1216 * <list>
1217 *   <item><ref type="variable" id="NSGeneralPboard">
1218 *   NSGeneralPboard</ref></item>
1219 *   <item><ref type="variable" id="NSFontPboard">
1220 *   NSFontPboard</ref></item>
1221 *   <item><ref type="variable" id="NSRulerPboard">
1222 *   NSRulerPboard</ref></item>
1223 *   <item><ref type="variable" id="NSFindPboard">
1224 *   NSFindPboard</ref></item>
1225 *   <item><ref type="variable" id="NSDragPboard">
1226 *   NSDragPboard</ref></item>
1227 * </list>
1228 */
1229+ (NSPasteboard*) pasteboardWithName: (NSString*)aName
1230{
1231  NS_DURING
1232    {
1233      id<GSPasteboardObj>	anObj;
1234
1235      anObj = [[self _pbs] pasteboardWithName: aName];
1236      if (anObj != nil)
1237	{
1238	  NSPasteboard	*ret;
1239
1240	  if ([(id)anObj isProxy] == YES)
1241	    {
1242	      Protocol	*p = @protocol(GSPasteboardObj);
1243
1244	      [(id)anObj setProtocolForProxy: p];
1245	    }
1246	  ret = [self _pasteboardWithTarget: anObj name: aName];
1247	  NS_VALRETURN(ret);
1248	}
1249    }
1250  NS_HANDLER
1251    {
1252      [NSException raise: NSPasteboardCommunicationException
1253		  format: @"%@", [localException reason]];
1254    }
1255  NS_ENDHANDLER
1256
1257  return nil;
1258}
1259
1260/**
1261 * Creates and returns a new pasteboard with a name guaranteed to be unique
1262 * within the pasteboard server.
1263 */
1264+ (NSPasteboard*) pasteboardWithUniqueName
1265{
1266  NS_DURING
1267    {
1268      id<GSPasteboardObj>	anObj;
1269
1270      anObj = [[self _pbs] pasteboardWithName: nil];
1271      if (anObj)
1272	{
1273	  NSString	*aName;
1274
1275	  aName = [anObj name];
1276	  if (aName)
1277	    {
1278	      NSPasteboard	*ret;
1279
1280	      ret = [self _pasteboardWithTarget: anObj name: aName];
1281	      NS_VALRETURN(ret);
1282	    }
1283	}
1284    }
1285  NS_HANDLER
1286    {
1287      [NSException raise: NSPasteboardCommunicationException
1288		  format: @"%@", [localException reason]];
1289    }
1290  NS_ENDHANDLER
1291
1292  return nil;
1293}
1294
1295/**
1296 * Returns an array of the types from which data of the specified type
1297 * can be produced by registered filter services.<br />
1298 * The original type is always present in this array.<br />
1299 * Raises an exception if type is nil.
1300 */
1301+ (NSArray*) typesFilterableTo: (NSString*)type
1302{
1303  NSMutableSet	*types = [NSMutableSet setWithCapacity: 8];
1304  NSArray	*filters = [[GSServicesManager manager] filters];
1305  NSEnumerator	*enumerator = [filters objectEnumerator];
1306  NSDictionary	*info;
1307
1308  [types addObject: type];	// Always include original type
1309
1310  /*
1311   * Step through the filters looking for those which handle the type
1312   */
1313  while ((info = [enumerator nextObject]) != nil)
1314    {
1315      NSArray	*returnTypes = [info objectForKey: @"NSReturnTypes"];
1316
1317      if ([returnTypes containsObject: type] == YES)
1318	{
1319	  NSArray	*sendTypes = [info objectForKey: @"NSSendTypes"];
1320
1321	  [types addObjectsFromArray: sendTypes];
1322	}
1323    }
1324
1325  return [types allObjects];
1326}
1327
1328/**
1329 * <p>Adds newTypes to the pasteboard and declares newOwner to be the owner
1330 * of the pasteboard.  Use only after -declareTypes:owner: has been called
1331 * for the same owner, because the new owner may not support all the types
1332 * declared by a previous owner.
1333 * </p>
1334 * <p>Returns the new change count for the pasteboard, or zero if an error
1335 * occurs.
1336 * </p>
1337 */
1338- (int) addTypes: (NSArray*)newTypes
1339	   owner: (id)newOwner
1340{
1341  int	count = 0;
1342
1343  NS_DURING
1344    {
1345      count = [target addTypes: newTypes
1346			 owner: newOwner
1347		    pasteboard: self
1348		      oldCount: changeCount];
1349      if (count > 0)
1350	{
1351	  changeCount = count;
1352	}
1353    }
1354  NS_HANDLER
1355    {
1356      count = 0;
1357      [NSException raise: NSPasteboardCommunicationException
1358		  format: @"%@", [localException reason]];
1359    }
1360  NS_ENDHANDLER
1361  return count;
1362}
1363
1364/**
1365 * <p>Sets the owner of the pasteboard to be newOwner and declares newTypes
1366 * as the types of data supported by it.<br />
1367 * This invalidates existing data in the pasteboard (except where the GNUstep
1368 * -setHistory: extension allows multi-version data to be held).
1369 * </p>
1370 * <p>The value of newOwner may be nil, but if it is, data should
1371 * immediately be written to the pasteboard for all the value in newTypes
1372 * as a nil owner cannot be used for lazy supply of data.
1373 * </p>
1374 * <p>This increments the change count for the pasteboard and the new
1375 * count is returned, or zero is returned if an error occurs.<br />
1376 * Where -setChangeCount: has been used, the highest count to date
1377 * is incremented and returned, rather than the last value specified
1378 * by the -setChangeCount: method.
1379 * </p>
1380 * <p>The types you declare can be arbitrary strings, but as at least two
1381 * applications really need to be aware of the same type for it to be
1382 * of use, it is much more normal to use a predefined (standard) type
1383 * or a type representing the name or content of a particular kind of
1384 * file (returned by the NSCreateFilenamePboardType() or
1385 * NSCreateFilenamePboardType() function).<br />
1386 * The standard type for raw data is
1387 * <ref type="variable" id="NSGeneralPboardType">NSGeneralPboardType</ref>
1388 * </p>
1389 * The predefined pasteboard types are -
1390 * <list>
1391 *   <item><ref type="variable" id="NSStringPboardType">
1392 *   NSStringPboardType</ref></item>
1393 *   <item><ref type="variable" id="NSColorPboardType">
1394 *   NSColorPboardType</ref></item>
1395 *   <item><ref type="variable" id="NSFileContentsPboardType">
1396 *   NSFileContentsPboardType</ref></item>
1397 *   <item><ref type="variable" id="NSFilenamesPboardType">
1398 *   NSFilenamesPboardType</ref></item>
1399 *   <item><ref type="variable" id="NSFontPboardType">
1400 *   NSFontPboardType</ref></item>
1401 *   <item><ref type="variable" id="NSRulerPboardType">
1402 *   NSRulerPboardType</ref></item>
1403 *   <item><ref type="variable" id="NSPostScriptPboardType">
1404 *   NSPostScriptPboardType</ref></item>
1405 *   <item><ref type="variable" id="NSTabularTextPboardType">
1406 *   NSTabularTextPboardType</ref></item>
1407 *   <item><ref type="variable" id="NSRTFPboardType">
1408 *   NSRTFPboardType</ref></item>
1409 *   <item><ref type="variable" id="NSRTFDPboardType">
1410 *   NSRTFDPboardType</ref></item>
1411 *   <item><ref type="variable" id="NSTIFFPboardType">
1412 *   NSTIFFPboardType</ref></item>
1413 *   <item><ref type="variable" id="NSDataLinkPboardType">
1414 *   NSDataLinkPboardType</ref></item>
1415 *   <item><ref type="variable" id="NSGeneralPboardType">
1416 *   NSGeneralPboardType</ref></item>
1417 *   <item><ref type="variable" id="NSPDFPboardType">
1418 *   NSPDFPboardType</ref></item>
1419 *   <item><ref type="variable" id="NSPICTPboardType">
1420 *   NSPICTPboardType</ref></item>
1421 *   <item><ref type="variable" id="NSURLPboardType">
1422 *   NSURLPboardType</ref></item>
1423 *   <item><ref type="variable" id="NSHTMLPboardType">
1424 *   NSHTMLPboardType</ref></item>
1425 * </list>
1426 */
1427- (int) declareTypes: (NSArray*)newTypes
1428	       owner: (id)newOwner
1429{
1430  NS_DURING
1431    {
1432      changeCount = [target declareTypes: newTypes
1433				   owner: newOwner
1434			      pasteboard: self];
1435    }
1436  NS_HANDLER
1437    {
1438      [NSException raise: NSPasteboardCommunicationException
1439		  format: @"%@", [localException reason]];
1440    }
1441  NS_ENDHANDLER
1442  return changeCount;
1443}
1444
1445- (void) dealloc
1446{
1447  DESTROY(target);
1448  [dictionary_lock lock];
1449  if (NSMapGet(pasteboards, (void*)name) == (void*)self)
1450    {
1451      NSMapRemove(pasteboards, (void*)name);
1452    }
1453  DESTROY(name);
1454  [dictionary_lock unlock];
1455  [super dealloc];
1456}
1457
1458- (NSString*) description
1459{
1460  return [NSString stringWithFormat: @"%@ %@ %p",
1461    [super description], name, target];
1462}
1463
1464/**
1465 * Encode for DO by using just our name.
1466 */
1467- (void) encodeWithCoder: (NSCoder*)aCoder
1468{
1469  [aCoder encodeObject: name];
1470}
1471
1472/**
1473 * Decode from DO by creating a new pasteboard with the decoded name.
1474 */
1475- (id) initWithCoder: (NSCoder*)aCoder
1476{
1477  NSString	*n = [aCoder decodeObject];
1478  NSPasteboard	*p = [[self class] pasteboardWithName: n];
1479
1480  ASSIGN(self, p);
1481  return self;
1482}
1483
1484/**
1485 * Returns the pasteboard name (as given to +pasteboardWithName:)
1486 * for the receiver.
1487 */
1488- (NSString*) name
1489{
1490  return name;
1491}
1492
1493/**
1494 * Releases the receiver in the pasteboard server so that no other application
1495 * can use the pasteboard.  This should not be called for any of the standard
1496 * pasteboards, only for temporary ones.
1497 */
1498- (void) releaseGlobally
1499{
1500  if ([name isEqualToString: NSGeneralPboard] == YES
1501    || [name isEqualToString: NSFontPboard] == YES
1502    || [name isEqualToString: NSRulerPboard] == YES
1503    || [name isEqualToString: NSFindPboard] == YES
1504    || [name isEqualToString: NSDragPboard] == YES)
1505    {
1506      [NSException raise: NSGenericException
1507		  format: @"Illegal attempt to globally release %@", name];
1508    }
1509  [target releaseGlobally];
1510  [dictionary_lock lock];
1511  if (NSMapGet(pasteboards, (void*)name) == (void*)self)
1512    {
1513      NSMapRemove(pasteboards, (void*)name);
1514    }
1515  [dictionary_lock unlock];
1516}
1517
1518/**
1519 * Pasteboards sent over DO should always be copied so that a local
1520 * instance is created to communicate with the pasteboard server.
1521 */
1522- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
1523{
1524  if ([self class] == [NSPasteboard class])
1525    {
1526      return self;	// Always encode bycopy.
1527    }
1528
1529/* But ... if this is actually a filter rather than a 'real' pasteboard,
1530 * we don't want it copied to the pasteboard server.
1531 */
1532 if ([self class] == [GSFiltered class])
1533   {
1534     return [super replacementObjectForPortCoder: aCoder];
1535   }
1536
1537  return [super replacementObjectForPortCoder: aCoder];
1538}
1539
1540/**
1541 * <p>Writes data of type dataType to the pasteboard server so that other
1542 * applications can read it.  The dataType must be one of the types
1543 * previously declared for the pasteboard.<br />
1544 * All the other methods for writing data to the pasteboard call this one.
1545 * </p>
1546 * <p>Returns YES on success, NO if the data could not be written for some
1547 * reason.
1548 * </p>
1549 */
1550- (BOOL) setData: (NSData*)data
1551	 forType: (NSString*)dataType
1552{
1553  BOOL	ok = NO;
1554
1555  NS_DURING
1556    {
1557      ok = [target setData: data
1558		   forType: dataType
1559		    isFile: NO
1560		  oldCount: changeCount];
1561    }
1562  NS_HANDLER
1563    {
1564      ok = NO;
1565      [NSException raise: NSPasteboardCommunicationException
1566		  format: @"%@", [localException reason]];
1567    }
1568  NS_ENDHANDLER
1569  return ok;
1570}
1571
1572- (BOOL) writeObjects: (NSArray*)objects
1573{
1574  // FIXME: not implemented
1575  return NO;
1576}
1577
1578/**
1579 * <p>Serialises the data in the supplied property list and writes it to the
1580 * pasteboard server using the -setData:forType: method.
1581 * </p>
1582 * <p>Data written using this method can be read by -propertyListForType:
1583 * or, if it was a simple string, by -stringForType:
1584 * </p>
1585 * <p>If the data is retrieved using -dataForType: then it needs to be
1586 * deserialized into a property list.
1587 * </p>
1588 */
1589- (BOOL) setPropertyList: (id)propertyList
1590		 forType: (NSString*)dataType
1591{
1592  NSData	*d = [NSSerializer serializePropertyList: propertyList];
1593
1594  return [self setData: d forType: dataType];
1595}
1596
1597/**
1598 * <p>Writes  string it to the pasteboard server using the
1599 * -setPropertyList:forType: method.
1600 * </p>
1601 * <p>The data may subsequently be read from the receiver using the
1602 * -stringForType: or -propertyListForType: method.
1603 * </p>
1604 * <p>If the data is retrieved using -dataForType: then it needs to be
1605 * deserialized into a property list.
1606 * </p>
1607 */
1608- (BOOL) setString: (NSString*)string
1609	   forType: (NSString*)dataType
1610{
1611  return [self setPropertyList: string forType: dataType];
1612}
1613
1614/**
1615 * <p>Writes the contents of the file filename to the pasteboard server
1616 * after declaring the type NSFileContentsPboardType as well as a type
1617 * based on the file extension (given by the NSCreateFileContentsPboardType()
1618 * function) if those types have not already been declared.<br />
1619 * If the filename has no extension, only NSFileContentsPboardType is used.
1620 * </p>
1621 * <p>Data written to a pasteboard by this method should be read using
1622 * the -readFileContentsType:toFile: or -readFileWrapper method.
1623 * </p>
1624 * <p>If the data is retrieved using -dataForType: then it needs to be
1625 * deserialized by the NSFileWrapper class.
1626 * </p>
1627 */
1628- (BOOL) writeFileContents: (NSString*)filename
1629{
1630  NSFileWrapper *wrapper;
1631  NSData	*data;
1632  NSArray	*types;
1633  NSString	*ext = [filename pathExtension];
1634  BOOL		ok = NO;
1635
1636  wrapper = [[NSFileWrapper alloc] initWithPath: filename];
1637  data = [wrapper serializedRepresentation];
1638  RELEASE(wrapper);
1639  if ([ext length] > 0)
1640    {
1641      types = [NSArray arrayWithObjects: NSFileContentsPboardType,
1642	NSCreateFileContentsPboardType(ext), nil];
1643    }
1644  else
1645    {
1646      types = [NSArray arrayWithObject: NSFileContentsPboardType];
1647    }
1648  if ([[self types] isEqual: types] == NO)
1649    {
1650      if ([self declareTypes: types owner: owner] == 0)
1651	{
1652	  return NO;	// Unable to declare types.
1653	}
1654    }
1655  NS_DURING
1656    {
1657      ok = [target setData: data
1658		   forType: NSFileContentsPboardType
1659		    isFile: YES
1660		  oldCount: changeCount];
1661    }
1662  NS_HANDLER
1663    {
1664      ok = NO;
1665      [NSException raise: NSPasteboardCommunicationException
1666		  format: @"%@", [localException reason]];
1667    }
1668  NS_ENDHANDLER
1669  return ok;
1670}
1671
1672/**
1673 * <p>Writes the contents of the file wrapper to the pasteboard server
1674 * after declaring the type NSFileContentsPboardType as well as a type
1675 * based on the file extension of the wrappers preferred filename if
1676 * those types have not already been declared.
1677 * </p>
1678 * <p>Raises an exception if there is no preferred filename.
1679 * </p>
1680 * <p>Data written to a pasteboard by this method should be read using
1681 * the -readFileContentsType:toFile: or -readFileWrapper method.
1682 * </p>
1683 * <p>If the data is retrieved using -dataForType: then it needs to be
1684 * deserialized by the NSFileWrapper class.
1685 * </p>
1686 */
1687- (BOOL) writeFileWrapper: (NSFileWrapper *)wrapper
1688{
1689  NSString	*filename = [wrapper preferredFilename];
1690  NSData	*data;
1691  NSArray	*types;
1692  NSString	*ext = [filename pathExtension];
1693  BOOL		ok = NO;
1694
1695  if (filename == nil)
1696    {
1697      [NSException raise: NSInvalidArgumentException
1698		  format: @"Cannot put file on pasteboard with "
1699	@"no preferred filename"];
1700    }
1701  data = [wrapper serializedRepresentation];
1702  if ([ext length] > 0)
1703    {
1704      types = [NSArray arrayWithObjects: NSFileContentsPboardType,
1705	NSCreateFileContentsPboardType(ext), nil];
1706    }
1707  else
1708    {
1709      types = [NSArray arrayWithObject: NSFileContentsPboardType];
1710    }
1711  if ([[self types] isEqual: types] == NO)
1712    {
1713      if ([self declareTypes: types owner: owner] == 0)
1714	{
1715	  return NO;	// Unable to declare types.
1716	}
1717    }
1718  NS_DURING
1719    {
1720      ok = [target setData: data
1721		   forType: NSFileContentsPboardType
1722		    isFile: YES
1723		  oldCount: changeCount];
1724    }
1725  NS_HANDLER
1726    {
1727      ok = NO;
1728      [NSException raise: NSPasteboardCommunicationException
1729		  format: @"%@", [localException reason]];
1730    }
1731  NS_ENDHANDLER
1732  return ok;
1733}
1734
1735/**
1736 * Returns the first type listed in types which the receiver has been
1737 * declared (see -declareTypes:owner:) to support.
1738 */
1739- (NSString*) availableTypeFromArray: (NSArray*)types
1740{
1741  NSString *type = nil;
1742
1743  NS_DURING
1744    {
1745      int	count = 0;
1746
1747      type = [target availableTypeFromArray: types
1748				changeCount: &count];
1749      changeCount = count;
1750    }
1751  NS_HANDLER
1752    {
1753      type = nil;
1754      [NSException raise: NSPasteboardCommunicationException
1755		  format: @"%@", [localException reason]];
1756    }
1757  NS_ENDHANDLER
1758  return type;
1759}
1760
1761/**
1762 * Returns all the types that the receiver has been declared to support.<br />
1763 * See -declareTypes:owner: for details.
1764 */
1765- (NSArray*) types
1766{
1767  NSArray *result = nil;
1768
1769  NS_DURING
1770    {
1771      int	count = 0;
1772
1773      result = [target typesAndChangeCount: &count];
1774      changeCount = count;
1775    }
1776  NS_HANDLER
1777    {
1778      result = nil;
1779      [NSException raise: NSPasteboardCommunicationException
1780		  format: @"%@", [localException reason]];
1781    }
1782  NS_ENDHANDLER
1783  return result;
1784}
1785
1786/**
1787 * Returns the change count for the receiving pasteboard.  This count
1788 * is incremented whenever the owner of the pasteboard is changed.
1789 */
1790- (int) changeCount
1791{
1792  NS_DURING
1793    {
1794      int	count;
1795
1796      count = [target changeCount];
1797      changeCount = count;
1798    }
1799  NS_HANDLER
1800    {
1801      [NSException raise: NSPasteboardCommunicationException
1802		  format: @"%@", [localException reason]];
1803    }
1804  NS_ENDHANDLER
1805  return changeCount;
1806}
1807
1808/**
1809 * Returns data from the pasteboard of the specified dataType, or nil
1810 * if no such data is available.<br />
1811 * May raise an exception if communication with the pasteboard server fails.
1812 */
1813- (NSData*) dataForType: (NSString*)dataType
1814{
1815  NSData	*d = nil;
1816
1817  NS_DURING
1818    {
1819      d = [target dataForType: dataType
1820		     oldCount: changeCount
1821		mustBeCurrent: (useHistory == NO) ? YES : NO];
1822    }
1823  NS_HANDLER
1824    {
1825      d = nil;
1826      [NSException raise: NSPasteboardCommunicationException
1827		  format: @"%@", [localException reason]];
1828    }
1829  NS_ENDHANDLER
1830  return d;
1831}
1832
1833/**
1834 * Calls -dataForType: to obtain data (expected to be a serialized property
1835 * list) and returns the object produced by deserializing it.
1836 */
1837- (id) propertyListForType: (NSString*)dataType
1838{
1839  NSData	*d = [self dataForType: dataType];
1840
1841  if (d)
1842    return [NSDeserializer deserializePropertyListFromData: d
1843					 mutableContainers: NO];
1844  else
1845    return nil;
1846}
1847
1848/**
1849 * <p>Obtains data of the specified dataType from the pasteboard, deserializes
1850 * it to the specified filename and returns the file name (or nil on failure).
1851 * </p>
1852 * <p>This method should only be used to read data written by
1853 * the -writeFileContents: or -writeFileWrapper: method.
1854 * </p>
1855 */
1856- (NSString*) readFileContentsType: (NSString*)type
1857			    toFile: (NSString*)filename
1858{
1859  NSData	*d;
1860  NSFileWrapper *wrapper;
1861
1862  if (type == nil)
1863    {
1864      type = NSCreateFileContentsPboardType([filename pathExtension]);
1865    }
1866  d = [self dataForType: type];
1867  if (d == nil)
1868    {
1869      d = [self dataForType: NSFileContentsPboardType];
1870      if (d == nil)
1871	return nil;
1872    }
1873
1874  wrapper = [[NSFileWrapper alloc] initWithSerializedRepresentation: d];
1875  if ([wrapper writeToFile: filename atomically: NO updateFilenames: NO] == NO)
1876    {
1877      RELEASE(wrapper);
1878      return nil;
1879    }
1880  RELEASE(wrapper);
1881  return filename;
1882}
1883
1884/**
1885 * <p>Obtains data of the specified dataType from the pasteboard, deserializes
1886 * it and returns the resulting file wrapper (or nil).
1887 * </p>
1888 * <p>This method should only be used to read data written by
1889 * the -writeFileContents: or -writeFileWrapper: method.
1890 * </p>
1891 */
1892- (NSFileWrapper*) readFileWrapper
1893{
1894  NSData *d = [self dataForType: NSFileContentsPboardType];
1895
1896  if (d == nil)
1897    return nil;
1898
1899  return
1900    AUTORELEASE([[NSFileWrapper alloc] initWithSerializedRepresentation: d]);
1901}
1902
1903/**
1904 * <p>Obtains data of the specified dataType from the pasteboard, deserializes
1905 * it and returns the resulting string (or nil).
1906 * </p>
1907 * <p>The string should have been written using the -setString:forType: or
1908 * -setPropertyList:forType: method.
1909 * </p>
1910 */
1911- (NSString*) stringForType: (NSString*)dataType
1912{
1913  NSString	*s = [self propertyListForType: dataType];
1914
1915  if ([s isKindOfClass: [NSString class]] == NO)
1916    {
1917      s = nil;
1918    }
1919  return s;
1920}
1921
1922@end
1923
1924@implementation NSPasteboard (Private)
1925
1926/*
1927 *	Special method to use a local server rather than connecting over DO
1928 */
1929+ (void) _localServer: (id<GSPasteboardSvr>)s
1930{
1931  the_server = s;
1932}
1933
1934+ (id) _lostServer: (NSNotification*)notification
1935{
1936  id	obj = the_server;
1937
1938  the_server = nil;
1939  [[NSNotificationCenter defaultCenter]
1940    removeObserver: self
1941	      name: NSConnectionDidDieNotification
1942	    object: [notification object]];
1943  RELEASE(obj);
1944  return self;
1945}
1946
1947+ (id<GSPasteboardSvr>) _pbs
1948{
1949  if (the_server == nil)
1950    {
1951      NSString	*host;
1952      NSString	*description;
1953
1954      host = [[NSUserDefaults standardUserDefaults] stringForKey: @"NSHost"];
1955      if (host == nil)
1956	{
1957	  host = @"";
1958	}
1959      else
1960	{
1961	  NSHost	*h;
1962
1963	  /*
1964	   * If we have a host specified, but it is the current host,
1965	   * we do not need to ask for a host by name (nameserver lookup
1966	   * can be faster) and the empty host name can be used to
1967	   * indicate that we may start a pasteboard server locally.
1968	   */
1969	  h = [NSHost hostWithName: host];
1970	  if (h == nil)
1971	    {
1972	      NSLog(@"Unknown NSHost (%@) ignored", host);
1973	      host = @"";
1974	    }
1975	  else if ([h isEqual: [NSHost currentHost]] == YES)
1976	    {
1977	      host = @"";
1978	    }
1979	  else
1980	    {
1981	      host = [h name];
1982	    }
1983	}
1984
1985      if ([host length] == 0)
1986	{
1987	  description = @"local host";
1988	}
1989      else
1990	{
1991	  description = host;
1992	}
1993
1994      the_server = (id<GSPasteboardSvr>)[NSConnection
1995	rootProxyForConnectionWithRegisteredName: PBSNAME host: host];
1996      if (the_server == nil && [host length] > 0)
1997	{
1998	  NSString	*service;
1999
2000	  service = [PBSNAME stringByAppendingFormat: @"-%@", host];
2001	  the_server = (id<GSPasteboardSvr>)[NSConnection
2002	    rootProxyForConnectionWithRegisteredName: service host: @"*"];
2003	}
2004
2005      if (RETAIN((id)the_server) != nil)
2006	{
2007	  NSConnection	*conn = [(id)the_server connectionForProxy];
2008          Protocol      *p = @protocol(GSPasteboardSvr);
2009
2010	  [conn enableMultipleThreads];
2011          [conn setReplyTimeout:2.0];
2012          [(id)the_server setProtocolForProxy: p];
2013	  [[NSNotificationCenter defaultCenter]
2014	    addObserver: self
2015	       selector: @selector(_lostServer:)
2016		   name: NSConnectionDidDieNotification
2017		 object: conn];
2018	}
2019      else
2020	{
2021	  static BOOL		recursion = NO;
2022	  static NSString	*cmd = nil;
2023
2024	  if (cmd == nil && recursion ==NO)
2025	    {
2026	      cmd = RETAIN([NSTask launchPathForTool: @"gpbs"]);
2027	    }
2028	  if (recursion == YES || cmd == nil)
2029	    {
2030	      NSLog(@"Unable to contact pasteboard server - "
2031		@"please ensure that gpbs is running for %@.", description);
2032	      return nil;
2033	    }
2034	  else
2035	    {
2036	      NSNotificationCenter *nc;
2037	      NSMutableArray *startIndicator;
2038	      NSArray *args = nil;
2039	      NSDate *timeoutDate;
2040
2041	      NSDebugLLog(@"NSPasteboard",
2042@"\nI couldn't contact the pasteboard server for %@ -\n"
2043@"so I'm attempting to start one - which might take a few seconds.\n"
2044@"Trying to launch gpbs from %@ or a machine/operating-system subdirectory.\n",
2045description, [cmd stringByDeletingLastPathComponent]);
2046
2047	      if ([host length] > 0)
2048		{
2049		  args = [[NSArray alloc] initWithObjects:
2050		    @"-NSHost", host,
2051		    @"-GSStartupNotification", @"GSStartup-GPBS",
2052		    @"--auto",
2053		    nil];
2054		}
2055	      else
2056		{
2057		  args = [[NSArray alloc] initWithObjects:
2058		    @"-GSStartupNotification",@"GSStartup-GPBS",
2059		    @"--auto",
2060		    nil];
2061		}
2062
2063	      /*
2064	      Trick: To avoid having to use global variables or new methods
2065	      to track whether the notification has been received or not, we
2066	      use a mutable array as an indicator. When the notification is
2067	      received, the array is emptied, so we just check the count.
2068	      */
2069	      startIndicator = [[NSMutableArray alloc] initWithObjects:
2070		AUTORELEASE([[NSObject alloc] init]), nil];
2071
2072	      nc = [NSDistributedNotificationCenter defaultCenter];
2073	      [nc addObserver: startIndicator
2074		     selector: @selector(removeAllObjects)
2075			 name: @"GSStartup-GPBS"
2076		       object: nil];
2077
2078	      [NSTask launchedTaskWithLaunchPath: cmd arguments: args];
2079	      RELEASE(args);
2080
2081	      timeoutDate = [NSDate dateWithTimeIntervalSinceNow: 5.0];
2082
2083	      while ([startIndicator count]
2084	        && [timeoutDate timeIntervalSinceNow] > 0.0)
2085		{
2086		  [[NSRunLoop currentRunLoop]
2087		       runMode: NSDefaultRunLoopMode
2088		    beforeDate: timeoutDate];
2089		}
2090
2091	      [nc removeObserver: startIndicator];
2092	      DESTROY(startIndicator);
2093
2094	      recursion = YES;
2095	      [self _pbs];
2096	      recursion = NO;
2097	    }
2098	}
2099    }
2100  return the_server;
2101}
2102
2103/*
2104 * Creating and Releasing an NSPasteboard Object
2105 */
2106+ (NSPasteboard*) _pasteboardWithTarget: (id<GSPasteboardObj>)aTarget
2107				   name: (NSString*)aName
2108{
2109  NSPasteboard	*p = nil;
2110
2111  [dictionary_lock lock];
2112  p = (NSPasteboard*)NSMapGet(pasteboards, (void*)aName);
2113  if (p != nil)
2114    {
2115      /*
2116       * It is conceivable that the following may have occurred -
2117       * 1. The pasteboard was created on the server
2118       * 2. We set up an NSPasteboard to point to it
2119       * 3. The pasteboard on the server was destroyed by a [-releaseGlobally]
2120       * 4. The named pasteboard was asked for again - resulting in a new
2121       *	object being created on the server.
2122       * If this is the case, our proxy for the object on the server will be
2123       *	out of date, so we swap it for the newly created one.
2124       */
2125      if (p->target != (id)aTarget)
2126	{
2127	  ASSIGN(p->target, (id)aTarget);
2128	}
2129    }
2130  else
2131    {
2132      /*
2133       * For a newly created NSPasteboard object, we must make an entry
2134       * in the dictionary so we can look it up safely.
2135       */
2136      p = [self alloc];
2137      if (p != nil)
2138	{
2139	  ASSIGN(p->target, (id)aTarget);
2140	  ASSIGNCOPY(p->name, aName);
2141	  NSMapInsert(pasteboards, (void*)p, (void*)p->name);
2142	  [p autorelease];
2143	}
2144    }
2145  p->changeCount = [p->target changeCount];
2146  [dictionary_lock unlock];
2147  return p;
2148}
2149
2150- (id) _target
2151{
2152  return target;
2153}
2154
2155@end
2156
2157
2158
2159/**
2160 * GNUstep specific extensions ...<br />
2161 * <p>GNUstep adds a mechanism for mapping between OpenStep pasteboard
2162 * types and MIME types.  This is useful for inter-operation with other
2163 * systems, as MIME types have come into common usage (long after the
2164 * OpenStep specification was created).
2165 * </p>
2166 * <p>The other extension to the pasteboard system produced by GNUstep
2167 * is the ability to keep a history of recent items placed in a
2168 * pasteboard, and retrieve data from that history rather than just
2169 * the current item.
2170 * </p>
2171 */
2172@implementation NSPasteboard (GNUstepExtensions)
2173
2174/**
2175 * <p>Once the -setChangeCount: message has been sent to an NSPasteboard
2176 * the object will gain an extra GNUstep behaviour - when getting data
2177 * from the pasteboard, the data need no longer be from the latest
2178 * version but may be a version from a previous representation with
2179 * the specified change count.
2180 * </p>
2181 * <p>The value of count must be one which has previously been returned
2182 * by -declareTypes:owner: and should not be further in the past than
2183 * specified by the -setHistory: method.
2184 * </p>
2185 */
2186- (void) setChangeCount: (int)count
2187{
2188  useHistory = YES;
2189  changeCount = count;
2190}
2191
2192/**
2193 * Sets the number of changes for which pasteboard data is kept.<br />
2194 * This is 1 by default.
2195 */
2196- (void) setHistory: (unsigned)length
2197{
2198  NS_DURING
2199    {
2200      [target setHistory: length];
2201    }
2202  NS_HANDLER
2203    {
2204      [NSException raise: NSPasteboardCommunicationException
2205		  format: @"%@", [localException reason]];
2206    }
2207  NS_ENDHANDLER
2208}
2209
2210+ (void) _initMimeMappings
2211{
2212  mimeMap = NSCreateMapTable(NSObjectMapKeyCallBacks,
2213    NSObjectMapValueCallBacks, 0);
2214
2215  NSMapInsert(mimeMap, (void *)NSStringPboardType,
2216    (void *)@"text/plain");
2217  NSMapInsert(mimeMap, (void *)NSFileContentsPboardType,
2218    (void *)@"text/plain");
2219  NSMapInsert(mimeMap, (void *)NSFilenamesPboardType,
2220    (void *)@"text/uri-list");
2221  NSMapInsert(mimeMap, (void *)NSPostScriptPboardType,
2222    (void *)@"application/postscript");
2223  NSMapInsert(mimeMap, (void *)NSTabularTextPboardType,
2224    (void *)@"text/tab-separated-values");
2225  NSMapInsert(mimeMap, (void *)NSRTFPboardType,
2226    (void *)@"text/richtext");
2227  NSMapInsert(mimeMap, (void *)NSTIFFPboardType,
2228    (void *)@"image/tiff");
2229  NSMapInsert(mimeMap, (void *)NSGeneralPboardType,
2230    (void *)@"text/plain");
2231}
2232
2233/**
2234 * Return the mapping for pasteboard->mime, or return the original pasteboard
2235 * type if no mapping is found
2236 */
2237+ (NSString *) mimeTypeForPasteboardType: (NSString *)type
2238{
2239  NSString	*mime;
2240
2241  if (mimeMap == NULL)
2242    {
2243      [self _initMimeMappings];
2244    }
2245  mime = NSMapGet(mimeMap, (void *)type);
2246  if (mime == nil)
2247    {
2248      mime = type;
2249    }
2250  return mime;
2251}
2252
2253/**
2254 * Return the mapping for mime->pasteboard, or return the original pasteboard
2255 * type if no mapping is found. This method may not have a one-to-one
2256 * mapping
2257 */
2258+ (NSString *) pasteboardTypeForMimeType: (NSString *)mimeType
2259{
2260  BOOL			found;
2261  NSString		*type;
2262  NSString		*mime;
2263  NSMapEnumerator	enumerator;
2264
2265  if (mimeMap == NULL)
2266    {
2267      [self _initMimeMappings];
2268    }
2269  enumerator = NSEnumerateMapTable(mimeMap);
2270  while ((found = NSNextMapEnumeratorPair(&enumerator,
2271    (void **)(&type), (void **)(&mime))))
2272    {
2273      if ([mimeType isEqual: mime])
2274	{
2275	  break;
2276	}
2277    }
2278
2279  if (found == NO)
2280    {
2281      type = mimeType;
2282    }
2283  return type;
2284}
2285
2286@end
2287
2288/**
2289 * Category of NSURL providing convenience methods.
2290 */
2291@implementation NSURL (NSPasteboard)
2292/**
2293 * Creates a URL with data (of NSURLPboardType) from pasteBoard.
2294 */
2295+ (NSURL *) URLFromPasteboard: (NSPasteboard *)pasteBoard
2296{
2297  return [self URLWithString: [pasteBoard stringForType: NSURLPboardType]];
2298}
2299
2300/**
2301 * Writes the receiver (as data of NSURLPboardType) to pasteBoard.
2302 */
2303- (void) writeToPasteboard: (NSPasteboard *)pasteBoard
2304{
2305  [pasteBoard setString: [self absoluteString]
2306		forType: NSURLPboardType];
2307}
2308
2309@end
2310
2311
2312/**
2313 * <p>Returns a standardised pasteboard type for file contents,
2314 * formed from the supplied file extension.
2315 * </p>
2316 * <p>Data written to a pasteboard with a file contents type should
2317 * be written using the [NSPasteboard-writeFileContents:] or
2318 * [NSPasteboard-writeFileWrapper:] method.  Similarly, the data should
2319 * be read using the [NSPasteboard-readFileContentsType:toFile:] or
2320 * [NSPasteboard-readFileWrapper] method.
2321 * </p>
2322 */
2323NSString*
2324NSCreateFileContentsPboardType(NSString *fileType)
2325{
2326  NSString	*ext = [fileType pathExtension];
2327
2328  if ([ext length] == 0)
2329    {
2330      ext = fileType;
2331    }
2332  return [NSString stringWithFormat: @"%@%@", contentsPrefix, ext];
2333}
2334
2335/**
2336 * <p>Returns a standardised pasteboard type for file names,
2337 * formed from the supplied file extension.
2338 * </p>
2339 * <p>Data written to a pasteboard with a file names type should
2340 * be a single name written using [NSPasteboard-setString:forType:] or
2341 * an array of strings written using
2342 * [NSPasteboard-setPropertyList:forType:].<br />
2343 * Similarly, the data should be read using
2344 * the [NSPasteboard-stringForType:] or
2345 * [NSPasteboard-propertyListForType:] method.
2346 * </p>
2347 * <p>See also the NSGetFileType() and NSGetFileTypes() functions.
2348 * </p>
2349 */
2350NSString*
2351NSCreateFilenamePboardType(NSString *fileType)
2352{
2353  NSString	*ext = [fileType pathExtension];
2354
2355  if ([ext length] == 0)
2356    {
2357      ext = fileType;
2358    }
2359  return [NSString stringWithFormat: @"%@%@", namePrefix, ext];
2360}
2361
2362/**
2363 * Returns the file type (fileType extension) corresponding to the
2364 * pasteboard type given.<br />
2365 * This is a counterpart to the NSCreateFilenamePboardType() function.
2366 */
2367NSString*
2368NSGetFileType(NSString *pboardType)
2369{
2370  if ([pboardType hasPrefix: contentsPrefix])
2371    {
2372      return [pboardType substringFromIndex: [contentsPrefix length]];
2373    }
2374  if ([pboardType hasPrefix: namePrefix])
2375    {
2376      return [pboardType substringFromIndex: [namePrefix length]];
2377    }
2378  return nil;
2379}
2380
2381/**
2382 * Returns the file types (filename extensions) corresponding to the
2383 * pasteboard types given.
2384 */
2385NSArray*
2386NSGetFileTypes(NSArray *pboardTypes)
2387{
2388  NSMutableArray *a = [NSMutableArray arrayWithCapacity: [pboardTypes count]];
2389  unsigned int	i;
2390
2391  for (i = 0; i < [pboardTypes count]; i++)
2392    {
2393      NSString	*s = NSGetFileType([pboardTypes objectAtIndex: i]);
2394
2395      if (s && ! [a containsObject: s])
2396	{
2397	  [a addObject: s];
2398	}
2399    }
2400  if ([a count] > 0)
2401    {
2402      return AUTORELEASE([a copy]);
2403    }
2404  return nil;
2405}
2406
2407/*
2408 * The following dummy classes are here solely as a workaround for pre 3.3
2409 * versions of gcc where protocols didn't work properly unless implemented
2410 * in the source where the '@protocol()' directive is used.
2411 */
2412@interface NSPasteboardServerDummy : NSObject <GSPasteboardSvr>
2413- (id<GSPasteboardObj>) pasteboardWithName: (in bycopy NSString*)name;
2414@end
2415@implementation NSPasteboardServerDummy
2416- (id<GSPasteboardObj>) pasteboardWithName: (in bycopy NSString*)name
2417{
2418  return nil;
2419}
2420@end
2421@interface NSPasteboardObjectDummy : NSObject <GSPasteboardObj>
2422- (int) addTypes: (in bycopy NSArray*)types
2423           owner: (id)owner
2424      pasteboard: (NSPasteboard*)pb
2425        oldCount: (int)count;
2426- (NSString*) availableTypeFromArray: (in bycopy NSArray*)types
2427                         changeCount: (int*)count;
2428- (int) changeCount;
2429- (NSData*) dataForType: (in bycopy NSString*)type
2430               oldCount: (int)count
2431          mustBeCurrent: (BOOL)flag;
2432- (int) declareTypes: (in bycopy NSArray*)types
2433               owner: (id)owner
2434          pasteboard: (NSPasteboard*)pb;
2435- (NSString*) name;
2436- (void) releaseGlobally;
2437- (BOOL) setData: (in bycopy NSData*)data
2438         forType: (in bycopy NSString*)type
2439          isFile: (BOOL)flag
2440        oldCount: (int)count;
2441- (void) setHistory: (unsigned)length;
2442- (bycopy NSArray*) typesAndChangeCount: (int*)count;
2443@end
2444@implementation NSPasteboardObjectDummy
2445- (int) addTypes: (in bycopy NSArray*)types
2446           owner: (id)owner
2447      pasteboard: (NSPasteboard*)pb
2448        oldCount: (int)count
2449{
2450  return 0;
2451}
2452- (NSString*) availableTypeFromArray: (in bycopy NSArray*)types
2453                         changeCount: (int*)count
2454{
2455  return nil;
2456}
2457- (int) changeCount
2458{
2459  return 0;
2460}
2461- (NSData*) dataForType: (in bycopy NSString*)type
2462               oldCount: (int)count
2463          mustBeCurrent: (BOOL)flag
2464{
2465  return nil;
2466}
2467- (int) declareTypes: (in bycopy NSArray*)types
2468               owner: (id)owner
2469          pasteboard: (NSPasteboard*)pb
2470{
2471  return 0;
2472}
2473- (NSString*) name
2474{
2475  return nil;
2476}
2477- (void) releaseGlobally
2478{
2479}
2480- (BOOL) setData: (in bycopy NSData*)data
2481         forType: (in bycopy NSString*)type
2482          isFile: (BOOL)flag
2483        oldCount: (int)count
2484{
2485  return NO;
2486}
2487- (void) setHistory: (unsigned)length
2488{
2489}
2490- (bycopy NSArray*) typesAndChangeCount: (int*)count
2491{
2492  return nil;
2493}
2494@end
2495
2496