oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

LinkBack: Applications Working Together
Pages: 1, 2

Step 3: Responding to Edit Requests

During this step you are going to modify the Sketch document class so you can initialize it with LinkBack data and then you will add the method to actually create documents when an edit request is received.

The LinkBack-Enabled Document Class

Remember the graphics data we generated back in step one? Now we're going to use that data to create a document with graphics for the user to edit. Most of this responsibility will fall to the Sketch document class, SKTDrawDocument.

To start with, change the SKTDrawDocument header file by adding the ivar and new method names shown below. These changes will be explained when we write the implementations of the methods.

@class LinkBack ;
@interface SKTDrawDocument : NSDocument {
	LinkBack* _link ;
	NSMutableArray *_graphics;

- (id)initWithLinkBack:(LinkBack*)aLink ;

By now you might be wondering exactly what this LinkBack object is anyway. Every time a client requests an edit from a server, a new LinkBack object is created. You use this object to get your application data, to notify the client of edits, and to close the connection when you are finished editing.

In the new init method we are adding, we will use the LinkBack object to obtain the application data so we can populate our document with graphics. Switch to the implementation of SKTDrawDocument and add the init method:

- (id)initWithLinkBack:(LinkBack*)aLink 
	if (self = [self init]) {
	  _link = [aLink retain] ;
	  [_link setRepresentedObject: self] ;
	  // get graphics from link
	  id linkBackData = [[_link pasteboard] propertyListForType: 
	     LinkBackPboardType] ;
	  id graphics = LinkBackGetAppData(linkBackData) ;
	  graphics = [self drawDocumentDictionaryFromData: graphics] ;
	  graphics = [self graphicsFromDrawDocumentDictionary: graphics] ;
	  [self setGraphics: graphics] ;
	  // fix up undo
	  [[self undoManager] removeAllActions] ;
	  [self updateChangeCount: NSChangeCleared] ;
	return self ;

There's one other change we want to make. When the user has a document window opening that is editing graphics from another application, we want them to know that, by setting the title of the window to reflect the source of the graphics. To do this, add the following method to the SKTDrawDocument implementation as well:

- (NSString*)displayName
	if (_link) {
	  NSString* sourceName = [_link sourceName] ;
	  NSString* sourceAppName = [_link sourceApplicationName] ;
	  NSString* ret = [NSString stringWithFormat: @"Graphics from %@ 
		(%@)", sourceName, sourceAppName] ;
	  return ret ;
	} else return [super displayName] ;

Creating the LinkBack Documents

Finally, we need to add the method that will actually get called by LinkBack whenever a client requests an edit. Switch to the SKTDrawAppDelegate implementation and add the following method:

- (void)linkBackClientDidRequestEdit:(LinkBack*)link 
	SKTDrawDocument* doc = [link representedObject] ;

	// if no document already exists for this link, create one
	if (nil==doc) {
	  NSDocumentController* dc = [NSDocumentController 
		sharedDocumentController] ;
	  doc = [[SKTDrawDocument alloc] initWithLinkBack: link] ;
	  [dc addDocument: doc] ;
	  [doc makeWindowControllers] ; // causes it to display.
	// always bring app and document window to the front.
	[NSApp activateIgnoringOtherApps: YES] ;
	[doc showWindows] ;

Now, build and run your Sketch app, create a graphic, add it to your client application, and double-click again. A document window opens with the graphic. Now we're getting somewhere. But how do changes to your graphic make it back to the client? That's where step 4 comes in.

LinkBack edit

Step 4: Sending Edits Back to the Client

Changes are sent back to the client application whenever you save your special document in Sketch. Simply add the following method to the SKTDrawDocument implementation.

- (void)saveDocument:(id)sender
	if (_link) {
	  NSArray *sel = [self graphics];
	  if ([sel count] > 0) {
	    NSPasteboard *pboard = [_link pasteboard];
	    id data ;
	    [pboard declareTypes:[NSArray 
		  arrayWithObjects:SKTDrawDocumentType, NSTIFFPboardType, 
		  NSPDFPboardType, LinkBackPboardType, nil] owner:nil];
	    data = [self drawDocumentDataForGraphics: sel] ;
	    [pboard setData: data forType: SKTDrawDocumentType];
	    [pboard setData:[self TIFFRepresentationForGraphics: sel] 
	    [pboard setData:[self PDFRepresentationForGraphics: sel] 
	    // save the pboard data for LinkBack.
	    [pboard setPropertyList: [NSDictionary 
		  linkBackDataWithServerName: @"sketch" appData: data] forType: 
		  LinkBackPboardType] ;
	    [_link sendEdit] ;
	    // fix up undo
	    [[self undoManager] removeAllActions] ;
	    [self updateChangeCount: NSChangeCleared] ;
	  } else NSBeep() ;      
	} else [super saveDocument: sender] ;

For documents created from a LinkBack object, this method simply loads the LinkBack pasteboard with the contents of the document and invokes -sendEdit, which notifies the client of the changed content. Most of this code is straight out of the -copy: method we looked at earlier. In a properly factored application, you could probably reduce the code needed for this method even more.

OK, let's try it all again. Build and run, create a graphic, paste it into a your client app, double-click on the graphic, make some changes, and save. Presto! The graphic updates in the client application. Satisfying isn't it? All we have left is to tie up a few loose ends and we're done.

Step 5: Handling Closures

As nice as it is to edit content, at some point the user is going to finish editing. The user may close the document in the client or in the server. Either way, both applications need to be informed when the user finishes editing so they can close their windows. This step adds code to clean up after the document closes a document on either the server or client side.

Handling Server-Side Closes

When the user closes the Sketch document, we need to inform the client that we are finished editing. That way if the user tries to edit the content again, the client knows that it needs to request a new edit instead of trying to reuse the existing one.

To do this, add the following method to SKTDrawDocument:

- (void)closeLinkIfNeeded
	if (_link) {
	  [_link setRepresentedObject: nil] ;
	  [_link closeLink] ;
	  [_link release] ;
	  _link = nil ;

- (void)close
	[self closeLinkIfNeeded] ;
	[super close] ;

Handling Client-Side Closes

Similarly, if the user quits the client application or closes a document, the client will notify Sketch if it has any open LinkBack sessions. This message is received by the object you registered to received edit requests in Step 2. Add the following method to the SKTDrawAppDelegate:

- (void)linkBackDidClose:(LinkBack*)link 
	// find document for live link.  Close document.
	SKTDrawDocument* doc = [link representedObject] ;
	[doc close] ;

Congratulations, you're done. You now have a fully functioning LinkBack-enabled Sketch application.

A Job Well Done

Applications are more useful when users can use them together, which is what makes LinkBack so interesting. It's also easy to add to your own application, so you can get the network benefits of all the other applications that support LinkBack with little effort.

If you are interested in using LinkBack in your own application, have a look at the LinkBack Developer's Guide in the latest LinkBack Source Distribution available from the website. You can also join the mailing list to get answers and advice on how to best use LinkBack in your software. I hope to see you on the list soon.

Charles Jolley is the creator of the LinkBack and managing director at Nisus Software, makers of Nisus Writer Express, the first Cocoa word processor for Mac OS X.

Return to the Mac DevCenter.