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.
FDPUtilities.c
/* |
File: FDPUtilities.c |
Description: utilities used in FinderDragPro.c. Routines in this file are used in the |
FinderDragPro.c file; however, since they are not directly related |
to the example, they have been moved here to simplify the main file. |
Author: John Montbriand |
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 created by John Montbriand |
*/ |
#include "FDPUtilities.h" |
#include <QuickDraw.h> |
#include <Gestalt.h> |
#include <Palettes.h> |
#include <Threads.h> |
#include <fp.h> |
/* ValidFSSpec verifies that *spec refers is formatted correctly, and it |
verifies that it refers to an existing file in an existing directory on |
and existing volume. If *spec is valid, the function returns noErr, |
otherwise an error is returned. */ |
OSErr ValidFSSpec(FSSpec *spec) { |
FInfo fndrInfo; |
/* check the name's size */ |
if (spec->name[0] + (sizeof(FSSpec) - sizeof(spec->name)) > sizeof(FSSpec)) return paramErr; |
/* ckeck if it refers to a file */ |
return FSpGetFInfo(spec, &fndrInfo); |
} |
/* ResolveAliasQuietly resolves an alias using a fast search with no user interaction. Our main loop |
periodically resolves gFileAlias comparing the result to gTargetFile to keep the display up to date. |
As a result, we would like the resolve alias call to be as quick as possible AND since the application |
may be in the background when it is called, we don't want any user interaction. */ |
OSErr ResolveAliasQuietly(ConstFSSpecPtr fromFile, AliasHandle alias, FSSpec *target, Boolean *wasChanged) { |
short aliasCount; |
Boolean needsUpdate; |
OSErr err; |
/* set up locals */ |
aliasCount = 1; |
needsUpdate = false; |
*wasChanged = false; |
/* call MatchAlias to resolve the alias. |
kARMNoUI = no user interaction, |
kARMSearch = do a fast search. */ |
err = MatchAlias(NULL, (kARMNoUI | kARMSearch), alias, &aliasCount, target, &needsUpdate, NULL, NULL); |
if (err == noErr) { |
/* if the alias was changed, update it. */ |
err = UpdateAlias(fromFile, target, alias, wasChanged); |
} |
return err; |
} |
/* MakeHFSFlavorFromAlias converts an alias handle into a HFSFlavor |
structure filling in the fields with their correct values. */ |
OSErr MakeHFSFlavorFromAlias(AliasHandle theAlias, HFSFlavor *theFlavor) { |
OSErr err = noErr; |
Boolean wasChanged; |
CInfoPBRec cat; |
/* resolve the alias with no user interaction. Do a fast search. */ |
err = ResolveAliasQuietly(NULL, theAlias, &theFlavor->fileSpec, &wasChanged); |
if (err != noErr) return err; |
/* pull some information about the file */ |
cat.hFileInfo.ioNamePtr = theFlavor->fileSpec.name; |
cat.hFileInfo.ioVRefNum = theFlavor->fileSpec.vRefNum; |
cat.hFileInfo.ioDirID = theFlavor->fileSpec.parID; |
cat.hFileInfo.ioFDirIndex = 0; |
err = PBGetCatInfoSync(&cat); |
if (err != noErr) return err; |
/* save the finder flags */ |
theFlavor->fdFlags = cat.hFileInfo.ioFlFndrInfo.fdFlags; |
/* calculate the type and creator */ |
if (theFlavor->fileSpec.parID == fsRtParID) { |
/* volumes have parrent id == 1 */ |
theFlavor->fileCreator = 'MACS'; |
theFlavor->fileType = 'disk'; |
} else if ((cat.hFileInfo.ioFlAttrib & ioDirMask) != 0) { |
/* directories have bit 4 set in the attributes */ |
theFlavor->fileCreator = 'MACS'; |
theFlavor->fileType = 'fold'; |
} else { |
/* assume anything else is a file */ |
theFlavor->fileCreator = cat.hFileInfo.ioFlFndrInfo.fdCreator; |
theFlavor->fileType = cat.hFileInfo.ioFlFndrInfo.fdType; |
} |
/* done */ |
return noErr; |
} |
/* IconsToMaskedPixMap converts either an IconServices icon reference or a IconUtilities icon suite into a |
(GWorldPtr, RgnHandle) pair appropriate for dragging as a transparent icon image. if iconReference |
then calls to IconServices are made, if iconSuite is not null, then calls to icon services are made. |
The resulting graphics world and region handle are returned in *imageGWorld and *maskRgn. */ |
OSErr IconsToMaskedPixMap(const Rect *iconRect, Handle iconSuite, IconRef iconReference, |
GWorldPtr *imageGWorld, RgnHandle *maskRgn) { |
OSErr err; |
GWorldPtr theWorld; |
RgnHandle theMask; |
Rect imageRect; |
Point offsetPt; |
GDHandle saveDevice; |
CGrafPtr savePort; |
PixMapHandle imagePixMap; |
/* set up locals */ |
theWorld = NULL; |
theMask = NULL; |
imageRect = *iconRect; |
OffsetRect(&imageRect, -imageRect.left, -imageRect.top); |
LocalToGlobal(&offsetPt); |
/* create the graphics world */ |
err = NewGWorld(&theWorld, 8, &imageRect, NULL, NULL, 0); |
if (err != noErr) goto bail; |
if (theWorld == NULL) { err = memFullErr; goto bail; } /* often missed... */ |
/* set up the mask region */ |
theMask = NewRgn(); |
if (theMask == NULL) { err = memFullErr; goto bail; } |
/* calculate the icon's mask region */ |
if (iconReference != NULL) |
err = IconRefToRgn(theMask, &imageRect, kAlignNone, kIconServicesNormalUsageFlag, iconReference); |
else if (iconSuite != NULL) |
err = IconSuiteToRgn(theMask, &imageRect, kAlignNone, iconSuite); |
else err = paramErr; |
if (err != noErr) goto bail; |
/* set up the drawing environment */ |
GetGWorld (&savePort, &saveDevice); |
LockPixels((imagePixMap = GetGWorldPixMap(theWorld))); |
SetGWorld(theWorld, NULL); |
/* draw the icon suite */ |
EraseRect(&imageRect); |
if (iconReference != NULL) |
err = PlotIconRef(&imageRect, kAlignNone, kTransformNone, kIconServicesNormalUsageFlag, iconReference); |
else err = PlotIconSuite(&imageRect, kAlignNone, kTransformNone, iconSuite); |
/* restore the drawing environment */ |
SetGWorld(savePort, saveDevice); |
UnlockPixels(imagePixMap); |
/* after restoring, check drawing result */ |
if (err != noErr) goto bail; |
/* store what we created. */ |
*imageGWorld = theWorld; |
*maskRgn = theMask; |
return noErr; |
bail: |
if (theWorld != NULL) DisposeGWorld(theWorld); |
if (theMask != NULL) DisposeRgn(theMask); |
return err; |
} |
/* GrayOutBox grays out an area of the screen in the current grafport. |
*theBox is in local coordinates in the current grafport. This routine |
is for direct screen drawing only. */ |
void GrayOutBox(Rect *theBox) { |
long response; |
Rect globalBox; |
GDHandle maxDevice; |
RGBColor rgbWhite = {0xFFFF, 0xFFFF, 0xFFFF}, rgbBlack = {0, 0, 0}, sForground, sBackground; |
PenState penSave; |
/* save the current drawing state */ |
GetPenState(&penSave); |
/* if no color quickdraw, fail...*/ |
if (Gestalt(gestaltQuickdrawVersion, &response) != noErr) response = 0; |
if (response >= gestalt8BitQD) { |
/* get the device for the rectangle */ |
globalBox = *theBox; |
LocalToGlobal((Point*) &globalBox.top); |
LocalToGlobal((Point*) &globalBox.bottom); |
maxDevice = GetMaxDevice(&globalBox); |
if (maxDevice != NULL) { |
/* calculate the best gray */ |
if ( GetGray(maxDevice, &rgbWhite, &rgbBlack)) { |
/* draw over the area in gray using addMax transfer mode */ |
GetForeColor(&sForground); |
GetBackColor(&sBackground); |
RGBForeColor(&rgbBlack); |
RGBBackColor(&rgbWhite); |
PenMode(addMax); |
PaintRect(theBox); |
RGBForeColor(&sForground); |
RGBBackColor(&sBackground); |
/* restore the pen state and leave */ |
SetPenState(&penSave); |
return; |
} |
} |
} |
/* fall through to using the gray pattern */ |
PenPat(&qd.gray); |
PenMode(notPatBic); |
PaintRect(theBox); |
SetPenState(&penSave); |
} |
/* ShowDragHiliteBox is called to hilite the drop box area in the |
main window. Here, we draw a 3 pixel wide border around *boxBounds. */ |
OSErr ShowDragHiliteBox(DragReference theDragRef, Rect *boxBounds) { |
RgnHandle boxRegion, insetRegion; |
OSErr err; |
Rect box; |
/* set up locals */ |
boxRegion = insetRegion = NULL; |
/* create the region */ |
if ((boxRegion = NewRgn()) == NULL) { err = memFullErr; goto bail; } |
if ((insetRegion = NewRgn()) == NULL) { err = memFullErr; goto bail; } |
box = *boxBounds; |
InsetRect(&box, -5, -5); |
RectRgn(boxRegion, &box); |
InsetRect(&box, 3, 3); |
RectRgn(insetRegion, &box); |
DiffRgn(boxRegion, insetRegion, boxRegion); |
/* hilite the region */ |
err = ShowDragHilite(theDragRef, boxRegion, true); |
bail: |
/* clean up and leave */ |
if (boxRegion != NULL) DisposeRgn(boxRegion); |
if (insetRegion != NULL) DisposeRgn(insetRegion); |
return err; |
} |
/* CopyCommandParam includes the parameters used for a copy |
operation. A record of this type is created in the CopyFileCmd routine, |
and a pointer to it is passed to a Thread Manager task that does |
the actual copy. */ |
typedef struct { |
FSSpec src, dst; |
short srcRef, dstRef; |
unsigned long total, number; |
CopyCallback callback; |
CopyErrorHandler errorhandler; |
ThreadID copythread; |
Ptr copyBuffer; |
} CopyCommandParam, *CopyCmdParamPtr; |
CopyCmdParamPtr gCopyParam = NULL; |
/* CopyFileInProgress returns true if a copy command is in |
progress. In this implementation, it simply checks to see |
if gCopyParam is not NULL. */ |
Boolean CopyFileInProgress(void) { |
return (gCopyParam != NULL); |
} |
/* AbortCopyOperation aborts an ongoing copy operation. this routine |
deallocates any structures allocated and deletes the target file. It |
also tears down the thread. */ |
void AbortCopyOperation(void) { |
if (gCopyParam != NULL) { |
gCopyParam->errorhandler(&gCopyParam->src, userCanceledErr); |
gCopyParam->callback(&gCopyParam->src, kCopyEnd, 100); |
DisposeThread(gCopyParam->copythread, 0, false); |
if (gCopyParam->copyBuffer != NULL) |
DisposePtr((Ptr) gCopyParam->copyBuffer); |
if (gCopyParam->srcRef != 0) FSClose(gCopyParam->srcRef); |
if (gCopyParam->dstRef != 0) FSClose(gCopyParam->dstRef); |
FSpDelete(&gCopyParam->dst); |
DisposePtr((Ptr) gCopyParam); |
gCopyParam = NULL; |
} |
} |
/* CopyForkOperation copies forkLength bytes from the source file to the destination |
file using (buffer, bufferSize) as a data buffer during the copy. As the copy proceeds, |
the value of gBytesCopied is updated to indicate the total number of bytes copied. */ |
static OSErr CopyForkOperation(CopyCmdParamPtr param, long forkLength) { |
long bytecount, count, percent; |
OSErr err; |
bytecount = 0; |
while (bytecount < forkLength) { |
count = forkLength - bytecount; |
if (count > kCopyBufferSize) count = kCopyBufferSize; |
err = FSRead(param->srcRef, &count, param->copyBuffer); |
if (err != noErr) return err; |
YieldToAnyThread(); |
err = FSWrite(param->dstRef, &count, param->copyBuffer); |
if (err != noErr) return err; |
YieldToAnyThread(); |
bytecount += count; |
param->number += count; |
if (param->total == 0) |
percent = 100; |
else percent = rinttol((((float) param->number) / ((float) param->total)) * 100.0); |
param->callback(¶m->src, kCopyRun, percent); |
YieldToAnyThread(); |
} |
return noErr; |
} |
/* CopyCommandThread is a Thread Manager task that completes a copy operation. |
The task is started in the CopyFileCmd routine. */ |
static pascal void* CopyCommandThread(void *threadParam) { |
CopyCmdParamPtr param; |
CInfoPBRec cat; |
OSErr err; |
/* set up */ |
param = (CopyCmdParamPtr) threadParam; |
YieldToAnyThread(); |
/* calculate the total bytes */ |
cat.hFileInfo.ioNamePtr = param->src.name; |
cat.hFileInfo.ioVRefNum = param->src.vRefNum; |
cat.hFileInfo.ioDirID = param->src.parID; |
cat.hFileInfo.ioFDirIndex = 0; |
err = PBGetCatInfoSync(&cat); |
if (err != noErr) goto bail; |
if ((cat.hFileInfo.ioFlAttrib & ioDirMask) != 0) { |
err = kCannotCopyDirError; |
goto bail; |
} |
param->total = cat.hFileInfo.ioFlLgLen + cat.hFileInfo.ioFlRLgLen; |
/* file created, switch out */ |
YieldToAnyThread(); |
/* allocate the copy buffer */ |
param->copyBuffer = NewPtr(kCopyBufferSize); |
if (param->copyBuffer == NULL) { err = memFullErr; goto bail; } |
/* copy the data fork, if there is one */ |
if (cat.hFileInfo.ioFlLgLen > 0) { |
err = FSpOpenDF(¶m->src, fsRdPerm, ¶m->srcRef); |
if (err != noErr) goto bail; |
err = FSpOpenDF(¶m->dst, fsWrPerm, ¶m->dstRef); |
if (err != noErr) goto bail; |
err = CopyForkOperation(param, cat.hFileInfo.ioFlLgLen); |
if (err != noErr) goto bail; |
FSClose(param->srcRef); |
param->srcRef = 0; |
FSClose(param->dstRef); |
param->dstRef = 0; |
} |
/* copy the resource fork, if there is one */ |
if (cat.hFileInfo.ioFlRLgLen > 0) { |
err = FSpOpenRF(¶m->src, fsRdPerm, ¶m->srcRef); |
if (err != noErr) goto bail; |
err = FSpOpenRF(¶m->dst, fsWrPerm, ¶m->dstRef); |
if (err != noErr) goto bail; |
err = CopyForkOperation(param, cat.hFileInfo.ioFlRLgLen); |
if (err != noErr) goto bail; |
FSClose(param->srcRef); |
param->srcRef = 0; |
FSClose(param->dstRef); |
param->dstRef = 0; |
} |
/* flush the destination volume */ |
FlushVol(NULL, param->dst.vRefNum); /* err = best effort */ |
/* clean up */ |
param->callback(¶m->src, kCopyEnd, 100); |
DisposePtr(param->copyBuffer); |
param->copyBuffer = NULL; |
DisposePtr((Ptr) param); |
gCopyParam = NULL; |
return NULL; |
bail: |
param->errorhandler(¶m->src, err); |
param->callback(¶m->src, kCopyEnd, 100); |
if (param->srcRef != 0) FSClose(param->srcRef); |
if (param->dstRef != 0) FSClose(param->dstRef); |
if (param->copyBuffer != NULL) DisposePtr(param->copyBuffer); |
FSpDelete(¶m->dst); |
DisposePtr((Ptr) param); |
gCopyParam = NULL; |
return NULL; |
} |
OSErr CopyFileCmd(FSSpec *theSource, FSSpec *theTarget, CopyCallback callback, CopyErrorHandler errorhandler) { |
CopyCmdParamPtr param; |
OSErr err; |
Boolean threadCreated; |
/* set up locals */ |
threadCreated = true; |
param = NULL; |
/* check copy conditions */ |
if (gCopyParam != NULL) return kCopyNotRentrantError; |
/* set up param */ |
param = (CopyCmdParamPtr) NewPtr(sizeof(CopyCommandParam)); |
if (param == NULL) { err = memFullErr; goto bail; } |
param->src = *theSource; |
param->dst = *theTarget; |
param->srcRef = 0; |
param->dstRef = 0; |
param->total = param->number = 0; |
param->callback = callback; |
param->errorhandler = errorhandler; |
param->copyBuffer = NULL; |
/* create a thread */ |
err = NewThread(kCooperativeThread, CopyCommandThread, param, 0, kCreateIfNeeded, NULL, ¶m->copythread); |
if (err != noErr) goto bail; |
threadCreated = true; |
gCopyParam = param; |
callback(¶m->src, kCopyStart, 0); |
return noErr; |
bail: |
if (threadCreated) |
DisposeThread(param->copythread, 0, false); |
if (param != NULL) DisposePtr((Ptr) param); |
return err; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14