Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: More Macintosh Toolbox /
Chapter 2 - Scrap Manager / Using the Scrap Manager


Getting Data From the Scrap

Your application should read data from the scrap (or from its own private scrap) whenever the user chooses the Paste command. In addition, if your application uses a private scrap, upon receiving a resume event your application should determine whether the contents of the scrap have changed since the previous resume event, and if so, it should take the appropriate actions. The next sections explain how to perform these tasks.

Handling the Paste Command

When the user chooses the Paste command, your application should paste the data last cut or copied by the user. You should insert the new data at the current insertion point or, if a selection exists, replace the selection with the new data. You get the data to paste by reading the data from the scrap or from your application's private scrap.

When you read data from the scrap, your application should request the data in its preferred scrap format type. If that type of format doesn't exist in the scrap, then request the data in another format. For example, SurfWriter's preferred format type is 'surf', so it requests data from the scrap in this format. If this format isn't in the scrap, SurfWriter requests its next preferred type, 'TEXT'. Finally, if the 'TEXT' format isn't in the scrap, SurfWriter requests the data in the 'PICT' format.

If your application doesn't have a preferred scrap format type, then read from the scrap each format type your application supports. Along with a pointer to the data of the requested format type, the GetScrap function returns an offset, a value that indicates the relative offset of the start of that format of data in the scrap. (Note that the returned value for the offset is valid only if the Translation Manager isn't available; if the Translation Manager is available, then your application should not rely on the offset value.) The format type with the lowest offset is the preferred format type of the application that put the data in the scrap; thus a format with a lower offset is more likely to contain more information than formats in the scrap with higher offsets. So when the Translation Manager isn't available, use the format with the lowest offset when your application doesn't have a particular scrap format that it prefers.

If you request a scrap format type that isn't in the scrap and the Translation Manager is available, the Scrap Manager uses the Translation Manager to convert any one of the scrap format types currently in the scrap into the scrap format type requested by your application. The Translation Manager looks for a translator that can perform one of these translations. If such a translator is available (for example, a translator that can translate the 'SDBS' scrap format type into the 'SURF' scrap format type), the Translation Manager uses the translator to translate the data in the scrap into the requested scrap format type. If the translation is successful, the Scrap Manager returns to your application the data from the scrap in the requested scrap format type.

Listing 2-4 shows SurfWriter's routine for handling the Paste command. The SurfWriter application doesn't use a private scrap; whenever the user performs a paste operation, SurfWriter reads the data that is to be pasted from the scrap.

For document windows, the SurfWriter application first determines whether the data in the scrap exists in its own private scrap format ('SURF') by using the GetScrap function. If you specify a NIL handle as the location to return the data, GetScrap does not return the data but does return as its function result the number of bytes (if any) of data of the specified format that exists in the scrap. If data of this format does exist, SurfWriter reads the data in this format. SurfWriter allocates the handle to hold any returned data but does not need to size the handle; GetScrap automatically resizes the handle passed to it to the required size to hold the retrieved data. Once the data is retrieved in 'SURF' format, SurfWriter pastes the data into the current document.

If the scrap does not contain data in 'SURF' format (and the available translators can't convert any of the scrap format types in the scrap to the 'SURF' format), SurfWriter determines whether any data in 'TEXT' format exists in the scrap. If so, SurfWriter uses GetScrap to retrieve the data. Once the data is retrieved in 'TEXT' format, SurfWriter pastes the data into the current document.

If the scrap does not contain data in 'TEXT' format, SurfWriter determines whether any data in 'PICT' format exists in the scrap. If so, SurfWriter uses GetScrap to retrieve the data. Once the data is retrieved in 'PICT' format, SurfWriter determines the destination rectangle, that is, the rectangle where the picture should be displayed, then uses the QuickDraw DrawPicture procedure to draw the picture in the window. SurfWriter stores a handle to this picture and sets other application-defined variables as needed.

Listing 2-4 Handling the Paste command using the scrap

 PROCEDURE DoPasteCommand;
 VAR
   window:              windowPtr;
   windowType:          LongInt;
   offset:              LongInt;
   sizeOfSurfData:      LongInt;
   sizeOfPictData:      LongInt;
   sizeOfTextData:      LongInt;
   hDest:               Handle;
   myData:              MyDocRecHnd;
   teHand:              TEHandle;
   destRect:            Rect;
   myErr:               OSErr;
BEGIN    
   window := FrontWindow;
   windowType := MyGetWindowType(window);
   IF windowType = kMyDocWindow THEN
   BEGIN {handle Paste command in document window. Check }
         { whether the scrap contains any data. This app }
         { checks for its preferred format type, 'SURF', first}
      sizeOfSurfData := GetScrap(NIL, 'SURF', offset);
      IF sizeOfSurfData > 0 THEN
      BEGIN
         {allocate handle to hold data from scrap--GetScrap }
         hDest := NewHandle(0);  { automatically resizes it}
         HLock(hDest);
         {put data into memory referenced thru hDest handle}
         sizeOfSurfData := GetScrap(hDest, 'SURF', offset);
         {paste the data into the current document}
         MyPasteSurfData(hDest);
         HUnlock(hDest);
         DisposeHandle(hDest);
      END
      ELSE
      BEGIN    {if no 'SURF' data in scrap, check for 'TEXT'}
         sizeOfTextData := GetScrap(NIL, 'TEXT', offset);
         IF sizeOfTextData > 0 THEN
         BEGIN
            {allocate handle to hold data from scrap--GetScrap }
            hDest := NewHandle(0);  { automatically resizes it}
            HLock(hDest);
            {put data into memory referenced thru hDest handle}
            sizeOfTextData := GetScrap(hDest, 'TEXT', offset);
            {paste the text into the current document}
            MyPasteText(hDest);
            HUnlock(hDest);
            DisposeHandle(hDest);
         END
         ELSE  {if no 'TEXT' data in scrap, check for 'PICT'}
         BEGIN
            sizeOfPictData := GetScrap(NIL, 'PICT', offset);
            IF sizeOfPictData > 0 THEN
            BEGIN
               {allocate handle to hold scrap data--GetScrap }
               hDest := NewHandle(0);  { automatically resizes it}
               HLock(hDest);
               {put data into memory referenced thru hDest handle}
               sizeOfPictData := GetScrap(hDest, 'PICT', offset);
               {calculate destination rectangle for plotting the }
               { picture}
               MyGetDestRect(hDest, destRect);
               DrawPicture(PicHandle(hDest), destRect);
               {save information about the picture}
               myData := MyDocRecHnd(GetWRefCon(window));
               myData^^.pictNum := myData^^.pictNum +1;
               myData^^.pictDestRect[myData^^.pictNum] :=
                                                       destRect;
               IF myData^^.windowPicHndl[myData^^.pictNum] = NIL 
               THEN
                  myData^^.windowPicHndl[myData^^.pictNum] := 
                     PicHandle(NewHandle(Size(sizeOfPictData)));
               myData^^.windowPicHndl[myData^^.pictNum] := 
                                                PicHandle(hDest);
               myErr := HandToHand(Handle
                     (myData^^.windowPicHndl[myData^^.pictNum]));
               HUnlock(hDest);
               DisposeHandle(hDest);
            END;     {of sizeOfPictData > 0}
         END;     {of "if no 'TEXT' data, check for 'PICT'"}
      END;     {of "if no 'surf' data, check for 'TEXT'"}
   END      {of "if windowType = kMyDocWindow"}
   ELSE     {window is not a document window}
   BEGIN
      IF windowType <> kNil THEN
      BEGIN {handle Paste command in dialog box, }
            { DialogPaste checks whether the dialog box has any }
            { editText items and if so, uses TEPaste to paste }
            { any text from the scrap to the currently selected }
            { editText item, if any}
         DialogPaste(window);
      END;
   END;
END;
If your application uses TextEdit in its document windows, then use the TextEdit routine TEPaste instead of GetScrap to read the data to paste. See Listing 2-9 on page 2-30 for an application-defined routine that uses TextEdit to help handle the application's Paste command.

If your application uses a private scrap, then read the data from your private scrap rather than from the scrap (unless the scrap contains the more recent data). Listing 2-5 shows SurfPaint's application-defined routine that handles the Paste command by reading the desired data from its private scrap.

Listing 2-5 Handling the Paste command using a private scrap

PROCEDURE DoPasteCmd;
VAR
   window:           WindowPtr;
   windowType:       Integer;
   dataToPaste:      Ptr;
BEGIN
   window := FrontWindow;
   windowType := MyGetWindowType(window);
   IF windowType = kMyDocWindow THEN
   BEGIN 
      IF gNewScrap THEN    {if new data in scrap, }
      BEGIN                { copy to private scrap}
         MyConvertScrap(kClipboardToPrivate);
         gNewScrap := FALSE;
      END;
      {get the data to paste from app's private scrap}
      dataToPaste := NewPtr(kDefaultSize);
      MyReadDataFromPrivateScrap(dataToPaste);
      MyPasteData(dataToPaste);
      DisposePtr(dataToPaste);
   END
   ELSE 
   IF windowType <> kNil THEN
   BEGIN {window is a dialog box}
      DialogPaste(window); 
   END;
END;
The SurfPaint application uses a private scrap, and when it receives a resume event, it determines whether the contents of the scrap have changed. If so, SurfPaint sets an application-defined global variable, gScrapNewData, but does not immediately read in the contents of the scrap. Instead, whenever the user chooses the Paste command, SurfPaint checks the value of this global variable. If gScrapNewData is TRUE SurfPaint reads the new data from the scrap to its private scrap, resets the gScrapNewData global variable to FALSE, and then performs the paste operation. SurfPaint also resets the value of the gScrapNewData global variable to FALSE whenever the user chooses the Cut or Copy command. By using this method, SurfPaint reads in new data from the scrap only when necessary and avoids reading in data that the user might not use. This method also decreases the time it takes for the application to return to the foreground, as the application avoids or delays any lengthy translation of data from the scrap.

Handling Resume Events

As previously described, when your application receives a resume event (and your application uses a private scrap), your application should determine whether the contents of the scrap have changed since the previous suspend event. If the contents of the scrap have changed, your application must be sure to use the new data in the scrap for the user's next Paste command (unless the user chooses Cut or Copy before choosing Paste).

In addition, if your application supports the Show Clipboard command and the Clipboard window was showing at the time of the previous suspend event, your application should update its Clipboard window to show the new contents of the scrap.

Listing 2-6 shows SurfPaint's procedure for handling resume events (and suspend events).

Listing 2-6 Handling resume events

PROCEDURE DoSuspendResumeEvent (event: EventRecord);
VAR
   currentFrontWindow: WindowPtr;
BEGIN                         
   currentFrontWindow := FrontWindow;
   IF (BAnd(event.message, resumeFlag) <> 0) THEN 
   BEGIN                {it's a resume event}
      IF (BAnd(event.message, convertClipboardFlag) <> 0) THEN
      BEGIN
         {set flag to indicate there's new data in the scrap}
         gNewScrap := TRUE;
      END;
      gInBackground := FALSE; {app no longer in background}
                              {activate front window}
      DoActivate(currentFrontWindow, NOT gInBackground, event);
      {show Clipboard window if it was showing at last suspend }
      { event and update its contents to match scrap}
      MyShowClipboardWindow(gNewScrap); 
      MyShowFloatingWindows;  {show any floating windows}
   END
   ELSE
   BEGIN                {it's a suspend event, }
                        { handle as shown in Listing 2-3}
   END;
END;           
Listing 2-6 shows a procedure that responds to suspend and resume events. The DoSuspendResumeEvent procedure first gets a pointer to the front window using the Window Manager function FrontWindow. It then examines bit 0 of the message field of the event record to determine whether the event is a suspend or resume event. If the event is a resume event, the code examines bit 1 of the message field of the event record to determine whether it needs to read in the contents of the scrap. If so, the code sets an application-defined global variable, gNewScrap, to indicate that new data exists in the scrap. When the user next chooses the Paste command, SurfPaint checks the value of the gNewScrap global variable and, if it's TRUE, reads the data from the scrap to its private scrap and then performs the paste operation. If the user chooses the Cut or Copy command before choosing Paste, then SurfPaint resets the gNewScrap global variable to FALSE to indicate that its private scrap contains the most recent data for the Paste command. This technique allows SurfPaint to delay or avoid any lengthy translation of data from the scrap to its private scrap and decreases the time it takes for SurfPaint to return to the foreground.

The DoSuspendResumeEvent procedure then sets a private global flag, gInBackground, to FALSE, to indicate that the application is not in the background. It then calls another application-defined routine, DoActivate, to activate the application's front window. It also calls the application-defined routine MyShowClipboardWindow to show the Clipboard window and update its contents if it was showing at the time of the previous suspend event.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
6 JUL 1996