Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
FinderDragPro.c
/* |
File: FinderDragPro.c |
Description: Sample file illustrating drag and drop techniques for use |
with file system objects. This file illustrates how applications |
can use drag and drop commands in a way compatible with current |
and past versions of the Finder. |
This file is organized into the following sections: |
COPYING FILES |
routines used for copying files. For promised file flavors, we copy |
files to the destination directory. routines in this section manage |
file copy operations performed when providing promised files. |
SENDING DRAGS |
routines used for dragging files out of the main window. This includes |
code for both standard hfs flavors and promised hfs flavors. |
RECEIVING DRAGS |
routines for receiving drags dropped into the main window. this implementation |
only accepts drags containing one item that can be either a promised hfs flavor |
or a standard hfs flavor. Of interest here is code for handling promised hfs flavors |
provided by Find File. |
THE MAIN FINDER DRAG PRO WINDOW |
routines for maintaining the main window. These routines are responsible for |
drawing the window's contents, and for handling clicks in the main window. |
MENU HANDLING |
routines for processing menu commands. In this implementation, there are |
only two valid menu commands: one to quit the application and one to clear |
the main window's display. |
APPLE EVENT HANDLERS |
apple event handlers used in the application. Of interest here is the bogus Finder |
event handler installed for compatibility with past versions of the Finder. |
EVENT HANDLING |
general event processing routines used in the application including calls to |
WaitNextEvent and dispatching based on the event returned. |
MAIN |
the main routine including initialization code, the main loop, and termination code. |
Author: John Montbriand |
Some techniques borrowed from Pete Gontier's original FinderDragPro. |
Copyright: Copyright: © 1999 by Apple Computer, Inc. |
all rights reserved. |
Disclaimer: You may incorporate this sample code into your applications without |
restriction, though the sample code has been provided "AS IS" and the |
responsibility for its operation is 100% yours. However, what you are |
not permitted to do is to redistribute the source as "DSC Sample Code" |
after having made changes. If you're going to re-distribute the source, |
we require that you make it clear in the source that the code was |
descended from Apple Sample Code, but that you've made changes. |
Change History (most recent first): |
9/9/99 by John Montbriand |
*/ |
#include "FinderDragPro.h" |
#include "GetIconSuiteFromFinder.h" |
#include "FDPUtilities.h" |
#include <Fonts.h> |
#include <Dialogs.h> |
#include <Icons.h> |
#include <PLStringFuncs.h> |
#include <TextUtils.h> |
#include <Gestalt.h> |
#include <QDOffscreen.h> |
#include <Sound.h> |
#include <StdIO.h> |
#include <String.h> |
#include <StdArg.h> |
#include <Devices.h> |
#include <Folders.h> |
#include <Appearance.h> |
#include <Threads.h> |
#ifndef __MWERKS__ |
QDGlobals qd; /* QuickDraw globals */ |
#endif |
/* application's globals */ |
Boolean gRunning = true; /* true while the application is running, set to false to quit */ |
Boolean gAppleEvents = false; /* true if the Apple event Manager is installed */ |
Boolean gHasDragManager = false; /* true if the Drag Manager is installed */ |
Boolean gCanTranslucentDrag = false; /* true if translucent dragging is available */ |
Boolean gHasIconServices = false; /* true if icon services is available */ |
Boolean gAppearance = false; /* true if the Appearance Manager is installed */ |
Boolean gColorExists = false; /* true if Color QuickDraw is installed */ |
Boolean gForground = true; /* true while FDP is the frontmost application */ |
Boolean gHasThreads = true; /* true if the thread manager is defined */ |
AEIdleUPP gAEIdleProc = NULL; /* idle proc called from AEInteractWithUser */ |
/* globals specific to the main window */ |
DialogPtr gDialog = NULL; /* a pointer to the main dialog */ |
Rect gIconBox; /* area in the window where the information about the current file is drawn */ |
Rect gIconImage; /* area inside of gIconBox where the icon is drawn */ |
ControlHandle gPromiseControl; /* control for setting gDragMode to kUsePromiseHFS */ |
ControlHandle gRegularControl; /* control for setting gDragMode to kUseRegularHFS */ |
short gDragMode = kUseRegularHFS; /* determines the type of data we will provide for drags */ |
PicHandle gSplashPict; /* splash image displayed in gIconBox when there is no file selected */ |
UserItemUPP gFDPUserItemProc = NULL; /* UPP for the icon's user item. This routine draws into gIconBox */ |
DragReceiveHandlerUPP gMainReceiveHandler = NULL; /* receive handler for the main dialog */ |
DragTrackingHandlerUPP gMainTrackingHandler = NULL; /* tracking handler for the main dialog */ |
DragSendDataUPP gSendDataProc = NULL; /* send data proc for the main dialog -- only used for promise hfs flavors. */ |
/* file related variables */ |
Boolean gFileInDisplay = false; /* true when gFileAlias contains an alias handle */ |
AliasHandle gFileAlias; /* an alias to the last file dragged into the gIconBox */ |
Boolean gFileUpToDate = false; /* true if we have an icon for the file */ |
Handle gIconSuite; /* icon suite for displaying the file -- only used if gHasIconServices is false */ |
IconRef gIconRef; /* an icon services reference to an icon for the file -- only used if gHasIconServices is true */ |
/* gTargetFile refers to the last known location of the file referred to by gFileAlias. once |
every 2 seconds or so this location is compared to the location actually referred to by |
gFileAlias. If they differ, both gTargetFile and the display are updated. */ |
FSSpec gTargetFile; /* refers to the last known location of the file. */ |
OSType gType, gCreator; /* file type and creator for the file in the display */ |
unsigned short gFlags; /* flags for the file in the display */ |
/* COPYING FILES ------------------------------------------------ */ |
/* for promise hfs flavors, FDP returns a fsspec referring to the file |
and then it completes the copy operation later after the drag and drop |
command has been completed. Most of the routines in this section are callbacks |
called by the CopyFileCmd routine defined in FDPUtilities.h. Normally, your |
application would create a new file containing interesting data rather than |
copying an existing one, but for the purposes of this example this is all |
we do here. */ |
/* copy command variables */ |
DialogPtr gCopyProgressWindow = NULL; |
/* HitCopyAsWindow is called when DialogSelect indicates an item has been |
hit in the copy progress window (gCopyProgressWindow). The only item |
we're interested in here is the 'Cancel' button. */ |
static void HitCopyAsWindow(DialogPtr theDialog, EventRecord *ev, short itemNo) { |
if (itemNo == kCProCancelItem) |
AbortCopyOperation(); |
} |
/* CopyProgressProc is called periodically during the file copy. It's main function |
is to swap out our task and allow other operating system services to run; however, |
if the copy operation takes longer than kCopyProgressTicksOffset and the copy operation |
is less than 90% complete it will pull up the copy progress window showing the status |
of the copy. */ |
static void CopyProgressProc(FSSpec *theFile, short message, long percentCompleted) { |
static long gPercentDrawn = 0; |
static unsigned long gCopyStart = 0; |
switch (message) { |
case kCopyStart: |
gPercentDrawn = 0; |
gCopyProgressWindow = NULL; |
gCopyStart = TickCount(); |
break; |
case kCopyRun: |
/* check the status of the copy status window */ |
if (gCopyProgressWindow == NULL) { |
unsigned long now; |
/* has the copy been active longer than kCopyProgressTicksOffset |
and the command is less than 90 % complete. */ |
now = TickCount(); |
if ((now - gCopyStart > kCopyProgressTicksOffset) && (percentCompleted < 90)) { |
Point wLocation = {25, 25}; |
/* display our progress window. Locate it relative to the main window. */ |
ParamText(theFile->name, NULL, NULL, NULL); |
gCopyProgressWindow = GetNewDialog((gAppearance ? kCopyProgressDialog : kPlainCopyProgressDialog), NULL, (WindowPtr) (-1)); |
SetPort(gDialog); |
LocalToGlobal(&wLocation); |
MoveWindow(gCopyProgressWindow, wLocation.h, wLocation.v, true); |
ShowWindow(gCopyProgressWindow); |
DrawDialog(gCopyProgressWindow); /* draw it */ |
gPercentDrawn = 0; |
} |
} else if (gPercentDrawn != percentCompleted) { |
short itemt; |
Handle itemh; |
Rect itemb; |
GetDialogItem(gCopyProgressWindow, kCProIndicatorItem, &itemt, &itemh, &itemb); |
if (gAppearance) { |
SetControlValue((ControlHandle) itemh, percentCompleted); |
} else { |
Str255 textbuf; |
textbuf[0] = sprintf((char*) textbuf+1, "%d%% Complete", percentCompleted); |
SetDialogItemText(itemh, textbuf); |
} |
gPercentDrawn = percentCompleted; |
} |
break; |
case kCopyEnd: |
if (gCopyProgressWindow != NULL) DisposeDialog(gCopyProgressWindow); |
break; |
} |
} |
static void MyCopyErrorHandler(FSSpec *theFile, short errorCode) { |
if (errorCode == kCannotCopyDirError) |
ParamAlert(kCannotCopyDirAlert, theFile->name, NULL); |
else if (errorCode != userCanceledErr) { |
Str255 errNum; |
NumToString(errorCode, errNum); |
ParamAlert(kCopyFailedAlert, theFile->name, errNum); |
} |
} |
/* SENDING DRAGS ------------------------------------------------ */ |
/* MyDragSendDataProc is the send data procedure used for providing promised |
flavors. In this routine we begin the copy operation to create the promised |
file by calling StartCopyCommand and return a FSSpec to the caller referring |
to the new file. */ |
static pascal OSErr MyDragSendDataProc(FlavorType flavorType, void *refcon, ItemReference itemRef, DragReference dragRef) { |
OSErr err = noErr; |
AEDesc dropLocDesc, targetDirDesc; |
FSSpec theTarget, theSource, destinationDir; |
long destinationDirID; |
Str255 targetName; |
CInfoPBRec cat; |
Boolean wasChanged; |
FInfo srcInfo, dstInfo; |
Boolean destCreated; |
/* set up locals */ |
AECreateDesc(typeNull, NULL, 0, NULL); |
AECreateDesc(typeNull, NULL, 0, NULL); |
destCreated = false; |
/* get the drop location where */ |
err = GetDropLocation(dragRef, &dropLocDesc); |
if (err != noErr) goto bail; |
/* attempt to convert the location record to a FSSpec. By doing it this way |
instead of looking at the descriptorType field, we don't need to know what |
type the location originally was or what coercion handlers are installed. */ |
err = AECoerceDesc(&dropLocDesc, typeFSS, &targetDirDesc); |
if (err != noErr) goto bail; |
BlockMoveData(*targetDirDesc.dataHandle, &destinationDir, sizeof(FSSpec)); |
/* establish the target directory */ |
cat.hFileInfo.ioNamePtr = destinationDir.name; |
cat.hFileInfo.ioVRefNum = destinationDir.vRefNum; |
cat.hFileInfo.ioDirID = destinationDir.parID; |
cat.hFileInfo.ioFDirIndex = 0; |
err = PBGetCatInfoSync(&cat); |
if (err != noErr) goto bail; |
destinationDirID = cat.hFileInfo.ioDirID; |
/* construct the target FSSpec, verify the name is free... */ |
err = GetAliasInfo(gFileAlias, asiAliasName, targetName); |
if (err != noErr) goto bail; |
err = FSMakeFSSpec(destinationDir.vRefNum, destinationDirID, targetName, &theTarget); |
if (err == noErr) { err = dupFNErr; goto bail; } |
if (err != fnfErr) goto bail; |
/* find the source file.. */ |
err = ResolveAliasQuietly(NULL, gFileAlias, &theSource, &wasChanged); |
if (err != noErr) goto bail; |
/* get the source file's finder info */ |
err = FSpGetFInfo(&theSource, &srcInfo); |
if (err != noErr) goto bail; |
/* create the destination file. Unless the promised file is created in |
the drag send proc, the Finder will not position the icon correctly. */ |
err = FSpCreate(&theTarget, srcInfo.fdCreator, srcInfo.fdType, smSystemScript); |
if (err != noErr) goto bail; |
destCreated = true; |
/* set the destintation's flags */ |
err = FSpGetFInfo(&theTarget, &dstInfo); |
if (err != noErr) goto bail; |
dstInfo.fdFlags = (srcInfo.fdFlags & (~kHasBeenInited)); |
err = FSpSetFInfo(&theTarget, &dstInfo); |
if (err != noErr) goto bail; |
/* begin the copy command. Now that the file has been created, we defer the |
remainder of the copy operation until after the drag. CopyFileCmd copies the |
resource fork and the data fork from a background thread. */ |
err = CopyFileCmd(&theSource, &theTarget, CopyProgressProc, MyCopyErrorHandler); |
if (err != noErr) goto bail; |
/* return a reference to the new file to the caller */ |
err = SetDragItemFlavorData(dragRef, itemRef, flavorType, &theTarget, sizeof(theTarget), 0); |
if (err != noErr) goto bail; |
/* clean up and leave */ |
AEDisposeDesc(&targetDirDesc); |
AEDisposeDesc(&dropLocDesc); |
return noErr; |
bail: |
if (destCreated) FSpDelete(&theTarget); |
AEDisposeDesc(&targetDirDesc); |
AEDisposeDesc(&dropLocDesc); |
return err; |
} |
/* DragOut performs a drag operation from the main window to |
somewhere else. if gDragMode is kUsePromiseHFS, then DragOut |
performs a drag using a promised hfs flavor that triggers a copy |
operation when successful. If translucent dragging is supported, then |
a translucent drag image of the file's icon is added to the drag. */ |
static pascal OSErr DragOut(const EventRecord *event) { |
DragReference dragRef; |
Boolean dragRefExists; |
Boolean hasDragImage; |
GWorldPtr imageGWorld; |
RgnHandle maskRgn, dragRgn, insetRgn; |
Point offsetPt, globalOrigin; |
OSErr err; |
Rect imageRect; |
HFSFlavor theFlavor; |
/* set up locals */ |
dragRefExists = false; |
hasDragImage = false; |
imageGWorld = NULL; |
maskRgn = NULL; |
dragRgn = NULL; |
insetRgn = NULL; |
/* create a new dragreference */ |
err = NewDrag(&dragRef); |
if (err != noErr) goto bail; |
dragRefExists = true; |
/* convert the file alias to a HFSFlavor structure */ |
err = MakeHFSFlavorFromAlias(gFileAlias, &theFlavor); |
if (err != noErr) goto bail; |
/* add our flavors to the drag */ |
if (gDragMode == kUsePromiseHFS) { /* if the option key is down work in promises */ |
PromiseHFSFlavor phfs; |
/* add a send data proc */ |
err = SetDragSendProc(dragRef, gSendDataProc, NULL); |
if (err != noErr) goto bail; |
/* add the promise flavor first */ |
phfs.fileType = theFlavor.fileType; |
phfs.fileCreator = theFlavor.fileCreator; |
phfs.fdFlags = theFlavor.fdFlags; |
phfs.promisedFlavor = kPromisedFlavor; |
err = AddDragItemFlavor(dragRef, kFDPDragItemID, flavorTypePromiseHFS, &phfs, sizeof(phfs), flavorNotSaved); |
if (err != noErr) goto bail; |
/* add the HFS flavor immediately after the promise */ |
err = AddDragItemFlavor(dragRef, kFDPDragItemID, kPromisedFlavor, NULL, 0, flavorNotSaved); |
if (err != noErr) goto bail; |
} else if (gDragMode == kUseRegularHFS) { |
/* add a hfs flavor to the drag */ |
err = AddDragItemFlavor(dragRef, kFDPDragItemID, flavorTypeHFS, &theFlavor, sizeof(HFSFlavor), flavorNotSaved); |
if (err != noErr) goto bail; |
} else { |
err = paramErr; |
goto bail; |
} |
/* add a translucent image, if appropriate */ |
if (gCanTranslucentDrag) { |
err = IconsToMaskedPixMap(&gIconImage, gIconSuite, gIconRef, &imageGWorld, &maskRgn); |
if (err == noErr) { |
imageRect = gIconImage; |
OffsetRect(&imageRect, -imageRect.left, -imageRect.top); |
SetPt(&offsetPt, gIconImage.left, gIconImage.top); |
LocalToGlobal(&offsetPt); |
err = SetDragImage(dragRef, GetGWorldPixMap(imageGWorld), maskRgn, offsetPt, kDragStandardTranslucency); |
if (err != noErr) goto bail; |
} |
} |
/* calculate an outline-style drag region */ |
if ((dragRgn = NewRgn()) == NULL) { err = memFullErr; goto bail; } |
if (gHasIconServices) |
err = IconRefToRgn(dragRgn, &gIconImage, kAlignNone, kIconServicesNormalUsageFlag, gIconRef); |
else err = IconSuiteToRgn(dragRgn, &gIconImage, kAlignNone, gIconSuite); |
if (err != noErr) goto bail; |
if ((insetRgn = NewRgn()) == NULL) { err = memFullErr; goto bail; } |
CopyRgn(dragRgn, insetRgn); |
InsetRgn(insetRgn, 1, 1); |
DiffRgn(dragRgn, insetRgn, dragRgn); |
SetPt(&globalOrigin, qd.thePort->portRect.left, qd.thePort->portRect.top); |
LocalToGlobal(&globalOrigin); |
OffsetRgn(dragRgn, globalOrigin.h, globalOrigin.v); |
/* perform the drag operation */ |
err = TrackDrag(dragRef, event, dragRgn); |
if (err == userCanceledErr) err = noErr; |
if (err != noErr) goto bail; |
/* clean up and leave */ |
if (imageGWorld != NULL) DisposeGWorld(imageGWorld); |
if (maskRgn != NULL) DisposeRgn(maskRgn); |
DisposeRgn(dragRgn); |
DisposeRgn(insetRgn); |
DisposeDrag(dragRef); |
return noErr; |
bail: /* error recovery */ |
if (imageGWorld != NULL) DisposeGWorld(imageGWorld); |
if (maskRgn != NULL) DisposeRgn(maskRgn); |
if (dragRgn != NULL) DisposeRgn(dragRgn); |
if (insetRgn != NULL) DisposeRgn(insetRgn); |
if (dragRefExists) DisposeDrag(dragRef); |
return err; |
} |
/* RECEIVING DRAGS ------------------------------------------------ */ |
/* ApproveDragReference is called by the drag tracking handler to determine |
if the contents of the drag can be handled by our receive handler. |
This routine checks to see if there is only one item in the drag |
and that item contains either one of two flavors: 'flavorTypeHFS' |
or 'flavorTypePromiseHFS'. If the option key was held down at |
the beginning of the drag, then this routine only checks for |
the 'flavorTypePromiseHFS' in the first drag item. |
We accept one item and one item only. |
Note that if a flavor can't be found, it's not really an |
error; it only means the flavor wasn't there and we should |
not accept the drag. Therefore, we translate 'badDragFlavorErr' |
into a 'false' value for '*approved'. */ |
static pascal OSErr ApproveDragReference(DragReference theDragRef, Boolean *approved) { |
OSErr err; |
UInt16 itemCount; |
DragAttributes dragAttrs; |
FlavorFlags flavorFlags; |
ItemReference theItem; |
/* we cannot drag to our own window */ |
if ((err = GetDragAttributes(theDragRef, &dragAttrs)) != noErr) goto bail; |
if ((dragAttrs & kDragInsideSenderWindow) != 0) { err = userCanceledErr; goto bail; } |
/* we only accept drags containing one item */ |
if ((err = CountDragItems(theDragRef, &itemCount)) != noErr) goto bail; |
if (itemCount != 1) { err = paramErr; goto bail; } |
/* gather information about the drag & a reference to item one. */ |
if ((err = GetDragItemReferenceNumber(theDragRef, 1, &theItem)) != noErr) goto bail; |
/* check for flavorTypeHFS */ |
err = GetFlavorFlags(theDragRef, theItem, flavorTypeHFS, &flavorFlags); |
if (err == noErr) { |
*approved = true; |
return noErr; |
} else if (err != badDragFlavorErr) |
goto bail; |
/* check for flavorTypePromiseHFS */ |
err = GetFlavorFlags(theDragRef, theItem, flavorTypePromiseHFS, &flavorFlags); |
if (err == noErr) { |
*approved = true; |
return noErr; |
} else if (err != badDragFlavorErr) |
goto bail; |
/* none of our flavors were found */ |
*approved = false; |
return noErr; |
bail: |
/* an error occured, clean up. set result to false. */ |
*approved = false; |
return err; |
} |
/* these routines are used both in the receive handler and inside of the |
tracking handler. The following variables are shared between MyDragTrackingHandler |
and MyDragReceiveHandler. */ |
static Boolean gApprovedDrag = false; /* set to true if the drag is approved */ |
static Boolean gInIconBox = false; /* set true if the drag is inside our drop box */ |
/* MyDragTrackingHandler is called for tracking the mouse while a drag is passing over our |
window. if the drag is approved, then the drop box will be hilitied appropriately |
as the mouse passes over it. */ |
static pascal OSErr MyDragTrackingHandler(DragTrackingMessage message, WindowPtr theWindow, void *refCon, DragReference theDragRef) { |
/* we're drawing into the image well if we hilite... */ |
switch (message) { |
case kDragTrackingEnterWindow: |
{ Point mouse; |
gApprovedDrag = false; |
if (theWindow == FrontWindow()) { |
if (ApproveDragReference(theDragRef, &gApprovedDrag) != noErr) break; |
if ( ! gApprovedDrag ) break; |
SetPort(theWindow); |
GetMouse(&mouse); |
if (PtInRect(mouse, &gIconBox)) { /* if we're in the box, hilite... */ |
SetPort(theWindow); |
gInIconBox = (ShowDragHiliteBox(theDragRef, &gIconBox) == noErr); |
} |
} |
} |
break; |
case kDragTrackingInWindow: |
if (gApprovedDrag) { |
Point mouse; |
SetPort(theWindow); |
GetMouse(&mouse); |
if (PtInRect(mouse, &gIconBox)) { |
if ( ! gInIconBox) { /* if we're entering the box, hilite... */ |
SetPort(theWindow); |
gInIconBox = (ShowDragHiliteBox(theDragRef, &gIconBox) == noErr); |
} |
} else if (gInIconBox) { /* if we're exiting the box, unhilite... */ |
HideDragHilite(theDragRef); |
gInIconBox = false; |
} |
} |
break; |
case kDragTrackingLeaveWindow: |
if (gApprovedDrag && gInIconBox) { |
HideDragHilite(theDragRef); |
} |
gApprovedDrag = gInIconBox = false; |
break; |
} |
return noErr; // there's no point in confusing Drag Manager or its caller |
} |
/* SetDragAndDropDirectory sets the drop location for the drag reference |
to the indicated directory. This routine will be called from inside of the |
drag receive handler when a promised hfs flavor about to be received. The purpose |
of the call to SetDropLocation is to tell the sending application where to put the |
promised file or folder. This routine sets up parameters referring to the |
(vRefNum, dirID) directory and calls SetDropLocation. */ |
static OSErr SetDragAndDropDirectory(DragReference theDragRef, short vRefNum, long dirID) { |
Str255 name; |
CInfoPBRec cat; |
FSSpec spec; |
AliasHandle theAlias; |
AEDesc theDropLocation; |
OSErr err; |
/* set up locals */ |
theAlias = NULL; |
AECreateDesc(typeNull, NULL, 0, NULL); |
/* find out the folder's parent ID */ |
cat.hFileInfo.ioNamePtr = name; |
cat.hFileInfo.ioVRefNum = vRefNum; |
cat.hFileInfo.ioDirID = dirID; |
cat.hFileInfo.ioFDirIndex = -1; |
err = PBGetCatInfoSync(&cat); |
if (err != noErr) goto bail; |
/* now convert it to a FSSpec record */ |
err = FSMakeFSSpec(vRefNum, cat.dirInfo.ioDrParID, name, &spec); |
if (err != noErr) goto bail; |
/* convert the FSSpec to an alias */ |
err = NewAlias(NULL, &spec, &theAlias); |
if (err != noErr) goto bail; |
/* and make it into a descriptor record */ |
HLock((Handle) theAlias); |
err = AECreateDesc(typeAlias,*theAlias, GetHandleSize((Handle) theAlias), &theDropLocation); |
if (err != noErr) goto bail; |
/* set it as the drag's drop location */ |
err = SetDropLocation(theDragRef, &theDropLocation); |
if (err != noErr) goto bail; |
/* we're done, clean up */ |
AEDisposeDesc(&theDropLocation); |
DisposeHandle((Handle) theAlias); |
return noErr; |
bail: |
AEDisposeDesc(&theDropLocation); |
if (theAlias != NULL) DisposeHandle((Handle) theAlias); |
return err; |
} |
/* MyDragReceiveHandler is the receive handler for the main window. It is called |
when a file or folder (or a promised file or folder) is dropped into the drop |
box in the main window. Here, if the drag reference has been approved in the |
track drag call, we handle three different cases: |
1. standard hfs flavors, |
2. promised flavors provided by find file, |
3. promised flavors provided by other applications. |
After receiving a file, the display is updated. Any promised files are |
placed on the desktop. */ |
static pascal OSErr MyDragReceiveHandler(WindowPtr theWindow, void *refcon, DragReference theDragRef) { |
ItemReference theItem; |
HFSFlavor targetFile; |
PromiseHFSFlavor targetPromise; |
FSSpec targetSpec; |
Size theSize; |
OSErr err; |
/* validate the drag. Recall the receive handler will only be called after |
the tracking handler has received a kDragTrackingInWindow event. As a result, |
the gApprovedDrag and gInIconBox will be defined when we arrive here. Hence, |
there is no need to spend extra time validating the drag at this point. */ |
if ( ! (gApprovedDrag && gInIconBox) ) { err = userCanceledErr; goto bail; } |
/* get the first item reference */ |
if ((err = GetDragItemReferenceNumber(theDragRef, 1, &theItem)) != noErr) goto bail; |
/* try to get a HFSFlavor*/ |
theSize = sizeof(HFSFlavor); |
err = GetFlavorData(theDragRef, theItem, flavorTypeHFS, &targetFile, &theSize, 0); |
if (err == noErr) { |
SetNewDisplay(&targetFile); |
return noErr; |
} else if (err != badDragFlavorErr) goto bail; |
/* try to get a promised HFSFlavor*/ |
theSize = sizeof(PromiseHFSFlavor); |
err = GetFlavorData(theDragRef, theItem, flavorTypePromiseHFS, &targetPromise, &theSize, 0); |
if (err != noErr) goto bail; |
/* check for a drop from find file */ |
if (targetPromise.promisedFlavor == kPromisedFlavorFindFile) { |
/* from find file, no need to set the file location... */ |
theSize = sizeof(FSSpec); |
err = GetFlavorData(theDragRef, theItem, targetPromise.promisedFlavor, &targetSpec, &theSize, 0); |
if (err != noErr) goto bail; |
} else { |
short deskVol; |
long deskDir; |
/* we'll put promised files on the desktop */ |
err = FindFolder(kOnSystemDisk, kDesktopFolderType, kCreateFolder, &deskVol, &deskDir); |
if (err != noErr) goto bail; |
/* set the drag destination to the desktop */ |
err = SetDragAndDropDirectory(theDragRef, deskVol, deskDir); |
if (err != noErr) goto bail; |
/* get the promised file */ |
theSize = sizeof(FSSpec); |
err = GetFlavorData(theDragRef, theItem, targetPromise.promisedFlavor, &targetSpec, &theSize, 0); |
if (err != noErr) goto bail; |
/* verify the promise structure, make sure it's valid. */ |
err = ValidFSSpec(&targetSpec); |
if (err != noErr) goto bail; |
} |
/* display the located file*/ |
SetNewDisplay(&targetFile); |
return noErr; |
bail: |
return err; |
} |
/* THE MAIN FINDER DRAG PRO WINDOW ------------------------------------------------ */ |
/* SetNewDisplay is called to set the file or folder being displayed in the main window. |
Here, structures are deallocated and an alias is saved referring to the file. |
SetNewDisplay is called from the drag receive handler and since it is not |
safe to call "GetIconSuiteFromFinder()" from inside of the drag receive handler |
(it uses apple events), the flag gFileUpToDate is used to defer that operation |
until the next time ValidateFDPWindowDisplay. ValidateFDPWindowDisplay is |
called from the main loop. If targetFile is NULL, then the display is cleared. */ |
void SetNewDisplay(HFSFlavor *targetFile) { |
/* remove the old file */ |
if (gFileInDisplay) { |
DisposeHandle((Handle) gFileAlias); |
if (gFileUpToDate) { |
if (gHasIconServices) |
ReleaseIconRef(gIconRef); |
else DisposeIconSuite(gIconSuite, true); |
gFileUpToDate = false; |
} |
gFileInDisplay = false; |
} |
/* set the new file, if there is one */ |
if (targetFile != NULL) { |
gType = targetFile->fileType; |
gCreator = targetFile->fileCreator; |
gFlags = targetFile->fdFlags; |
gTargetFile = targetFile->fileSpec; |
gFileInDisplay = (NewAliasMinimal(&gTargetFile, &gFileAlias) == noErr); |
} |
/* post an update for the window */ |
SetPort(gDialog); |
InvalRect(&gIconBox); |
} |
/* ValidateFDPWindowDisplay is called from the main loop, before update events, |
and when the application is switching in. It works together with SetNewDisplay. |
SetNewDisplay is called from the drag receive handler where sometimes it is |
unsafe to retrieve a file's icons. Instead of gathering the icon information inside |
of SetNewDisplay, SetNewDisplay sets the flag gFileUpToDate. ValidateFDPWindowDisplay |
watches gFileUpToDate. When this flag is false and there is a new file to be displayed, |
ValidateFDPWindowDisplay retrieves the icon and posts an update event for the window. |
To minimize resolve alias calls, if the icons have been fetched, then the alias is only |
verified at most once per second. */ |
static OSErr ValidateFDPWindowDisplay(void) { |
Boolean wasChanged; |
OSErr err; |
static unsigned long gLastValidCall = 0; |
unsigned long now; |
wasChanged = false; |
if (gFileInDisplay) { |
now = TickCount(); |
if ( ( ! gFileUpToDate) || (gLastValidCall == 0) || (now - gLastValidCall >= 60)) { |
gLastValidCall = now; |
err = ResolveAliasQuietly(NULL, gFileAlias, &gTargetFile, &wasChanged); |
if (err != noErr) goto bail; |
if ( ! gFileUpToDate) { |
if (gHasIconServices) { |
SInt16 theLabel; |
err = GetIconRefFromFile(&gTargetFile, &gIconRef, &theLabel); |
} else err = GetIconSuiteFromFinder(&gTargetFile, &gIconSuite); |
if (err != noErr) goto bail; |
gFileUpToDate = true; |
wasChanged = true; |
} |
} |
} |
if (wasChanged) { |
SetPort(gDialog); |
InvalRect(&gIconBox); |
} |
return noErr; |
bail: |
SetNewDisplay(NULL); |
return err; |
} |
/* MyFDPUserItem draws the image in the drop box in the window. If the window |
is not the active window, then the image is drawn grayed out. If appearance |
is in use, then the drop box is drawn as a generic well. */ |
static pascal void MyFDPUserItem(WindowPtr theWindow, DialogItemIndex itemNo) { |
Rect rdraw; |
RGBColor sForground, sBackground; |
RGBColor rgbWhite = {0xFFFF, 0xFFFF, 0xFFFF}, rgbBlack = {0, 0, 0}, rgbGray = {0x7FFF, 0x7FFF, 0x7FFF}; |
/* set up */ |
SetPort(theWindow); |
/* set the colors we're using for drawing */ |
if (gColorExists) { |
GetForeColor(&sForground); |
GetBackColor(&sBackground); |
RGBForeColor(&rgbBlack); |
RGBBackColor(&rgbWhite); |
} |
/* draw the frame */ |
if (gAppearance) { |
ThemeDrawState themeDrawState; |
themeDrawState = (gForground && (theWindow == FrontWindow())) ? kThemeStateActive : kThemeStateInactive; |
DrawThemeGenericWell(&gIconBox, themeDrawState, true); |
} else { |
rdraw = gIconBox; |
EraseRect(&gIconBox); |
InsetRect(&rdraw, -1, -1); |
FrameRect(&rdraw); |
} |
/* draw the file information */ |
if (gFileInDisplay && gFileUpToDate) { |
OSErr err; |
short baseLine; |
long textLength; |
char textbuf[256]; |
FontInfo fin; |
Str255 name; |
/* begin drawing */ |
TextFont(kFontIDGeneva); /* geneva */ |
TextSize(9); |
GetFontInfo(&fin); |
/* draw the icon image */ |
if (gHasIconServices) |
err = PlotIconRef(&gIconImage, kAlignNone, kTransformNone, kIconServicesNormalUsageFlag, gIconRef); |
else err = PlotIconSuite(&gIconImage, kAlignNone, kTransformNone, gIconSuite); |
/* draw the file name */ |
baseLine = gIconImage.bottom + fin.ascent; |
memcpy(name, gTargetFile.name, gTargetFile.name[0] + 1); |
TruncString(gIconBox.right - gIconBox.left - 4, name, truncEnd); |
MoveTo((gIconBox.left + gIconBox.right - StringWidth(name))/2, baseLine); |
DrawString(name); |
/* draw the location */ |
textLength = sprintf(textbuf, "vRefNum %d, parID %d", gTargetFile.vRefNum, gTargetFile.parID); |
baseLine += fin.ascent + fin.descent; |
MoveTo((gIconBox.left + gIconBox.right - TextWidth(textbuf, 0, textLength))/2, baseLine); |
DrawText(textbuf, 0, textLength); |
/* draw the type and flags */ |
textLength = sprintf(textbuf, "%.4s %.4s 0x%.4X", &gType, &gCreator, gFlags); |
baseLine += fin.ascent + fin.descent; |
MoveTo((gIconBox.left + gIconBox.right - TextWidth(textbuf, 0, textLength))/2, baseLine); |
DrawText(textbuf, 0, textLength); |
/* end drawing */ |
TextFont(systemFont); /* back to the system font */ |
TextSize(12); |
} else { |
Rect rPic; |
rPic = (**gSplashPict).picFrame; |
OffsetRect(&rPic, -rPic.left, -rPic.top); |
OffsetRect(&rPic, (gIconBox.left + gIconBox.right - rPic.right)/2, (gIconBox.top + gIconBox.bottom - rPic.bottom)/2); |
DrawPicture(gSplashPict, &rPic); |
} |
/* gray the image if we're in the background */ |
if ( ! (gForground && (theWindow == FrontWindow())) ) { |
GrayOutBox(&gIconBox); |
} |
/* restore previous colors */ |
if (gColorExists) { |
RGBForeColor(&sForground); |
RGBBackColor(&sBackground); |
} |
} |
/* CreateFDPWindow creates the main finder drag pro window. It installs |
tracking handlers and sets up the controls and user items in the window. */ |
static pascal OSErr CreateFDPWindow(DialogPtr *theDialog) { |
OSErr err; |
Boolean installedTracker, installedReceiver; |
short itemt; |
Rect itemb; |
Handle itemh; |
DialogPtr dialog; |
/* set up locals for recovery */ |
dialog = NULL; |
installedTracker = installedReceiver = false; |
/* create the dialog */ |
dialog = GetNewDialog(kFDPDialogResource, NULL, (WindowPtr) (-1)); |
if (dialog == NULL) { err = memFullErr; goto bail; } |
/* grab and set up our dialog items */ |
GetDialogItem(dialog, kFDPUserItem, &itemt, (Handle*) &itemh, &gIconBox); |
SetDialogItem(dialog, kFDPUserItem, userItem, (Handle) gFDPUserItemProc, &gIconBox); |
GetDialogItem(dialog, kFDPpromiseItem, &itemt, (Handle*) &gPromiseControl, &itemb); |
GetDialogItem(dialog, kFDPhfsItem, &itemt, (Handle*) &gRegularControl, &itemb); |
/* set initial control values */ |
SetControlValue(gPromiseControl, (gDragMode == kUsePromiseHFS ? 1 : 0)); |
SetControlValue(gRegularControl, (gDragMode == kUseRegularHFS ? 1 : 0)); |
/* calculate the drawn icon's boundary */ |
SetRect(&gIconImage, 0, 0, 32, 32); |
OffsetRect(&gIconImage, (gIconBox.right + gIconBox.left - 32) / 2, gIconBox.top + 16); |
/* install the drag handlers */ |
err = InstallTrackingHandler(gMainTrackingHandler, dialog, NULL); |
if (err != noErr) { err = memFullErr; goto bail; } |
installedTracker = true; |
err = InstallReceiveHandler(gMainReceiveHandler, dialog, NULL); |
if (err != noErr) { err = memFullErr; goto bail; } |
installedReceiver = true; |
/* done, window complete */ |
*theDialog = dialog; |
return noErr; |
bail: |
if (installedReceiver) |
RemoveReceiveHandler(gMainReceiveHandler, dialog); |
if (installedTracker) |
RemoveTrackingHandler(gMainTrackingHandler, dialog); |
if (dialog != NULL) DisposeDialog(dialog); |
return err; |
} |
/* DisposeFDPWindow disposes of any structures allocated for the |
main finder drag pro window. the call to SetNewDisplay with a |
NULL parameter forces it to deallocate any icons and the |
alias allocated for the item being displayed. */ |
static void DisposeFDPWindow(DialogPtr theDialog) { |
SetNewDisplay(NULL); |
RemoveTrackingHandler(gMainTrackingHandler, theDialog); |
RemoveReceiveHandler(gMainReceiveHandler, theDialog); |
DisposeDialog(theDialog); |
} |
/* HitFDPWindow is called when the dialog manager's DialogSelect indicates |
that an item in the main finder drag pro window has been hit. Here, |
either we begin a drag, or we adjust the promise/regular hfs controls */ |
static void HitFDPWindow(DialogPtr theDialog, EventRecord *event, short itemNo) { |
switch (itemNo) { |
case kFDPUserItem: |
if (gFileInDisplay) { |
Point where; |
Boolean isInSuite; |
OSErr err, err2; |
/* set up */ |
SetPort(theDialog); |
where = event->where; |
GlobalToLocal(&where); |
/* if the click is in the icon.... */ |
if (gHasIconServices) |
isInSuite = PtInIconRef(&where, &gIconImage, kAlignNone, kIconServicesNormalUsageFlag, gIconRef); |
else isInSuite = PtInIconSuite(where, &gIconImage, kAlignNone, gIconSuite); |
if (isInSuite) { |
/* draw the icon as 'selected' */ |
if (gHasIconServices) |
err = PlotIconRef(&gIconImage, kAlignNone, kTransformSelected, kIconServicesNormalUsageFlag, gIconRef); |
else err = PlotIconSuite(&gIconImage, kAlignNone, kTransformSelected, gIconSuite); |
if (err == noErr) { |
/* is it a drag command? */ |
if (WaitMouseMoved (event->where)) { |
/* if it is, drag it */ |
err = DragOut(event); |
} |
/* restore the icon's original view */ |
if (gHasIconServices) |
err2 = PlotIconRef(&gIconImage, kAlignNone, kTransformNone, kIconServicesNormalUsageFlag, gIconRef); |
else err2 = PlotIconSuite(&gIconImage, kAlignNone, kTransformNone, gIconSuite); |
if (err == noErr) err = err2; |
} |
if (err != noErr) { |
Str255 errStr; |
NumToString(err, errStr); |
ParamAlert(kDragFailedAlert, errStr, NULL); |
} |
} |
} |
break; |
case kFDPhfsItem: /* use regular hfs drags */ |
gDragMode = kUseRegularHFS; |
SetControlValue(gPromiseControl, 0); |
SetControlValue(gRegularControl, 1); |
break; |
case kFDPpromiseItem: /* use promised hfs drags */ |
gDragMode = kUsePromiseHFS; |
SetControlValue(gPromiseControl, 1); |
SetControlValue(gRegularControl, 0); |
break; |
} |
} |
/* MENU HANDLING ------------------------------------------------ */ |
/* ResetMenus is called to reset the menus immediately before |
either MenuSelect or MenuKey is called. Here, we disable the |
quit command during file copies. */ |
static void ResetMenus(void) { |
MenuHandle fileMenu; |
/* get the file menu from the current menu list */ |
fileMenu = GetMenuHandle(mFile); |
/* disable quit if we're in the middle of copying a file */ |
if (CopyFileInProgress()) |
DisableItem(fileMenu, iQuit); |
else EnableItem(fileMenu, iQuit); |
} |
/* DoMenuCommand is called after either MenuSelect of MenuKey. The |
parameter rawMenuSelectResult is the result from one of these two routines. |
DoMenuCommand parses this result into its two parts, and dispatches |
the menu command as appropriate. */ |
static void DoMenuCommand(long rawMenuSelectResult) { |
short menu, item; |
/* decode the MenuSelect result */ |
menu = (rawMenuSelectResult >> 16); |
if (menu == 0) return; |
item = (rawMenuSelectResult & 0x0000FFFF); |
/* dispatch on result */ |
switch (menu) { |
case mApple: |
if (item == iAbout) { |
/* show the about box. */ |
ParamAlert(kAboutBoxAlert, NULL, NULL); |
} else if (item >= iFirstAppleItem) { |
Str255 deskAccName; |
/* open an apple menu item. */ |
GetMenuItemText(GetMenuHandle(mApple), item, deskAccName); |
OpenDeskAcc(deskAccName); |
} |
break; |
case mFile: |
if (item == iQuit) gRunning = false; /* file.quit == quit */ |
break; |
case mEdit: |
if (item == iClear) SetNewDisplay(NULL); /* edit.clear == clear the display */ |
break; |
} |
/* unhilite the menu once we're done the command */ |
HiliteMenu(0); |
} |
/* APPLE EVENT HANDLERS ------------------------------------------------ */ |
/* BogusFinderEventHandler is an event handler installed for compatibility reasons |
as described below. Not all finders behave in this way, but or universal compatibility, |
applications wishing to drag and drop hfs flavors to the finder should add one of |
these handlers to their code. In cases where the finder does have this problem, |
this routine will lie dormant and will not be used. |
When a HFSFlavor item is provided to the Finder and the drop location is on a |
different volume, the finder will attempt to copy the file to the new volume. As |
a part of the copy operation, the finder sends some apple events to the frontmost |
application (assuming it's the finder) that are used to run the finder's copy progress |
windows. If our application happens to be frontmost, those events will be sent to |
us which will cause the Finder to cancel. By installing the handler, we avoid this problem |
by ignoring the bogus events ourselves.,.... */ |
static pascal OSErr BogusFinderEventHandler(const AppleEvent *theEvent, AppleEvent *theReply, long refcon) { |
return noErr; |
} |
static OSErr GotReqParam(const AppleEvent *apple_event) { |
DescType retType; |
Size actSize; |
OSErr err; |
err = AEGetAttributePtr(apple_event, keyMissedKeywordAttr, typeWildCard, &retType, NULL, 0, &actSize); |
if (err == errAEDescNotFound) |
return noErr; |
else if (err == noErr) |
return errAEEventNotHandled; |
else return err; |
} |
/* OpenApplication is an apple event handler called for 'open application' apple events. */ |
static pascal OSErr OpenApplication(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) { |
OSErr err; |
if ((err = GotReqParam(appleEvt)) != noErr) return err; |
err = CreateFDPWindow(&gDialog); |
if (err != noErr) { |
Str255 errStr; |
NumToString(err, errStr); |
ParamAlert(kOpenAppFailedAlert, errStr, NULL); |
} |
return noErr; |
} |
/* CloseApplication is an apple event handler called for 'close application' apple events. */ |
static pascal OSErr CloseApplication(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) { |
OSErr err; |
if ((err = GotReqParam(appleEvt)) != noErr) return err; |
gRunning = false; |
return noErr; |
} |
/* EVENT HANDLING ------------------------------------------------ */ |
/* HandleNextEvent handles the event in the event record *ev dispatching |
the event to appropriate routines. */ |
static void HandleNextEvent(EventRecord *ev) { |
DialogPtr theDialog; |
WindowPtr theWindow; |
short itemNo; |
/* dialog pre-processing */ |
if (((ev->what == keyDown) || (ev->what == autoKey)) && ((ev->modifiers & cmdKey) != 0)) { |
ResetMenus(); |
DoMenuCommand(MenuKey((char) (ev->message & charCodeMask))); |
} else if (ev->what == osEvt) { |
if ( (((ev->message >> 24) & 0x0FF) == suspendResumeMessage) && ((ev->message & resumeFlag) != 0)) { |
/* switching in */ |
ValidateFDPWindowDisplay(); |
gForground = true; |
} else gForground = false; |
if (gDialog != NULL) { |
HiliteControl(gPromiseControl, (gForground ? 0 : 255)); |
HiliteControl(gRegularControl, (gForground ? 0 : 255)); |
DrawDialog(gDialog); |
} |
} else if (ev->what == updateEvt) { |
if (gDialog == ((DialogPtr) ev->message)) |
ValidateFDPWindowDisplay(); |
} else if (ev->what == activateEvt) { |
if (gDialog == ((DialogPtr) ev->message)) { |
HiliteControl(gPromiseControl, ((gDialog == FrontWindow()) ? 0 : 255)); |
HiliteControl(gRegularControl, ((gDialog == FrontWindow()) ? 0 : 255)); |
DrawDialog(gDialog); |
} |
} |
/* handle clicks in the dialog window */ |
if (IsDialogEvent(ev)) |
if (DialogSelect(ev, &theDialog, &itemNo)) { |
if (theDialog == gDialog) |
HitFDPWindow(theDialog, ev, itemNo); |
else if (theDialog == gCopyProgressWindow) |
HitCopyAsWindow(theDialog, ev, itemNo); |
} |
/* clicks and apple events... */ |
if (ev->what == kHighLevelEvent && gAppleEvents) { |
AEProcessAppleEvent(ev); |
} else if (ev->what == nullEvent) { |
ValidateFDPWindowDisplay(); /* revalidate once every two seconds */ |
} else if (ev->what == mouseDown) |
switch (FindWindow(ev->where, &theWindow)) { |
/* menu bar clicks */ |
case inMenuBar: |
ResetMenus(); |
DoMenuCommand(MenuSelect(ev->where)); |
break; |
/* clicks in the close box, close the app */ |
case inGoAway: |
if (TrackGoAway(theWindow, ev->where)) { |
gRunning = false; |
} |
break; |
/* allow window drags */ |
case inDrag: |
if (theWindow == FrontWindow()) { |
Rect boundsRect = { -32000, -32000, 32000, 32000}; |
DragWindow(theWindow, ev->where, &boundsRect); |
} |
break; |
/* desktop clicks, etc... */ |
case inSysWindow: |
SystemClick(ev, theWindow); |
break; |
} |
} |
/* ProcessNextEvent calls WaitNextEvent to get the next event and then it passes |
the event along to the HandleNextEvent routine. sleepTime is passed to the |
WaitNextEvent routine in the sleep parameter. */ |
void ProcessNextEvent(long sleepTime) { |
EventRecord ev; |
/* get the next event */ |
if ( ! WaitNextEvent(everyEvent, &ev, sleepTime, NULL) ) |
ev.what = nullEvent; |
HandleNextEvent(&ev); |
} |
/* FDPIdleProcedure is the idle procedure called by AEInteractWithUser while we are waiting |
for the application to be pulled into the forground. It simply passes the event along |
to HandleNextEvent */ |
static pascal Boolean FDPIdleProcedure(EventRecord *theEvent, long *sleepTime, RgnHandle *mouseRgn) { |
HandleNextEvent(theEvent); |
return false; |
} |
/* ParamAlert is a general alert handling routine. If Apple events exist, then it |
calls AEInteractWithUser to ensure the application is in the forground, and then |
it displays an alert after passing the s1 and s2 parameters to ParamText. */ |
short ParamAlert(short alertID, StringPtr s1, StringPtr s2) { |
if (gAppleEvents) |
AEInteractWithUser(kNoTimeOut, NULL, gAEIdleProc); |
ParamText(s1, s2, NULL, NULL); |
return Alert(alertID, NULL); |
} |
/* MAIN ------------------------------------------------ */ |
int main(void) { |
OSErr err; |
long response; |
/* ***** set up the os ***** */ |
/* set up our app */ |
SetApplLimit(GetApplLimit()); |
MaxApplZone(); |
InitGraf(&qd.thePort); |
InitFonts(); |
InitWindows(); |
TEInit(); |
InitMenus(); |
InitDialogs(0); |
FlushEvents(everyEvent, 0); |
InitCursor(); |
/* ***** look at machine features ***** */ |
/* apple events??? */ |
if (Gestalt(gestaltAppleEventsAttr, &response) != noErr) response = 0; |
gAppleEvents = ((response & (1<<gestaltAppleEventsPresent)) != 0); |
/* check for the drag manager & translucent feature??? */ |
if (Gestalt(gestaltDragMgrAttr, &response) != noErr) response = 0; |
gHasDragManager = ((response & (1 << gestaltDragMgrPresent)) != 0); |
gCanTranslucentDrag = ((response & (1 << gestaltDragMgrHasImageSupport)) != 0); |
/* icon services??? */ |
if (Gestalt(gestaltIconUtilitiesAttr, &response) != noErr) response = 0; |
gHasIconServices = ((response & (1 << gestaltIconUtilitiesHasIconServices)) != 0); |
/* colour quickdraw */ |
if (Gestalt(gestaltQuickdrawVersion, &response) != noErr) response = 0; |
gColorExists = (response >= gestalt8BitQD); |
/* colour quickdraw */ |
if (Gestalt(gestaltThreadMgrAttr, &response) != noErr) response = 0; |
gHasThreads = ((response & (1<<gestaltThreadMgrPresent)) != 0); |
/* appearance */ |
if (Gestalt(gestaltAppearanceAttr, &response) != noErr) response = 0; |
if ((response & (1<<gestaltAppearanceExists)) != 0) { |
err = RegisterAppearanceClient(); |
if (err != noErr) goto bail; |
gAppearance = true; |
} |
/* ***** abort if features we need aren't present ***** */ |
if ( ! gHasDragManager) { err = kNoDragMgrError; goto bail; } |
if ( ! gHasThreads) { err = kNoThreadMgrError; goto bail; } |
/* ***** install apple event handlers ***** */ |
/* install our Apple event handlers */ |
if (gAppleEvents) { |
AEEventHandlerUPP aehandler; |
/* the bogus finder event handler */ |
aehandler = NewAEEventHandlerProc(BogusFinderEventHandler); |
if (aehandler == NULL) { err = memFullErr; goto bail; } |
err = AEInstallEventHandler ('cwin','****', aehandler, 0, false); |
if (err != noErr) goto bail; |
/* standard apple events */ |
aehandler = NewAEEventHandlerProc(OpenApplication); |
if (aehandler == NULL) { err = memFullErr; goto bail; } |
err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, aehandler, 0, false); |
if (err != noErr) goto bail; |
aehandler = NewAEEventHandlerProc(CloseApplication); |
if (aehandler == NULL) { err = memFullErr; goto bail; } |
err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, aehandler, 0, false); |
if (err != noErr) goto bail; |
} |
/* ***** initialize the application's globals ***** */ |
/* set up our routine descriptors */ |
gFDPUserItemProc = NewUserItemProc(MyFDPUserItem); |
if (gFDPUserItemProc == NULL) { err = memFullErr; goto bail; } |
gMainTrackingHandler = NewDragTrackingHandlerProc(MyDragTrackingHandler); |
if (gMainTrackingHandler == NULL) { err = memFullErr; goto bail; } |
gMainReceiveHandler = NewDragReceiveHandlerProc(MyDragReceiveHandler); |
if (gMainReceiveHandler == NULL) { err = memFullErr; goto bail; } |
gSendDataProc = NewDragSendDataProc(MyDragSendDataProc); |
if (gSendDataProc == NULL) { err = memFullErr; goto bail; } |
gAEIdleProc = NewAEIdleProc(FDPIdleProcedure); |
if (gAEIdleProc == NULL) { err = memFullErr; goto bail; } |
/* get other resources */ |
gSplashPict = GetPicture(kFDPSpashPictResource); |
if (gSplashPict == NULL) { err = resNotFound; goto bail; } |
/* ***** set up the menu bar ***** */ |
SetMenuBar(GetNewMBar(kFDPMenuBarResource)); |
DrawMenuBar(); |
AppendResMenu(GetMenuHandle(mApple), 'DRVR'); |
/* ***** run the app ***** */ |
while (gRunning) { |
ProcessNextEvent(CopyFileInProgress() ? kCopySleepTime : kNormalSleepTime); |
YieldToAnyThread(); |
} |
/* ***** tear down ***** */ |
bail: |
switch (err) { |
case userCanceledErr: |
case noErr: |
/* no alert here */ |
break; |
case kNoDragMgrError: |
ParamAlert(kNoDragManagerAlert, NULL, NULL); |
break; |
default: |
{ Str255 errStr; |
NumToString(err, errStr); |
ParamAlert(kMainFailedAlert, errStr, NULL); |
} |
break; |
} |
if (gDialog != NULL) DisposeFDPWindow(gDialog); |
if (gAppearance) |
UnregisterAppearanceClient(); |
ExitToShell(); |
return 0; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14