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.
PackageWindow.c
/* |
File: PackageWindow.c |
Description: |
Routines for managing the PackageWindow displayed by the |
PackageTool application. This file also contains the routines used |
for converting folders into packages and packages back into folders. |
Copyright: |
© Copyright 1999 Apple Computer, Inc. All rights reserved. |
Disclaimer: |
IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): |
Wed, Dec 22, 1999 -- created |
*/ |
#include "PackageWindow.h" |
#include "PackageTool.h" |
#include "PackageUtils.h" |
#include "Utilities.h" |
#include <Drag.h> |
#include <Controls.h> |
#include <Dialogs.h> |
#include <Appearance.h> |
#include <Icons.h> |
#include <Sound.h> |
#include <TextUtils.h> |
#include <PLStringFuncs.h> |
#include <Navigation.h> |
#include <ControlDefinitions.h> |
enum { /* picture displayed when selection is empty */ |
kSpashPictResource = 128 |
}; |
/* main window constants */ |
enum { |
kPackageDialogResource = 128, |
kPackageUserItem = 1, |
kFolderItem = 2, |
kPackageItem = 3, |
kSelectButtonItem = 4, |
/* kFolderIsPackage and kFolderIsFolder are used for |
internal tracking of the state of the kFolderItem and |
the kPackageItem dialog controls. */ |
kFolderIsPackage = 100, /* item being displayed is a package */ |
kFolderIsFolder = 101 /* item being displayed is a folder */ |
}; |
/* variables used to record properties of the main window */ |
DialogPtr gPackageWindow = NULL; /* a pointer to the main dialog */ |
PicHandle gSplashPict; /* splash image displayed in gIconBox when there is no file selected */ |
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 gPackageButton; /* control for setting gFolderTypeSelection to kFolderIsPackage */ |
ControlHandle gFolderButton; /* control for setting gFolderTypeSelection to kFolderIsFolder */ |
ControlHandle gSelectButton; /* control providing command equivalence to the select file menu command */ |
/* variables used to record information about the file being displayed */ |
Boolean gFileInDisplay = false; /* true when gFileAlias contains an alias handle */ |
AliasHandle gFileAlias = NULL; /* an alias to the last file dragged into the gIconBox */ |
IconRef gIconRef = NULL; /* an icon services reference to an icon for the file */ |
short gFolderTypeSelection = kFolderIsFolder; /* determines the type of data we will provide for drags */ |
Boolean gPWActive = false; |
/* these variables 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 */ |
/* procedure pointers used in the main window */ |
DragReceiveHandlerUPP gMainReceiveHandler = NULL; /* receive handler for the main dialog */ |
DragTrackingHandlerUPP gMainTrackingHandler = NULL; /* tracking handler for the main dialog */ |
UserItemUPP myUserItem; /* UPP for the icon's user item. This routine draws into gIconBox */ |
Boolean IsPackageWindow(WindowPtr target) { |
return (target == GetDialogWindow(gPackageWindow)); |
} |
/* ApproveDragReference is called by the drag tracking handler to determine |
if the contents of the drag can be handled by our receive handler. |
here, we only allow packages and folders. */ |
static pascal OSErr ApproveDragReference(DragReference theDragRef, Boolean *approved) { |
OSErr err; |
UInt16 itemCount; |
DragAttributes dragAttrs; |
ItemReference theItem; |
HFSFlavor targetFile; |
long theSize; |
/* 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; |
/* try to get a HFSFlavor*/ |
theSize = sizeof(HFSFlavor); |
err = GetFlavorData(theDragRef, theItem, flavorTypeHFS, &targetFile, &theSize, 0); |
if (err != noErr) goto bail; |
/* it must be a folder or a package */ |
if (IdentifyPackage(&targetFile.fileSpec, NULL)) |
*approved = true; |
else if (targetFile.fileCreator == 'MACS' && targetFile.fileType == 'fold') |
*approved = true; |
else { |
err = paramErr; |
goto bail; |
} |
return noErr; |
bail: |
/* an error occured, clean up. set result to false. */ |
*approved = false; |
return err; |
} |
/* 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(GetWindowPort(theWindow)); |
GetMouse(&mouse); |
if (PtInRect(mouse, &gIconBox)) { /* if we're in the box, hilite... */ |
gInIconBox = (ShowDragHiliteBox(theDragRef, &gIconBox) == noErr); |
} |
} |
} |
break; |
case kDragTrackingInWindow: |
if (gApprovedDrag) { |
Point mouse; |
SetPort(GetWindowPort(theWindow)); |
GetMouse(&mouse); |
if (PtInRect(mouse, &gIconBox)) { |
if ( ! gInIconBox) { /* if we're entering the box, hilite... */ |
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 |
} |
/* MyDragReceiveHandler receives drags that are dropped into the |
main window. here, we only receive packages or folders. */ |
static pascal OSErr MyDragReceiveHandler(WindowPtr theWindow, void *refcon, DragReference theDragRef) { |
ItemReference theItem; |
HFSFlavor targetFile; |
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 */ |
err = GetDragItemReferenceNumber(theDragRef, 1, &theItem); |
if (err != noErr) goto bail; |
/* try to get a HFSFlavor*/ |
theSize = sizeof(HFSFlavor); |
err = GetFlavorData(theDragRef, theItem, flavorTypeHFS, &targetFile, &theSize, 0); |
if (err != noErr) goto bail; |
/* display the located file*/ |
SetNewDisplay(&targetFile.fileSpec); |
return noErr; |
bail: |
return err; |
} |
/* MyNavFilterProc This is the filter function we provide for the Navigation services |
dialogs. */ |
static pascal Boolean MyNavFilterProc( AEDesc* theItem, void* info, NavCallBackUserData callBackUD, NavFilterModes filterMode) { |
NavFileOrFolderInfo* theInfo; |
if ( theItem->descriptorType == typeFSS ) { |
theInfo = (NavFileOrFolderInfo*) info; |
return theInfo->isFolder; |
} |
return true; |
} |
/* NavEventCallBack is the event handling call back we provide for Navigation |
services. It's presence is important so our windows will be updated appropriately |
when the navigation window is resized or moved. */ |
static pascal void NavEventCallBack( NavEventCallbackMessage callBackSelector, |
NavCBRecPtr callBackParms, NavCallBackUserData callBackUD) { |
if (callBackSelector == kNavCBEvent) { |
EventRecord *ev; |
ev = callBackParms->eventData.eventDataParms.event; |
if (ev->what == updateEvt || ev->what == activateEvt) |
HandleNextEvent(ev); |
} |
} |
/* SelectFolderOrPackage provides user interaction through calling |
navigation services that allows the user to choose a package or |
a folder for display in the main window. */ |
OSStatus SelectFolderOrPackage(void) { |
NavDialogOptions dialogOptions; |
NavReplyRecord theReply; |
NavEventUPP eventf; |
NavObjectFilterUPP filterf; |
Boolean hasreply; |
OSStatus err; |
AEDescList documents; |
Boolean watchCursorON; |
Cursor theArrow; |
/* set up locals */ |
eventf = NULL; |
filterf = NULL; |
hasreply = false; |
watchCursorON = false; |
BlockZero(&theReply, sizeof(theReply)); |
AECreateDesc(typeNull, NULL, 0, &documents); |
GetQDGlobalsArrow(&theArrow); |
/* Let's put up the watch cursor while we *wait* for the navigation |
services window to appear. */ |
SetCursor(*GetCursor(watchCursor)); |
watchCursorON = true; |
/* allocate data */ |
filterf = NewNavObjectFilterUPP(MyNavFilterProc); |
if (filterf == NULL) { err = memFullErr; goto bail; } |
eventf = NewNavEventProc(NavEventCallBack); |
if (eventf == NULL) { err = memFullErr; goto bail; } |
/* set up dialog options */ |
err = NavGetDefaultDialogOptions(&dialogOptions); |
if (err != noErr) goto bail; |
/* NOTE: Setting the kNavSupportPackages flag allows us to |
select Mac OS 9 packages. */ |
dialogOptions.dialogOptionFlags = (kNavDontAutoTranslate | kNavSupportPackages); |
/* pick one or more files */ |
err = NavChooseObject(NULL, &theReply, &dialogOptions, eventf, filterf, NULL); |
/* reset the cursor to a known state*/ |
SetCursor(&theArrow); |
watchCursorON = false; |
/* dispatch on our nav services reply */ |
if (err != noErr) goto bail; |
if (!theReply.validRecord) { err = userCanceledErr; goto bail; } |
hasreply = true; |
/* standardize the document list returned by navigation services. |
Note: |
when you select a package in Navigation Services, it it will be returned |
as if it were a folder. And, in Nav Services replies, that means the |
FSSpec referring to the folder will contain a zero length name and |
the directory ID of the folder. The following routine automatically |
coerces all such Nav-format-FSSpecs into FSMakeFSSpec format |
and stores them into a list that has the same format as the list |
passed to your 'odoc' Apple event handler. By doing this, we only |
need to have one OpenDocumentList routine in our application. */ |
err = NavReplyToODOCList(&theReply.selection, &documents); |
if (err != noErr) goto bail; |
/* if we have a valid reply, then call our |
open documents routine. */ |
err = OpenDocumentList(&theReply.selection); |
if (err != noErr) goto bail; |
/* clean up the structures we allocated */ |
NavDisposeReply(&theReply); |
DisposeNavEventUPP(eventf); |
DisposeNavObjectFilterUPP(filterf); |
return noErr; |
bail: |
AEDisposeDesc(&documents); |
if (hasreply) NavDisposeReply(&theReply); |
if (eventf != NULL) DisposeNavEventUPP(eventf); |
if (filterf != NULL) DisposeNavObjectFilterUPP(filterf); |
if (watchCursorON) SetCursor(&theArrow); |
return err; |
} |
/* 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 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(FSSpec *targetFile) { |
SInt16 theLabel; |
FSSpec mainPackageFile; |
/* remove the old file */ |
if (gFileInDisplay) { |
DisposeHandle((Handle) gFileAlias); |
gFileAlias = NULL; |
ReleaseIconRef(gIconRef); |
gIconRef = NULL; |
gFileInDisplay = false; |
InvalWindowRect(GetDialogWindow(gPackageWindow), &gIconBox); |
} |
/* if there's no new file, we're done */ |
if (targetFile == NULL) goto bail; |
/* create the new alias */ |
if (NewAliasMinimal(targetFile, &gFileAlias) != noErr) goto bail; |
/* determine the kind of object we're displaying */ |
if (IdentifyPackage(targetFile, &mainPackageFile)) { |
if (GetIconRefFromFile(&mainPackageFile, &gIconRef, &theLabel) != noErr) goto bail; |
gFolderTypeSelection = kFolderIsPackage; |
} else { |
if (GetIconRefFromFile(targetFile, &gIconRef, &theLabel) != noErr) goto bail; |
gFolderTypeSelection = kFolderIsFolder; |
} |
/* set up the controls */ |
SetControlValue(gPackageButton, (gFolderTypeSelection == kFolderIsPackage ? 1 : 0)); |
SetControlValue(gFolderButton, (gFolderTypeSelection == kFolderIsFolder ? 1 : 0)); |
if (gPWActive) { |
ActivateControl(gPackageButton); |
ActivateControl(gFolderButton); |
} else { |
DeactivateControl(gPackageButton); |
DeactivateControl(gFolderButton); |
} |
/* update the window */ |
InvalWindowRect(GetDialogWindow(gPackageWindow), &gIconBox); |
/* set the flag and return */ |
gFileInDisplay = true; |
return; |
bail: |
if (gFileAlias != NULL) DisposeHandle((Handle) gFileAlias); |
if (gIconRef != NULL) ReleaseIconRef(gIconRef); |
DeactivateControl(gPackageButton); |
DeactivateControl(gFolderButton); |
InvalWindowRect(GetDialogWindow(gPackageWindow), &gIconBox); |
gFileInDisplay = false; |
} |
/* ActivatePackageWindow handles an activate event for the |
package window. */ |
void ActivatePackageWindow(WindowPtr target, Boolean activate) { |
if (activate != gPWActive) { |
gPWActive = activate; |
if (gPWActive) { |
if (gFileInDisplay) { |
ActivateControl(gPackageButton); |
ActivateControl(gFolderButton); |
} else { |
DeactivateControl(gPackageButton); |
DeactivateControl(gFolderButton); |
} |
if (NavServicesAvailable()) { |
ActivateControl(gSelectButton); |
} else DeactivateControl(gSelectButton); |
} else { |
DeactivateControl(gPackageButton); |
DeactivateControl(gFolderButton); |
DeactivateControl(gSelectButton); |
} |
DrawDialog(gPackageWindow); |
} |
} |
/* PackageWindowUserItem 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 PackageWindowUserItem(DialogPtr theWindow, DialogItemIndex itemNo) { |
RGBColor sForground, sBackground; |
RGBColor rgbWhite = {0xFFFF, 0xFFFF, 0xFFFF}, rgbBlack = {0, 0, 0}, rgbGray = {0x7FFF, 0x7FFF, 0x7FFF}; |
FSSpec target; |
ThemeDrawState themeDrawState; |
Boolean wasChanged; |
WindowPtr wp; |
/* set up */ |
wp = GetDialogWindow(theWindow); |
SetPort(GetWindowPort(wp)); |
/* set the colors we're using for drawing */ |
GetForeColor(&sForground); |
GetBackColor(&sBackground); |
RGBForeColor(&rgbBlack); |
RGBBackColor(&rgbWhite); |
/* draw the frame */ |
themeDrawState = (gPWActive ? kThemeStateActive : kThemeStateInactive); |
DrawThemeGenericWell(&gIconBox, themeDrawState, true); |
/* verify that we still have a file */ |
if (ResolveAlias(NULL, gFileAlias, &target, &wasChanged) != noErr) { |
DisposeHandle((Handle) gFileAlias); |
gFileAlias = NULL; |
ReleaseIconRef(gIconRef); |
gIconRef = NULL; |
gFileInDisplay = false; |
} |
/* draw the file information */ |
if (gFileInDisplay) { |
OSErr err; |
short baseLine; |
FontInfo fin; |
Str255 name; |
/* begin drawing */ |
TextFont(kFontIDGeneva); /* geneva */ |
TextSize(9); |
GetFontInfo(&fin); |
/* draw the icon image */ |
err = PlotIconRef(&gIconImage, kAlignNone, kTransformNone, kIconServicesNormalUsageFlag, gIconRef); |
/* draw the file name */ |
baseLine = gIconImage.bottom + fin.ascent; |
PLstrcpy(name, target.name); |
TruncString(gIconBox.right - gIconBox.left - 4, name, truncEnd); |
MoveTo((gIconBox.left + gIconBox.right - StringWidth(name))/2, baseLine); |
DrawString(name); |
/* 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 ( ! gPWActive ) |
GrayOutBox(&gIconBox); |
/* restore previous colors */ |
RGBForeColor(&sForground); |
RGBBackColor(&sBackground); |
} |
/* VerifyPackage is called before we attempt to convert a folder into |
a package. This routine verifies that the contents of the folder are |
formatted correctly and the environmental conditions are right |
so that if the folder is turned into a package, the package will |
be formatted correctly. *packageFolder is a file specification record |
pointing to the folder we would like to turn into a package. *mustRebuildChain |
will be set to true by this routine if (a) the package's main file is in |
the package's root directory or (b) there is a chain of alias files |
and one of those files along the way is outside of the package's folder |
or is in the same directory as the previous one. Normally, folder |
packages aren't created, but or those intersted in experimenting |
they can be created by holding down the option key. if allowFolderPackages |
is true, then will not abort if the main object in a package happens |
to be a folder. */ |
static Boolean VerifyPackage(FSSpec *packageFolder, Boolean *mustRebuildChain, Boolean allowFolderPackages) { |
CInfoPBRec cat; |
OSErr err; |
long packageFolderDirID; |
Str255 name, errstr; |
FSSpec topAliasFile, nextAliasFile, prevAliasFile, mainTargetFile; |
Boolean targetIsFolder, wasAliased; |
long aliasCount; |
/* set up locals */ |
aliasCount = 0; |
*mustRebuildChain = false; |
/* make sure file sharing is turned off */ |
if ( FileSharingAppIsRunning() ) { |
GetIndString(errstr, kMainStringList, kFileSharingOn); |
ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr); |
return false; |
} |
/* verify the package folder is a folder */ |
BlockZero(&cat, sizeof(cat)); |
cat.dirInfo.ioNamePtr = packageFolder->name; |
cat.dirInfo.ioVRefNum = packageFolder->vRefNum; |
cat.dirInfo.ioFDirIndex = 0; |
cat.dirInfo.ioDrDirID = packageFolder->parID; |
err = PBGetCatInfoSync(&cat); |
if (err != noErr) return false; |
if ((cat.dirInfo.ioFlAttrib & 16) == 0) { |
GetIndString(errstr, kMainStringList, kNotAFolder); |
ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr); |
return false; |
} |
packageFolderDirID = cat.dirInfo.ioDrDirID; |
/* if the bundle flag is set and we arrive here, |
then we didn't recognize the package in the first |
place. That's an internal error. */ |
if ((cat.dirInfo.ioDrUsrWds.frFlags & kHasBundle) != 0) { |
GetIndString(errstr, kMainStringList, kBundleAlreadySet); |
ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr); |
return false; |
} |
/* search for a top level alias file */ |
BlockZero(&cat, sizeof(cat)); |
cat.dirInfo.ioNamePtr = name; |
cat.dirInfo.ioVRefNum = packageFolder->vRefNum; |
cat.dirInfo.ioFDirIndex = 1; |
cat.dirInfo.ioDrDirID = packageFolderDirID; |
while (PBGetCatInfoSync(&cat) == noErr) { |
if (((cat.dirInfo.ioFlAttrib & 16) == 0) && ((cat.dirInfo.ioDrUsrWds.frFlags & kIsAlias) != 0)) { |
/* increment the alias counter. There can be only one. */ |
aliasCount += 1; |
if (aliasCount > 1) { |
GetIndString(errstr, kMainStringList, kMoreThanOneAlias); |
ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr); |
return false; |
} |
/* get a reference to the alias file */ |
err = FSMakeFSSpec(packageFolder->vRefNum, packageFolderDirID, name, &topAliasFile); |
if (err != noErr) return false; |
/* chain through all alias files one at a time making sure that each |
file is in the package heirarchy. Set the *mustRebuildChain flag if |
any two files are in the same directory. We must do this because |
relative alias files do not work when any two files in the chain |
are in the same directory. */ |
nextAliasFile = topAliasFile; |
while (true) { |
/* move on to the next file */ |
prevAliasFile = nextAliasFile; |
err = ResolveAliasFile(&nextAliasFile, false, &targetIsFolder, &wasAliased); |
if (err != noErr) { |
GetIndString(errstr, kMainStringList, kBrokenAlias); |
ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr); |
return false; |
} |
/* exit this loop once we've found the main target file */ |
if ( ! wasAliased ) { |
mainTargetFile = nextAliasFile; /* we found the main target file */ |
break; |
} |
/* if any one of the files is in the same directory as the previous one, |
then the alias chain needs to be rebuilt.... */ |
if ( prevAliasFile.parID == nextAliasFile.parID ) { |
*mustRebuildChain |= true; |
} |
/* ......and, if it's an alias file somewhere between the first alias and |
the main target file AND it is outside of the package directory, |
then the alias chain needs to be rebuilt. */ |
if ( ! FSSpecIsInDirectory(&nextAliasFile, packageFolder->vRefNum, packageFolderDirID) ) { |
*mustRebuildChain |= true; |
} |
} |
/* ....but we only need an alias chain if the package's main file |
is in the package's root directory. i.e., it's in the same directory |
as the top level alias file. */ |
if ( mainTargetFile.parID != topAliasFile.parID ) { |
*mustRebuildChain = false; |
} |
/* make sure the main target file is inside of the package directory */ |
if ( ! FSSpecIsInDirectory(&mainTargetFile, packageFolder->vRefNum, packageFolderDirID) ) { |
GetIndString(errstr, kMainStringList, kMainOutsideOfPackage); |
ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr); |
return false; |
} |
/* special permission (the option key) is required to make folder packages. */ |
if (targetIsFolder) { |
/* If we're allowing folder packages, make sure it's |
not a package folder. */ |
if (allowFolderPackages) { |
CInfoPBRec targetCat; |
BlockZero(&targetCat, sizeof(targetCat)); |
targetCat.dirInfo.ioNamePtr = mainTargetFile.name; |
targetCat.dirInfo.ioVRefNum = mainTargetFile.vRefNum; |
targetCat.dirInfo.ioFDirIndex = 0; |
targetCat.dirInfo.ioDrDirID = mainTargetFile.parID; |
err = PBGetCatInfoSync(&targetCat); |
if (err != noErr) return false; |
if ( ((targetCat.dirInfo.ioDrUsrWds.frFlags & kHasBundle) != 0) ) { |
GetIndString(errstr, kMainStringList, kAliasRefersToPackage); |
ParamAlert(kPackageDidNotVerify, mainTargetFile.name, errstr); |
return false; |
} |
} else { |
GetIndString(errstr, kMainStringList, kAliasRefersToFolder); |
ParamAlert(kPackageDidNotVerify, mainTargetFile.name, errstr); |
return false; |
} |
} |
} /* if alias */ |
cat.dirInfo.ioFDirIndex++; |
cat.dirInfo.ioDrDirID = packageFolderDirID; |
} /* of directory loop */ |
/* make sure we found at least one alias */ |
if (aliasCount == 0) { |
GetIndString(errstr, kMainStringList, kNoAliasPresent); |
ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr); |
return false; |
} |
/* all tests successful */ |
return true; |
} |
/* SetPackageBit sets the package bit for a folder to |
the value specified by newValue */ |
static OSStatus SetPackageBit(FSSpec *packageFolder, Boolean newValue) { |
CInfoPBRec cat; |
OSStatus err; |
/* set the package flag */ |
BlockZero(&cat, sizeof(cat)); |
cat.dirInfo.ioNamePtr = packageFolder->name; |
cat.dirInfo.ioVRefNum = packageFolder->vRefNum; |
cat.dirInfo.ioFDirIndex = 0; |
cat.dirInfo.ioDrDirID = packageFolder->parID; |
err = PBGetCatInfoSync(&cat); |
if (err != noErr) return err; |
cat.dirInfo.ioDrDirID = packageFolder->parID; |
if (newValue) |
cat.dirInfo.ioDrUsrWds.frFlags |= kHasBundle; |
else cat.dirInfo.ioDrUsrWds.frFlags &= (~ kHasBundle); |
err = PBSetCatInfoSync(&cat); |
if (err != noErr) return err; |
return noErr; |
} |
/* FolderToPackage converts a folder into a Mac OS 9 package. This routine |
assumes the folder is set up correctly (i.e. VerifyPackage was called for |
the folder and it returned true. if constructChain is true, then a chain |
of relative alias files will be created. This should be the value returned |
by VerifyPackage in the *mustRebuildChain parameter. A chain of alias |
files must be created when the package's main file is stored in the |
package's root folder. This is because there is a long standing Alias Manager |
but that prevents relative alias files from being resolved when the |
alias file and the target file are in the same folder. */ |
static OSStatus FolderToPackage(FSSpec *packageFolder, Boolean constructChain) { |
CInfoPBRec cat; |
OSErr err; |
FSSpec topAliasFile, mainTargetFile, chainAliasFile, chainAliasFolder; |
long packageFolderDirID, chainAliasFolderDirID; |
Boolean foundAlias, targetIsFolder, wasAliased; |
Str255 name, folderName, aliasName, errstr; |
/* set up locals */ |
foundAlias = false; |
/* find out the folder's directory ID */ |
BlockZero(&cat, sizeof(cat)); |
cat.dirInfo.ioNamePtr = packageFolder->name; |
cat.dirInfo.ioVRefNum = packageFolder->vRefNum; |
cat.dirInfo.ioFDirIndex = 0; |
cat.dirInfo.ioDrDirID = packageFolder->parID; |
err = PBGetCatInfoSync(&cat); |
if (err != noErr) return err; |
packageFolderDirID = cat.dirInfo.ioDrDirID; |
/* search for a top level alias file */ |
BlockZero(&cat, sizeof(cat)); |
cat.dirInfo.ioNamePtr = name; |
cat.dirInfo.ioVRefNum = packageFolder->vRefNum; |
cat.dirInfo.ioFDirIndex = 1; |
cat.dirInfo.ioDrDirID = packageFolderDirID; |
while (PBGetCatInfoSync(&cat) == noErr) { |
if (((cat.dirInfo.ioFlAttrib & 16) == 0) && ((cat.dirInfo.ioDrUsrWds.frFlags & kIsAlias) != 0)) { |
foundAlias = true; |
break; |
} |
cat.dirInfo.ioFDirIndex++; |
cat.dirInfo.ioDrDirID = packageFolderDirID; |
} |
if ( ! foundAlias) return paramErr; |
/* resolve the alias file to the main target file */ |
err = FSMakeFSSpec(packageFolder->vRefNum, packageFolderDirID, name, &topAliasFile); |
if (err != noErr) return err; |
mainTargetFile = topAliasFile; |
err = ResolveAliasFile(&mainTargetFile, true, &targetIsFolder, &wasAliased); |
if (err != noErr) return err; |
/* create a sub directory if needed */ |
if (constructChain) { |
/* set up the folder FSSpec */ |
GetIndString(folderName, kMainStringList, kAliasRedirectFolderName); |
err = FSMakeFSSpec(packageFolder->vRefNum, packageFolderDirID, folderName, &chainAliasFolder); |
if (err == fnfErr) err = noErr; |
if (err != noErr) return err; |
/* find or create a sub directory for the indirect alias. */ |
err = FSpGetDirID(&chainAliasFolder, &chainAliasFolderDirID); |
if (err != noErr) |
err = FSpDirCreate(&chainAliasFolder, smSystemScript, &chainAliasFolderDirID); |
if (err != noErr) { |
GetIndString(errstr, kMainStringList, kErrorRedirectingAlias); |
ParamAlert(kRerouteFailedAlert, errstr, NULL); |
return err; |
} |
/* create a reference to the indirect alias file */ |
GetIndString(aliasName, kMainStringList, kRedirectAliasName); |
err = FSMakeFSSpec(packageFolder->vRefNum, chainAliasFolderDirID, aliasName, &chainAliasFile); |
if (err == fnfErr) err = noErr; |
if (err != noErr) return err; |
/* create an indirect alias file referencing the main target file */ |
err = UpdateRelativeAliasFile(&chainAliasFile, &mainTargetFile, true); |
if (err != noErr) return err; |
/* create an alias file referring to the indirect file. */ |
err = UpdateRelativeAliasFile(&topAliasFile, &chainAliasFile, false); |
if (err != noErr) return err; |
} else { |
/* update the alias file to a relative path */ |
err = UpdateRelativeAliasFile(&topAliasFile, &mainTargetFile, false); |
if (err != noErr) return err; |
} |
/* set the package flag */ |
cat.dirInfo.ioNamePtr = packageFolder->name; |
cat.dirInfo.ioVRefNum = packageFolder->vRefNum; |
cat.dirInfo.ioFDirIndex = 0; |
cat.dirInfo.ioDrDirID = packageFolder->parID; |
err = PBGetCatInfoSync(&cat); |
if (err != noErr) return err; |
cat.dirInfo.ioDrDirID = packageFolder->parID; |
cat.dirInfo.ioDrUsrWds.frFlags |= kHasBundle; |
err = PBSetCatInfoSync(&cat); |
if (err != noErr) return err; |
return noErr; |
} |
/* PackageToFolder converts a Mac OS 9 package into a regular |
folder. It does this by clearing the package bit in the folder's |
Finder flags. */ |
static OSStatus PackageToFolder(FSSpec *target) { |
CInfoPBRec cat; |
OSErr err; |
cat.dirInfo.ioNamePtr = target->name; |
cat.dirInfo.ioVRefNum = target->vRefNum; |
cat.dirInfo.ioFDirIndex = 0; |
cat.dirInfo.ioDrDirID = target->parID; |
err = PBGetCatInfoSync(&cat); |
if (err != noErr) return err; |
cat.dirInfo.ioDrDirID = target->parID; |
cat.dirInfo.ioDrUsrWds.frFlags &= ~ kHasBundle; |
err = PBSetCatInfoSync(&cat); |
return err; |
} |
/* HitPackageWindow 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 */ |
void HitPackageWindow(DialogPtr theDialog, EventRecord *event, short itemNo) { |
switch (itemNo) { |
case kPackageUserItem: |
break; |
case kFolderItem: /* turn it into a folder */ |
if (gFolderTypeSelection != kFolderIsFolder) { |
FSSpec target; |
Boolean wasChanged; |
if (ResolveAlias(NULL, gFileAlias, &target, &wasChanged) != noErr) { |
SetNewDisplay(NULL); |
} else { |
if ((event->modifiers & (optionKey|shiftKey)) == optionKey|shiftKey) { |
SetPackageBit(&target, false); |
gFolderTypeSelection = kFolderIsFolder; |
SetControlValue(gPackageButton, (gFolderTypeSelection == kFolderIsPackage ? 1 : 0)); |
SetControlValue(gFolderButton, (gFolderTypeSelection == kFolderIsFolder ? 1 : 0)); |
ShowChangesInFinderWindow(target.vRefNum, target.parID); |
} else if (PackageToFolder(&target) == noErr) { |
SetNewDisplay(&target); /* update the view */ |
ShowChangesInFinderWindow(target.vRefNum, target.parID); |
} |
} |
} |
break; |
case kPackageItem: /* turn it into a package */ |
if (gFolderTypeSelection != kFolderIsPackage) { |
FSSpec target; |
Boolean wasChanged; |
if (ResolveAlias(NULL, gFileAlias, &target, &wasChanged) != noErr) { |
SetNewDisplay(NULL); |
} else { |
Boolean mustRebuildChain; |
if ((event->modifiers & (optionKey|shiftKey)) == (optionKey|shiftKey)) { |
SetPackageBit(&target, true); |
gFolderTypeSelection = kFolderIsPackage; |
SetControlValue(gPackageButton, (gFolderTypeSelection == kFolderIsPackage ? 1 : 0)); |
SetControlValue(gFolderButton, (gFolderTypeSelection == kFolderIsFolder ? 1 : 0)); |
ShowChangesInFinderWindow(target.vRefNum, target.parID); |
} else if ( VerifyPackage(&target, &mustRebuildChain, ((event->modifiers & optionKey) != 0)) ) { |
OSStatus err; |
err = FolderToPackage(&target, mustRebuildChain); |
if (err != noErr) { |
Str255 errStr; |
NumToString(err, errStr); |
ParamAlert(kFailedToCreatePackage, errStr, target.name); |
} else { |
SetNewDisplay(&target); /* update the view */ |
ShowChangesInFinderWindow(target.vRefNum, target.parID); |
} |
} |
} |
} |
break; |
case kSelectButtonItem: /* call the select routine defined in PackageTool.c */ |
SelectFolderOrPackage(); |
break; |
} |
} |
/* CreatePackageWindow creates the package window. If it cannot be |
created, then an error is returned. */ |
OSStatus CreatePackageWindow(void) { |
OSErr err; |
Boolean installedTracker, installedReceiver; |
short itemt; |
Rect itemb; |
Handle itemh; |
DialogPtr dialog; |
long itemSize; |
Point where; |
/* set up locals for recovery */ |
dialog = NULL; |
installedTracker = installedReceiver = false; |
/* get other resources */ |
gSplashPict = GetPicture(kSpashPictResource); |
if (gSplashPict == NULL) { err = resNotFound; goto bail; } |
/* create the dialog */ |
dialog = GetNewDialog(kPackageDialogResource, NULL, (WindowPtr) (-1)); |
if (dialog == NULL) { err = memFullErr; goto bail; } |
/* grab and set up our dialog items */ |
GetDialogItem(dialog, kPackageUserItem, &itemt, (Handle*) &itemh, &gIconBox); |
myUserItem = NewUserItemUPP(PackageWindowUserItem); |
if (myUserItem == NULL) { err = memFullErr; goto bail; } |
SetDialogItem(dialog, kPackageUserItem, userItem, (Handle) myUserItem, &gIconBox); |
GetDialogItem(dialog, kFolderItem, &itemt, (Handle*) &gFolderButton, &itemb); |
GetDialogItem(dialog, kPackageItem, &itemt, (Handle*) &gPackageButton, &itemb); |
GetDialogItem(dialog, kSelectButtonItem, &itemt, (Handle*) &gSelectButton, &itemb); |
/* set initial control values */ |
SetControlValue(gPackageButton, (gFolderTypeSelection == kFolderIsPackage ? 1 : 0)); |
SetControlValue(gFolderButton, (gFolderTypeSelection == kFolderIsFolder ? 1 : 0)); |
DeactivateControl(gPackageButton); |
DeactivateControl(gFolderButton); |
/* calculate the drawn icon's boundary */ |
SetRect(&gIconImage, 0, 0, 32, 32); |
OffsetRect(&gIconImage, (gIconBox.right + gIconBox.left - 32) / 2, gIconBox.top + 32); |
/* install the drag handlers */ |
gMainTrackingHandler = NewDragTrackingHandlerProc(MyDragTrackingHandler); |
if (gMainTrackingHandler == NULL) { err = memFullErr; goto bail; } |
err = InstallTrackingHandler(gMainTrackingHandler, GetDialogWindow(dialog), NULL); |
if (err != noErr) { err = memFullErr; goto bail; } |
installedTracker = true; |
gMainReceiveHandler = NewDragReceiveHandlerProc(MyDragReceiveHandler); |
if (gMainReceiveHandler == NULL) { err = memFullErr; goto bail; } |
err = InstallReceiveHandler(gMainReceiveHandler, GetDialogWindow(dialog), NULL); |
if (err != noErr) { err = memFullErr; goto bail; } |
installedReceiver = true; |
/* position the window, if necessary */ |
if (GetCollectionItem(GetCollectedPreferences(), 'QDPt', 1, (itemSize = sizeof(where), &itemSize), &where) == noErr) { |
MoveWindow(GetDialogWindow(dialog), where.h, where.v, true); |
} |
/* done, window complete */ |
ShowWindow(GetDialogWindow(dialog)); |
gPackageWindow = dialog; |
return noErr; |
bail: |
if (installedReceiver) |
RemoveReceiveHandler(gMainReceiveHandler, GetDialogWindow(dialog)); |
if (installedTracker) |
RemoveTrackingHandler(gMainTrackingHandler, GetDialogWindow(dialog)); |
if (dialog != NULL) DisposeDialog(dialog); |
return err; |
} |
/* ClosePackageWindow closes the package window and disposes of |
any structures allocated when it was opened. */ |
void ClosePackageWindow(void) { |
Point where; |
WindowPtr wp; |
CGrafPtr gp; |
Rect r; |
wp = GetDialogWindow(gPackageWindow); |
gp = GetWindowPort(wp); |
SetPort(gp); |
GetPortBounds(gp, &r); |
where = * (Point*) &r; |
LocalToGlobal(&where); |
AddCollectionItem(GetCollectedPreferences(), 'QDPt', 1, sizeof(where), &where); |
SetNewDisplay(NULL); |
DisposeDialog(gPackageWindow); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30