Using the Pasteboard in GraphPaper

To demonstrate how to use the pasteboard, we’ll first modify the GraphPaper application so that a user can copy a graph to the pasteboard by choosing the Edit Copy menu command.

When you create a new project in Project Builder and then open the new MainMenu.nib file in Interface Builder, you are automatically provided with an Edit submenu like the one shown in Figure 20-1. The top seven menu items listed (all but the Find and Spelling submenus) come preconnected to the First Responder icon (a proxy icon in IB that represents the current First Responder object, which changes in response to user events). The methods invoked by the First Responder object in response to these menu commands are undo:, redo:, cut:, copy:, paste:,clear:, and selectAll:, respectively. Thus, to implement Cut and Copy menu commands for the graph, all we need to do is add cut: and copy: methods to GraphPaper’s Controller class and make Controller the NSApp (NSApplication object) delegate. (Note that we already made Controller the NSApp delegate, in Chapter 17.) The cut: and copy: messages will automatically be forwarded to NSApp’s delegate, unless another responder in the responder chain intercepts them first.

Default Edit menu provided for a new Cocoa application in IB

Figure 20-1. Default Edit menu provided for a new Cocoa application in IB

GraphPaper’s implementation of cut, copy, and paste will be able to provide data in two formats: PDF and TIFF. Because PDF is the richer of these two formats, GraphPaper will put PDF on the pasteboard first and then convert it to TIFF if requested by lazy evaluation.

Now we’re ready to discuss the implementations of Controller’s copyToPasteboard: and copy: methods. The supporting copyToPasteboard: method is the one that does most of the work.

  1. Insert the following four method declarations into Controller.h:

                         - (void)copyToPasteboard:(NSPasteboard *)pboard;
                         - (IBAction)copy:(id)sender;
                         - (void)pasteboard:(NSPasteboard *)sender 
                                 provideDataForType:(NSString *)type;
                         - (IBAction)cut:(id)sender;
  2. Insert the following copyToPasteboard: andcopy: methods into Controller.m:

                         - (void)copyToPasteboard:(NSPasteboard *)pboard
                         {
                             // Declare that we can handle PDF and TIFF
                             [pboard declareTypes:[NSArray arrayWithObjects:
                                NSPDFPboardType,NSTIFFPboardType,nil] owner:self];
                         
        // Now put a PDF on the pasteboard
                             [pboard setData:[self PDFForView:graphView] forType:NSPDFPboardType];
                         }
                         
    - (IBAction)copy:(id)sender
                         {
                             [self copyToPasteboard: [NSPasteboard generalPasteboard] ];
                         }

The copyToPasteboard: method begins by constructing a disposable array of two elements, NSPDFPboardType and NSTIFFPboardType. The order of these two elements is important: it specifies the preferred order in which the types should be used (PDF is better than TIFF). It then sends the declareTypes:owner: message to the pasteboard object (which is passed from the copy: method) to do three things:

  1. Erase any existing data on the pasteboard.

  2. Tell the pasteboard that your object can provide data of type PDF or TIFF.

  3. Specify an object (via the owner: argument) that the pasteboard can message to provide any types necessary for lazy evaluation. Whenever there is a request for lazy data from the NSPasteboard, the pasteboard will send the pasteboard:provideDataForType: message to the object specified by the owner: argument.

The setData:forType: message in the copyToPasteboard: method gets an NSData object with the PDF representation and puts it on the pasteboard.

The copy: method simply calls copyToPasteboard: with a general NSPasteboard. We use this methodology so that we can use the copyToPasteboard: method later in this chapter to copy the PDF and TIFF representations to pasteboards other than the general pasteboard.

Providing Data Through Lazy Evaluation

Suppose that a user has copied a GraphPaper graph to the pasteboard and wants to paste it into another application, such as TextEdit (which we’ll refer to as the receiving application). When the user chooses the Paste command to paste the graph, the receiving application obtains access to the selection pasteboard with the[NSPasteboard generalPasteboard] message and then sends the types message to find out what types are available. The types message will return the following array of two types that the GraphPaper put on the pasteboard with the declareTypes:owner: method:

{NSPDFPboardType, NSTIFFPboardType}

Even if the receiving program knows what kind of data it wants, the program must first send the NSPasteboard the types message to set it up for returning the requested data. Once the types message is sent, the receiving program can ask for either type and be reasonably well assured of getting it.

If the receiving application wants the NSPDFPboardType, it will simply take the data off the pasteboard when it invokes the dataForType: method. However, if it wants the NSTIFFPboardType, it will wait while the NSPasteboard object sends the pasteboard:provideDataForType: message to GraphPaper’s Controller object and receives a reply. This lazy evaluation is completely transparent to the program that is receiving the pasted data.

The pasteboard:provideDataForType: method that performs the conversion from PDF to TIFF is a little tricky. We can’t just use the Controller instance method TIFFForView: (as in the previous chapter), because it is possible that the graph that was copied to the pasteboard is no longer the one displayed in the GraphPaper window. Instead, this method needs to take the PDF image from the pasteboard and convert it to a TIFF image. It does this conversion by using an NSImage object.

  1. Insert the following pasteboard:provideDataForType: method into Controller.m:

                            - (void)pasteboard:(NSPasteboard *)sender
                                    provideDataForType:(NSString *)type
                            {
                                if ([type isEqualToString:NSTIFFPboardType]) {
                                    NSImage *image  = [[NSImage alloc]
                                                  initWithData:[sender dataForType:NSPDFPboardType]];
                            
            [sender setData:[image TIFFRepresentation] 
                                            forType:NSTIFFPboardType];
                                    [image release];
                                }
                            }

This method both reads information off the pasteboard and puts new data on the pasteboard. The data is read off the pasteboard with the method dataForType:. Data from the pasteboard arrives in the form of an NSData object. Although this NSData object looks like the others, the kernel may implement the copy by mapping the data from the address space of one application to another without actually copying the data. This is why Cocoa doesn’t “choke” when you cut and paste tens of megabytes of information at once.

If the user quits GraphPaper after some of its data has been copied to the pasteboard, its NSApplication object will automatically force the pasteboard owner to turn all of the lazy data into real data (or at least ask the user if the copied data will be needed by another application). This lets the user paste data into another application even if the source (application) of the data copied to the pasteboard is no longer running.

Implementing the Cut Command

Cutting data is similar to copying it, except the data in the application is deleted after the copy operation is performed. For GraphPaper, it doesn’t make a lot of sense to cut out a graph from the ZoomScrollView, but implementing it still makes sense from a user-interface point of view (it’s good practice to give the user the expected feedback from a well-known and widely used command). Therefore, if the user tries to cut a graph, GraphPaper will copy the graph onto the pasteboard and then erase the ZoomScrollView.

  1. Insert the following directive at the top of Controller.m:

                            #import "ZoomScrollView.h"
  2. Insert the following cut: method implementation into Controller.m:

                            - (IBAction)cut:(id)sender
                            {
                                [self copy:sender];
                                [graphView clear];
                            }

Testing GraphPaper’s Copy and Cut Commands

  1. Build and run GraphPaper, saving all files first.

  2. Graph an equation and click the cursor on the graph (otherwise, one of the text fields might be the first responder).

  3. Choose GraphPaper’s Edit Copy menu command to copy the graph to the pasteboard. GraphPaper’s NSApplication object will send the copy: message to its delegate Controller object.

  4. Open a document in Word, Create, or any other application that supports graphics (TextEdit does so in Rich Text mode) and choose the Edit Paste menu command.

    If the modifications to GraphPaper are correct and the program you’re using can handle the appropriate pasteboard types, the graph will appear in your word-processor document. (An example of pasting into Word is shown in Figure 20-2.) If you don’t have any such applications, download a trial version from the Web.

    A GraphPaper graph pasted into Word

    Figure 20-2. A GraphPaper graph pasted into Word

  5. Now graph a different equation and click in the ZoomScrollView area.

  6. Choose GraphPaper’s Edit Cut menu command this time. The graph should disappear.

  7. Again choose the Edit Paste menu command in the word processor, and the second graph should appear.

  8. Quit GraphPaper.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.67.5