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