eudora-mac/navUtils.c

1 line
40 KiB
C
Executable File

/* Copyright (c) 2017, Computer History Museum
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
the limitations in the disclaimer below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE. */
#include "navUtils.h"
#define FILE_NUM 118
#pragma segment navUtils
extern Boolean DoUpdate(WindowPtr topWin,EventRecord *event);
extern OSErr InsertGraphicFile(MyWindowPtr win,FSSpecPtr spec);
extern OSErr InsertTextFile(MyWindowPtr win, FSSpecPtr spec, Boolean rich, Boolean delSP);
typedef struct {
ZoneAndResFileRec zoneAndResFile;
// DialogPtr theDialog;
// short defaultItem;
Boolean customControlsCreated; // Safety for Nav 1.0
Boolean insertDefault; // Insert button is the default
// Boolean insertHit; // The insert button was hit
// Boolean defaultButtonIsInsert;
} attachInsertRec, *attachInsertRecPtr, **attachInsertRecHandle;
pascal void attachNavProc (NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, attachInsertRecPtr attachInsertPtr);
pascal void updateNavProc (NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, ZoneAndResFilePtr zoneAndResFile);
// OSErr HandleNavEvent (DialogPtr navDialog, EventRecord *event, NavDialogRef context, attachInsertRecPtr attachInsertPtr);
void NegotiateCustomRectSize (Rect *customRect, short minWidth, short minHeight);
OSErr SelectNavEntry (DialogPtr navDialog, NavDialogRef context, AEDescList *fileList, attachInsertRecPtr attachInsertPtr);
OSErr AddDialogItemsToNav (short ditlResID, NavDialogRef context);
short GetCustomNavItem (NavDialogRef context, short customNavItem);
pascal Boolean InsertNavFilterProc ( AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode );
void SeeIfWeShouldPreloadNav (void)
{
OSErr theError;
if (UseNavServices () && PrefIsSet (PREF_PRELOAD_NAV))
if (theError = NavLoad ())
DieWithError (NAV_COULD_NOT_LOAD_ERR, theError);
}
Boolean UseNavServices (void)
{
UInt32 version;
Boolean useNav;
if (HaveOSX ())
return (true);
if (useNav = NavServicesAvailable ()) {
version = NavLibraryVersion ();
useNav = (version >= 0x01100000);
}
return (useNav && version);
}
void GetZoneAndResFile (ZoneAndResFilePtr zoneAndResFile)
{
zoneAndResFile->resFile = CurResFile ();
}
void SetZoneAndResFile (ZoneAndResFilePtr zoneAndResFile)
{
UseResFile (zoneAndResFile->resFile);
}
pascal Boolean InsertNavFilterProc ( AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode ) {
OSErr err = noErr;
NavFileOrFolderInfo *theInfo;
FSSpec spec;
Boolean wasAlias;
FInfo fInfo;
// if it's not a file/folder, we don't want to know about it
if ( theItem->descriptorType != 'fss ' )
return false;
theInfo = (NavFileOrFolderInfo *) info;
if ( theInfo->isFolder )
return true;
fInfo.fdType = 0;
err = AEGetDescData ( theItem, &spec, sizeof ( spec ));
if ( noErr == err )
err = ResolveAliasNoMount ( &spec, &spec, &wasAlias );
if ( noErr == err )
err = AFSpGetFInfo ( &spec, &spec, &fInfo );
if ( err != noErr )
return false;
// We can always insert text
if ( fInfo.fdType == 'TEXT' || fInfo.fdType == 'ttro' )
return true;
if ( HasFeature (featureStyleGraphics) && !PrefIsSet ( PREF_SEND_ENRICHED_NEW ) && IsGraphicFile ( &spec ))
return true;
return false; // don't show the file
}
void CompAttachNav (MyWindowPtr win, Boolean insertDefault)
{
attachInsertRec attachInsert;
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
NavEventUPP eventUPP;
NavObjectFilterUPP filterUPP;
FSSpec spec;
OSErr theError,
disposeError;
long count,
i;
Zero (theReply);
Zero (attachInsert);
attachInsert.insertDefault = insertDefault;
eventUPP = NewNavEventUPP (attachNavProc);
filterUPP = NewNavObjectFilterUPP ( InsertNavFilterProc );
theError = NavGetDefaultDialogOptions (&dialogOptions);
if (theError)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
if (HaveOSX ())
dialogOptions.dialogOptionFlags |= kNavSupportPackages;
GetRString (dialogOptions.windowTitle, insertDefault ? INSERT_DOCUMENT_NAV_TITLE : ATTACH_DOCUMENT_NAV_TITLE);
GetRString (dialogOptions.actionButtonLabel, insertDefault ? INSERT_DOCUMENT_ACTION_BUTTON : ATTACH_DOCUMENT_ACTION_BUTTON);
GetRString (dialogOptions.message, insertDefault ? INSERT_DOCUMENT_INSTRUCTIONS : ATTACH_DOCUMENT_INSTRUCTIONS);
}
if (!theError) {
GetZoneAndResFile (&attachInsert.zoneAndResFile);
PushCursor(arrowCursor); // Have to do this to clear the watch cursor for OS X
theError = NavChooseFile (nil, &theReply, &dialogOptions, eventUPP, nil, insertDefault ? filterUPP : nil, nil, &attachInsert);
PopCursor();
SetZoneAndResFile (&attachInsert.zoneAndResFile);
if (theError)
if (theError == fnfErr)
WarnUser (ALIAS_TO_NOWHERE, theError);
else if (theError != userCanceledErr)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError && theReply.validRecord) {
theError = AECountItems (&theReply.selection, &count);
for (i = 1; !theError && i <= count; ++i) {
theError = GetSpecFromNthDesc (&theReply.selection, i, &spec);
if (!theError) {
// if (attachInsert.insertHit || attachInsert.defaultButtonIsInsert)
if ( insertDefault )
{
if (IsGraphicFile (&spec))
InsertGraphicFile (win, &spec);
else
InsertTextFile (win, &spec, false, false);
}
else
CompAttachSpec (win, &spec);
}
}
}
disposeError = NavDisposeReply (&theReply);
if (!theError)
theError = disposeError;
}
if (filterUPP)
DisposeNavObjectFilterUPP (filterUPP);
if (eventUPP)
DisposeNavEventUPP (eventUPP);
}
//
// Very annoying note...
//
// Selectors are passed in the following order:
// 1. kNavCBCustomize
// 2. kNavCBCustomize
// 3. kNavCBCustomize
// 4. kNavCBSelectEntry
// 5. kNavCBSelectEntry
// 6. kNavCBStart
//
// This is not helpful because our custom control is not created until kNavCBStart. Therefore, we
// can't activate our custom controls when we get the initial kNavCBSelectEntry selectors. (grr... grr...)
//
// On top of this, the kNavCBSelectEntry passes a nil dialog pointer!! Yikes! C'mon Nav Service guys...
//
pascal void attachNavProc (NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, attachInsertRecPtr attachInsertPtr)
{
AEDescList fileList;
ZoneAndResFileRec navZoneAndResFile;
DialogPtr theDialog;
OSErr theError;
theError = noErr;
GetZoneAndResFile (&navZoneAndResFile);
SetZoneAndResFile (&attachInsertPtr->zoneAndResFile);
theDialog = callBackParms->window ? GetDialogFromWindow (callBackParms->window) : nil;
switch (callBackSelector) {
case kNavCBEvent:
// theError = HandleNavEvent (theDialog, callBackParms->eventData.eventDataParms.event, callBackParms->context, attachInsertPtr);
break;
case kNavCBCustomize :
// NegotiateCustomRectSize (&callBackParms->customRect, 400, 40);
break;
case kNavCBStart :
// attachInsertPtr->defaultItem = GetDialogDefaultItem (theDialog);
// theError = AddDialogItemsToNav (ATTACH_NAV_DITL, callBackParms->context);
// Hack!! - kNavCBSelectEntry selector comes in before we start and Nav 1.0 is really unfriendly about this
// if (!theError) {
attachInsertPtr->customControlsCreated = true;
theError = NavCustomControl (callBackParms->context, kNavCtlGetSelection, &fileList);
if (!theError)
theError = SelectNavEntry (theDialog, callBackParms->context, &fileList, attachInsertPtr);
// }
break;
case kNavCBSelectEntry:
// !@#$%^ Nav Services... The window field of the NavCBRecPtr is nil!!!
// theError = SelectNavEntry (callBackParms->window, callBackParms->context, (AEDescList *) callBackParms->eventData.eventDataParms.param, attachInsertPtr);
if (attachInsertPtr->customControlsCreated)
theError = SelectNavEntry (GetDialogFromWindow(FrontWindow()), callBackParms->context, (AEDescList *) callBackParms->eventData.eventDataParms.param, attachInsertPtr);
break;
}
SetZoneAndResFile (&navZoneAndResFile);
}
pascal void updateNavProc (NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, ZoneAndResFilePtr zoneAndResFile)
{
ZoneAndResFileRec navZoneAndResFile;
WindowPtr win;
GetZoneAndResFile (&navZoneAndResFile);
SetZoneAndResFile (zoneAndResFile);
switch (callBackSelector) {
case kNavCBEvent:
switch (callBackParms->eventData.eventDataParms.event->what) {
case updateEvt:
win = (WindowPtr)callBackParms->eventData.eventDataParms.event->message;
if (IsMyWindow(win) && (GetWindowKind(win)==dialogKind))
// Nav Services attempts to udpate dialogs itself by redrawing all the
// controls which zaps the udpate rgn. Not good enough. We need to
// do some drawing ourself. This is a problem mainly with the
// Settings dialog.
InvalContent(GetWindowMyWindowPtr(win));
DoUpdate (win, (EventRecord *) callBackParms->eventData.eventDataParms.event);
break;
case nullEvent:
//#ifdef THREADING_ON
// if (NEED_YIELD)
// MyYieldToAnyThread();
//#endif
break;
}
break;
}
SetZoneAndResFile (&navZoneAndResFile);
}
#if 0
OSErr HandleNavEvent (DialogPtr navDialog, EventRecord *event, NavDialogRef context, attachInsertRecPtr attachInsertPtr)
{
DialogItemIndexZeroBased foundItem,
firstItem;
GrafPtr savedPort;
WindowPtr win;
ControlHandle theControl;
Point mouseLoc;
OSErr theError;
short partCode;
theError = noErr;
switch (event->what) {
case mouseDown:
partCode = FindWindow (event->where, &win);
if (partCode == inContent && navDialog == GetDialogFromWindow (win)) {
GetPort (&savedPort);
SetPort (GetDialogPort(navDialog)); // Hack for Nav Services 1.0
GetMouse (&mouseLoc);
SetPort (savedPort);
foundItem = FindDialogItem (navDialog, mouseLoc);
if (foundItem >= 0)
if ((theControl = GetDItemCtl (navDialog, foundItem + 1)) && !GetControlHilite(theControl))
if (!NavCustomControl (context, kNavCtlGetFirstControlID, &firstItem))
if (foundItem >= firstItem) {
attachInsertPtr->insertHit = true;
NavCustomControl (context, kNavCtlAccept, nil);
}
}
break;
case updateEvt:
DoUpdate ((WindowPtr) event->message, event);
break;
}
return (theError);
}
#endif
void NegotiateCustomRectSize (Rect *customRect, short minWidth, short minHeight)
{
if (!customRect->bottom)
customRect->bottom = customRect->top + minHeight;
if (!customRect->right)
customRect->right = customRect->left + minWidth;
}
OSErr SelectNavEntry (DialogPtr navDialog, NavDialogRef context, AEDescList *fileList, attachInsertRecPtr attachInsertPtr)
{
// ControlHandle theControl;
AEKeyword keyword;
DescType typeCode;
FInfo info;
FSSpec spec;
OSErr theError;
long actualSize,
count,
i;
Boolean dimInsertButton,
wasAlias;
dimInsertButton = false;
theError = AECountItems (fileList, &count);
for (i = 1; !theError && !dimInsertButton && i <= count; ++i) {
theError = AEGetNthPtr (fileList, i, typeFSS, &keyword, &typeCode,(Ptr) &spec, sizeof(FSSpec), &actualSize);
if (!theError)
theError = ResolveAliasNoMount (&spec, &spec, &wasAlias);
if (!theError)
theError = AFSpGetFInfo (&spec, &spec, &info);
if (!theError)
if (!(info.fdType == 'TEXT' || info.fdType == 'ttro' || (HasFeature (featureStyleGraphics) && !PrefIsSet(PREF_SEND_ENRICHED_NEW) && IsGraphicFile (&spec))))
dimInsertButton = true;
}
if (theError)
dimInsertButton = true;
// if (theControl = GetDItemCtl (navDialog, GetCustomNavItem (context, 1))) {
// attachInsertPtr->defaultButtonIsInsert = attachInsertPtr->insertDefault && !dimInsertButton;
// HiliteControl (theControl, dimInsertButton ? 255 : 0);
// SetDialogDefaultItem (navDialog, attachInsertPtr->defaultButtonIsInsert ? GetCustomNavItem (context, 1) : attachInsertPtr->defaultItem);
// }
return (theError);
}
OSErr AddDialogItemsToNav (short ditlResID, NavDialogRef context)
{
Handle ditl;
OSErr theError;
theError = noErr;
ditl = GetResource ('DITL', ditlResID);
if (!ditl) {
if (!(theError = ResError ()))
theError = resNotFound;
}
else {
theError = NavCustomControl (context, kNavCtlAddControlList, ditl);
ReleaseResource (ditl);
}
return (theError);
}
short GetCustomNavItem (NavDialogRef context, short customNavItem)
{
DialogItemIndexZeroBased item;
item = 0;
if (!NavCustomControl (context, kNavCtlGetFirstControlID, &item))
item += customNavItem;
return (item);
}
void DoSFOpenNav (short modifiers)
{
static OSType types[] = {kContainerFolderAliasType,'TEXT','ttro',kFakeAppType,STATIONERY_TYPE,'ttro','PREF','eAp1','eAp2','eAp3','eAp4','eAp5','eAp6','eAp7','eAp8','eAp9','eApA','eApB','eApC','eApD','eApE','eApF',SEARCH_FILE_TYPE,REG_FILE_TYPE,0};
ZoneAndResFileRec zoneAndResFile;
ProcessSerialNumber psn;
NavTypeListHandle typeList;
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
NavEventUPP eventUPP;
AEDescList fileList;
FSSpec spec;
FInfo info;
OSErr theError,
disposeError;
short numTypes;
Zero (theReply);
theError = noErr;
NullADList (&fileList, nil);
numTypes = CountOSTypes (types, 0);
typeList = NewNavTypeList (CREATOR, numTypes, types);
eventUPP = NewNavEventUPP (updateNavProc);
theError = NavGetDefaultDialogOptions (&dialogOptions);
if (theError)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
// We get one hell of a nasty popup... let's not, for now.
dialogOptions.dialogOptionFlags |= kNavNoTypePopup;
dialogOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles;
dialogOptions.dialogOptionFlags |= kNavSelectAllReadableItem;
BlockMoveData (LMGetCurApName(), dialogOptions.clientName, LMGetCurApName()[0] + 1);
}
if (!theError) {
GetZoneAndResFile (&zoneAndResFile);
PushCursor(arrowCursor); // Have to do this to clear the watch cursor for OS X
theError = NavGetFile (nil, &theReply, &dialogOptions, eventUPP, nil, nil, typeList, &zoneAndResFile);
PopCursor();
SetZoneAndResFile (&zoneAndResFile);
if (theError && theError != userCanceledErr)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError && theReply.validRecord) {
theError = GetSpecFromNthDesc (&theReply.selection, 1, &spec);
if (!theError)
theError = AFSpGetFInfo (&spec, &spec, &info);
if (!theError)
if (info.fdType == SETTINGS_TYPE)
OpenNewSettings (&spec, false, GetNagState());
else
SimpleAESend (CurrentPSN (&psn),
kCoreEventClass,kAEOpenDocuments,nil,kEAEWhenever,
keyDirectObject,keyEuDesc,SimpleAEList (&fileList, typeFSS, &spec, sizeof (spec), nil),
nil,nil);
}
disposeError = NavDisposeReply (&theReply);
if (!theError)
theError = disposeError;
}
if (eventUPP)
DisposeNavEventUPP (eventUPP);
ZapHandle (typeList);
DisposeADList (&fileList, nil);
}
NavTypeListHandle NewNavTypeList (OSType applicationSignature, short numTypes, OSType typeList[])
{
NavTypeListHandle navTypeList;
navTypeList = nil;
if (numTypes > 0) {
navTypeList = (NavTypeListHandle) NuHandle (sizeof (NavTypeList) + numTypes * sizeof (OSType));
if (navTypeList) {
(*navTypeList)->componentSignature = applicationSignature;
(*navTypeList)->osTypeCount = numTypes;
BlockMoveData (typeList, (*navTypeList)->osType, numTypes * sizeof(OSType));
}
}
return (navTypeList);
}
OSErr GetSpecFromNthDesc (AEDescList *list, short n, FSSpec *spec)
{
AEDesc firstDesc;
AEKeyword ignoreKeyword;
OSErr theError,
disposeError;
theError = AEGetNthDesc (list, n, typeFSS, &ignoreKeyword, &firstDesc);
if (!theError)
AEGetDescData(&firstDesc,spec,sizeof(*spec));
disposeError = AEDisposeDesc (&firstDesc);
return (theError ? theError : disposeError);
}
Boolean GetFolderNav (char *name, short *volume, long *folder)
{
ZoneAndResFileRec zoneAndResFile;
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
NavEventUPP eventUPP;
NavObjectFilterUPP objectFilterUPP;
AEDesc defaultFolderDesc,
*defaultFolderDescPtr;
FSSpec spec;
OSErr theError,
disposeError;
Boolean result = false;
Zero (theReply);
eventUPP = NewNavEventUPP (updateNavProc);
objectFilterUPP = NewNavObjectFilterUPP (getFolderObjectFilterProc);
theError = NavGetDefaultDialogOptions (&dialogOptions);
if (theError)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
dialogOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
dialogOptions.dialogOptionFlags |= kNavAllowInvisibleFiles;
GetRString (dialogOptions.message, NAV_CHOOSE_FOLDER_MESSAGE);
// Try to make a spec... some routines pass us bogus vRef's though, so we'll try to
// make a daring (and, probably, temporary) recovery.
if (theError = FSMakeFSSpec (*volume, *folder, "\p", &spec)) {
HGetVol (name, volume, folder);
theError = FSMakeFSSpec (*volume, *folder, "\p", &spec);
}
if (!theError)
theError = AECreateDesc (typeFSS, &spec, sizeof (spec), &defaultFolderDesc);
defaultFolderDescPtr = theError ? nil : &defaultFolderDesc;
theError = noErr;
}
if (!theError) {
GetZoneAndResFile (&zoneAndResFile);
PushCursor(arrowCursor); // Have to do this to clear the watch cursor for OS X
theError = NavChooseFolder (defaultFolderDescPtr, &theReply, &dialogOptions, eventUPP, objectFilterUPP, &zoneAndResFile);
PopCursor(); // Have to do this to clear the watch cursor for OS X
SetZoneAndResFile (&zoneAndResFile);
if (theError && theError != userCanceledErr)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError && theReply.validRecord) {
theError = GetSpecFromNthDesc (&theReply.selection, 1, &spec);
if (!theError) {
*volume = spec.vRefNum;
*folder = spec.parID;
GetMyVolName (*volume, name);
}
result = true;
}
disposeError = NavDisposeReply (&theReply);
if (!theError)
theError = disposeError;
AEDisposeDesc (&defaultFolderDesc);
}
if (eventUPP)
DisposeNavEventUPP (eventUPP);
if (objectFilterUPP)
DisposeNavObjectFilterUPP (objectFilterUPP);
return (result);
}
pascal Boolean getFolderObjectFilterProc (AEDesc *theItem, void *info, ZoneAndResFilePtr zoneAndResFile, NavFilterModes filterMode)
{
AEDesc specDescriptor;
FSSpec spec;
CInfoPBRec hfi;
ZoneAndResFileRec navZoneAndResFile;
NavFileOrFolderInfo *theInfo;
long trashDirID;
short trashVRefNum;
Boolean display;
GetZoneAndResFile (&navZoneAndResFile);
SetZoneAndResFile (zoneAndResFile);
display = false;
theInfo = (NavFileOrFolderInfo*) info;
if (theItem->descriptorType == typeFSS)
if (display = theInfo->isFolder)
if (!theInfo->visible)
if (!AECoerceDesc (theItem, typeFSS, &specDescriptor)) {
AEGetDescData(&specDescriptor, &spec, sizeof(FSSpec));
if (!FindFolder (spec.vRefNum, kTrashFolderType, false, &trashVRefNum, &trashDirID)) {
hfi.hFileInfo.ioCompletion = nil;
hfi.hFileInfo.ioNamePtr = spec.name;
hfi.hFileInfo.ioVRefNum = spec.vRefNum;
hfi.hFileInfo.ioDirID = spec.parID;
hfi.hFileInfo.ioFDirIndex = 0;
if (!PBGetCatInfoSync ((CInfoPBPtr) &hfi))
display = (hfi.dirInfo.ioDrDirID == trashDirID);
}
}
SetZoneAndResFile (&navZoneAndResFile);
return (display);
}
Boolean MakeUserFindSettingsNav (FSSpecPtr theSpec)
{
static OSType types[] = {'PREF',0};
ZoneAndResFileRec zoneAndResFile;
NavTypeListHandle typeList;
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
NavEventUPP eventUPP;
FSSpec spec;
OSErr theError,
disposeError;
short numTypes;
Boolean result = true;
Zero (theReply);
numTypes = CountOSTypes (types, 0);
typeList = NewNavTypeList (CREATOR, numTypes, types);
eventUPP = NewNavEventUPP (updateNavProc);
theError = NavGetDefaultDialogOptions (&dialogOptions);
if (theError)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
dialogOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles;
GetRString (dialogOptions.message, NAV_CHOOSE_SETTINGS);
BlockMoveData (LMGetCurApName(), dialogOptions.clientName, LMGetCurApName()[0] + 1);
}
if (!theError) {
GetZoneAndResFile (&zoneAndResFile);
PushCursor(arrowCursor); // Have to do this to clear the watch cursor for OS X
theError = NavChooseFile (nil, &theReply, &dialogOptions, eventUPP, nil, nil, typeList, &zoneAndResFile);
PopCursor(); // Have to do this to clear the watch cursor for OS X
SetZoneAndResFile (&zoneAndResFile);
if (theError && theError != userCanceledErr)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError && theReply.validRecord) {
theError = GetSpecFromNthDesc (&theReply.selection, 1, &spec);
if (!theError) {
Root.vRef = spec.vRefNum;
Root.dirId = spec.parID;
*theSpec = spec;
result = false;
}
}
disposeError = NavDisposeReply (&theReply);
if (!theError)
theError = disposeError;
}
if (eventUPP)
DisposeNavEventUPP (eventUPP);
ZapHandle (typeList);
return (result);
}
OSErr SFPutOpenNav (FSSpecPtr spec, long creator, long type, short *refN, short ditlID, ScriptCode *script, FSSpecPtr defaultSpec,PStr windowTitle, PStr message)
{
ZoneAndResFileRec zoneAndResFile;
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
NavEventUPP eventUPP;
OSErr theError,
disposeError;
AEDesc defaultFolderDesc,
*defaultFolderDescPtr;
Zero (theReply);
switch (ditlID) {
case SAVEAS_NAV_DITL:
eventUPP = NewNavEventUPP (saveAsNavProc);
break;
default:
eventUPP = NewNavEventUPP (updateNavProc);
break;
}
theError = NavGetDefaultDialogOptions (&dialogOptions);
if (theError)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
dialogOptions.dialogOptionFlags = kNavNoTypePopup | kNavDontAddTranslateItems;
PCopy (dialogOptions.savedFileName, spec->name);
if (windowTitle)
PCopy (dialogOptions.windowTitle, windowTitle);
if (message)
PCopy (dialogOptions.message, message);
GetZoneAndResFile (&zoneAndResFile);
// Set up default folder (if any)
if (defaultSpec)
defaultFolderDescPtr = AECreateDesc (typeFSS, defaultSpec, sizeof (FSSpec), &defaultFolderDesc) ? nil : &defaultFolderDesc;
else
defaultFolderDescPtr = nil;
PushCursor(arrowCursor); // Have to do this to clear the watch cursor for OS X
theError = NavPutFile (defaultFolderDescPtr, &theReply, &dialogOptions, eventUPP, type, creator, &zoneAndResFile);
PopCursor(); // Have to do this to clear the watch cursor for OS X
if (defaultFolderDescPtr)
AEDisposeDesc(defaultFolderDescPtr);
SetZoneAndResFile (&zoneAndResFile);
if (theError && theError != userCanceledErr)
WarnUser (NAV_GENERAL_ERR, theError);
else {
if (theReply.validRecord) {
*script = GetScriptManagerVariable (smKeyScript);
theError = GetSpecFromNthDesc (&theReply.selection, 1, spec);
}
else
theError = 1;
}
disposeError = NavDisposeReply (&theReply);
if (!theError)
theError = disposeError;
}
if (eventUPP)
DisposeNavEventUPP (eventUPP);
return (theError);
}
pascal void saveAsNavProc (NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, ZoneAndResFilePtr zoneAndResFile)
{
ZoneAndResFileRec navZoneAndResFile;
DialogPtr theDialog;
OSErr theError;
GetZoneAndResFile (&navZoneAndResFile);
SetZoneAndResFile (zoneAndResFile);
theDialog = callBackParms->window ? GetDialogFromWindow (callBackParms->window) : nil;
theError = noErr;
switch (callBackSelector) {
case kNavCBEvent:
theError = HandleSaveAsNavEvent (theDialog, callBackParms->eventData.eventDataParms.event, callBackParms->context);
break;
case kNavCBCustomize :
NegotiateCustomRectSize (&callBackParms->customRect, 400, 50);
break;
case kNavCBStart :
theError = AddDialogItemsToNav (SAVEAS_NAV_DITL, callBackParms->context);
if (!theError) {
SetDItemState (theDialog, GetCustomNavItem (callBackParms->context, rNavSaveAsGuessParagraphsItem), PrefIsSet(PREF_PARAGRAPHS));
SetDItemState (theDialog, GetCustomNavItem (callBackParms->context, rNavSaveAsIncludeHeadersItem), !PrefIsSet(PREF_EXCLUDE_HEADERS));
#ifdef TWO
if (Stationery == 2) {
HideDialogItem (theDialog, GetCustomNavItem (callBackParms->context, rNavSaveAsStationeryItem));
HideDialogItem (theDialog, GetCustomNavItem (callBackParms->context, rNavSaveAsGoToStationeryItem));
Stationery = 0; // (jp) Since we're hiding the items, we better make sure we don't save as stationery
}
else
SetDItemState (theDialog, GetCustomNavItem (callBackParms->context, rNavSaveAsStationeryItem), Stationery);
saveAsCustomHilite (theDialog, callBackParms->context);
#endif
}
break;
}
SetZoneAndResFile (&navZoneAndResFile);
}
void saveAsCustomHilite (DialogPtr navDialog, NavDialogRef context)
{
HiliteControl (GetDItemCtl (navDialog, GetCustomNavItem (context, rNavSaveAsIncludeHeadersItem)), Stationery == 1 ? 255 : 0);
HiliteControl (GetDItemCtl (navDialog, GetCustomNavItem (context, rNavSaveAsGuessParagraphsItem)), Stationery == 1 ? 255 : 0);
HiliteControl (GetDItemCtl (navDialog, GetCustomNavItem (context, rNavSaveAsGoToStationeryItem)), Stationery == 1 ? 0 : 255);
}
OSErr HandleSaveAsNavEvent (DialogPtr navDialog, EventRecord *event, NavDialogRef context)
{
GrafPtr savedPort;
WindowPtr win;
AEDesc location;
FSSpec spec;
Point mouseLoc;
OSErr theError;
short partCode,
firstItem,
foundItem;
switch (event->what) {
case mouseDown:
partCode = FindWindow (event->where, &win);
if (partCode == inContent && navDialog == GetDialogFromWindow (win)) {
GetPort (&savedPort);
SetPort (GetDialogPort(navDialog)); // Hack for Nav Services 1.0
GetMouse (&mouseLoc);
SetPort (savedPort);
foundItem = FindDialogItem (navDialog, mouseLoc);
if (foundItem >= 0) {
firstItem = GetCustomNavItem (context, 1);
switch (foundItem - firstItem + 2) {
case rNavSaveAsGuessParagraphsItem:
SetDItemState (navDialog, foundItem + 1, !GetDItemState (navDialog, foundItem + 1));
TogglePref(PREF_PARAGRAPHS);
break;
case rNavSaveAsIncludeHeadersItem:
SetDItemState (navDialog, foundItem + 1, !GetDItemState (navDialog, foundItem + 1));
TogglePref(PREF_EXCLUDE_HEADERS);
break;
case rNavSaveAsStationeryItem:
Stationery = !GetDItemState (navDialog, foundItem + 1);
SetDItemState (navDialog, foundItem + 1, Stationery);
saveAsCustomHilite (navDialog, context);
break;
case rNavSaveAsGoToStationeryItem:
theError = FSMakeFSSpec (StationVRef, StationDirId, "\p", &spec);
if (!theError)
theError = AECreateDesc (typeFSS, &spec, sizeof (spec), &location);
if (!theError) {
theError = NavCustomControl (context, kNavCtlSetLocation, &location);
AEDisposeDesc (&location);
}
break;
}
}
}
break;
case updateEvt:
DoUpdate ((WindowPtr) event->message, event);
break;
}
return (theError);
}
short CountOSTypes (OSType *types, short limit)
{
OSType *tPtr;
short numTypes;
tPtr = types;
numTypes = 0;
while (*tPtr++)
if (++numTypes == limit)
return (limit);
return (numTypes);
}
OSErr GetFileNav (OSType *types, short titleResID, Str255 prompt, short actionButtonLabelResID, Boolean allowPackages, FSSpec *spec, Boolean *good, ProcPtr objectFilterProc)
{
ZoneAndResFileRec zoneAndResFile;
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
NavTypeListHandle typeList;
NavEventUPP eventUPP;
NavObjectFilterUPP objectFilterUPP;
OSErr theError,
disposeError;
short numTypes;
*good = false;
Zero (theReply);
numTypes = 0;
typeList = nil;
if (types) {
numTypes = CountOSTypes (types, 0);
typeList = NewNavTypeList (CREATOR, numTypes, types);
}
eventUPP = NewNavEventUPP (updateNavProc);
objectFilterUPP = objectFilterProc ? NewNavObjectFilterUPP (objectFilterProc) : nil;
theError = NavGetDefaultDialogOptions (&dialogOptions);
if (theError)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
if (titleResID)
GetRString (dialogOptions.windowTitle, titleResID);
if (actionButtonLabelResID)
GetRString (dialogOptions.actionButtonLabel, actionButtonLabelResID);
if (allowPackages && HaveOSX ())
dialogOptions.dialogOptionFlags |= kNavSupportPackages;
PCopy (dialogOptions.message, prompt);
GetZoneAndResFile (&zoneAndResFile);
PushCursor(arrowCursor); // Have to do this to clear the watch cursor for OS X
theError = NavChooseFile (nil, &theReply, &dialogOptions, eventUPP, nil, objectFilterUPP, typeList, &zoneAndResFile);
PopCursor(); // Have to do this to clear the watch cursor for OS X
SetZoneAndResFile (&zoneAndResFile);
if (theError && theError != userCanceledErr)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
if (*good = theReply.validRecord)
theError = GetSpecFromNthDesc (&theReply.selection, 1, spec);
}
disposeError = NavDisposeReply (&theReply);
if (!theError)
theError = disposeError;
}
if (eventUPP)
DisposeNavEventUPP (eventUPP);
if (objectFilterUPP)
DisposeNavObjectFilterUPP (objectFilterUPP);
ZapHandle (typeList);
return (theError);
}
pascal Boolean mailboxFileFilterNavProc (AEDesc *theItem, void *info, ZoneAndResFilePtr zoneAndResFile, NavFilterModes filterMode)
{
ZoneAndResFileRec navZoneAndResFile;
NavFileOrFolderInfo *theInfo;
FSSpec spec;
Boolean display;
GetZoneAndResFile (&navZoneAndResFile);
SetZoneAndResFile (zoneAndResFile);
display = true;
theInfo = (NavFileOrFolderInfo*) info;
if (theItem->descriptorType == typeFSS)
if (!theInfo->isFolder) {
AEGetDescData(theItem, &spec, sizeof(FSSpec));
display = IsMailbox (&spec);
}
SetZoneAndResFile (&navZoneAndResFile);
return (display);
}
OSErr SFODocNav (FSSpecPtr doc, SFTypeList types, Str255 prompt, FSSpec *spec, SFODocPtr optionsPtr, Boolean *good)
{
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
NavTypeListHandle typeList;
NavEventUPP eventUPP;
NavObjectFilterUPP objectFilterUPP;
OSErr theError,
disposeError;
short numTypes;
*good = false;
Zero (theReply);
numTypes = CountOSTypes (types, 0);
typeList = NewNavTypeList (CREATOR, numTypes, types);
eventUPP = NewNavEventUPP (sfODocNavProc);
objectFilterUPP = NewNavObjectFilterUPP (sfOdocObjectFilterProc);
theError = NavGetDefaultDialogOptions (&dialogOptions);
if (theError)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
if (HaveOSX ())
dialogOptions.dialogOptionFlags |= kNavSupportPackages;
PCopy (dialogOptions.message, prompt);
GetZoneAndResFile (&optionsPtr->zoneAndResFile);
PushCursor(arrowCursor); // Have to do this to clear the watch cursor for OS X
theError = NavChooseFile (nil, &theReply, &dialogOptions, eventUPP, nil, objectFilterUPP, typeList, optionsPtr);
PopCursor(); // Have to do this to clear the watch cursor for OS X
SetZoneAndResFile (&optionsPtr->zoneAndResFile);
if (theError && theError != userCanceledErr)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
if (*good = theReply.validRecord)
theError = GetSpecFromNthDesc (&theReply.selection, 1, spec);
}
disposeError = NavDisposeReply (&theReply);
if (!theError)
theError = disposeError;
}
if (eventUPP)
DisposeNavEventUPP (eventUPP);
if (objectFilterUPP)
DisposeNavObjectFilterUPP (objectFilterUPP);
ZapHandle (typeList);
return (theError);
}
pascal void sfODocNavProc (NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, SFODocPtr optionsPtr)
{
ZoneAndResFileRec navZoneAndResFile;
DialogPtr theDialog;
OSErr theError;
GetZoneAndResFile (&navZoneAndResFile);
SetZoneAndResFile (&optionsPtr->zoneAndResFile);
theDialog = callBackParms->window ? GetDialogFromWindow (callBackParms->window) : nil;
theError = noErr;
switch (callBackSelector) {
case kNavCBEvent:
theError = HandleODocNavEvent (theDialog, callBackParms->eventData.eventDataParms.event, callBackParms->context, optionsPtr);
break;
case kNavCBCustomize :
NegotiateCustomRectSize (&callBackParms->customRect, 400, 40);
break;
case kNavCBStart :
theError = AddDialogItemsToNav (SFODOC_DITL, callBackParms->context);
if (!theError)
if (TypeIsOnList (optionsPtr->type, FINDER_LIST_TYPE) == kTOLOther) {
optionsPtr->permanently = False;
SetDItemState (theDialog, GetCustomNavItem (callBackParms->context, rNavOdocAlwaysUseThisApp), false);
HiliteControl (GetDItemCtl (theDialog, GetCustomNavItem (callBackParms->context, rNavOdocAlwaysUseThisApp)), 255);
}
else
SetDItemState (theDialog, GetCustomNavItem (callBackParms->context, rNavOdocAlwaysUseThisApp), optionsPtr->permanently);
break;
}
SetZoneAndResFile (&navZoneAndResFile);
}
OSErr HandleODocNavEvent (DialogPtr navDialog, EventRecord *event, NavDialogRef context, SFODocPtr optionsPtr)
{
GrafPtr savedPort;
WindowPtr win;
Point mouseLoc;
OSErr theError;
short partCode,
firstItem,
foundItem;
theError = noErr;
switch (event->what) {
case mouseDown:
partCode = FindWindow (event->where, &win);
if (partCode == inContent && navDialog == GetDialogFromWindow (win)) {
GetPort (&savedPort);
SetPort (GetDialogPort(navDialog)); // Hack for Nav Services 1.0
GetMouse (&mouseLoc);
SetPort (savedPort);
foundItem = FindDialogItem (navDialog, mouseLoc);
if (foundItem >= 0) {
firstItem = GetCustomNavItem (context, 1);
switch (foundItem - firstItem + 2) {
case rNavOdocUseFinder:
optionsPtr->finder = True;
NavCustomControl (context, kNavCtlCancel, nil); // Can't return 'accept' since 'Choose' might be inactive
break;
case rNavOdocAlwaysUseThisApp:
SetDItemState (navDialog, GetCustomNavItem (context, rNavOdocAlwaysUseThisApp), optionsPtr->permanently = !optionsPtr->permanently);
break;
}
}
}
break;
case updateEvt:
DoUpdate ((WindowPtr) event->message, event);
break;
}
return (theError);
}
pascal Boolean sfOdocObjectFilterProc (AEDesc *theItem, void *info, SFODocPtr optionsPtr, NavFilterModes filterMode)
{
ZoneAndResFileRec navZoneAndResFile;
NavFileOrFolderInfo *theInfo;
FSSpec spec;
Str31 name;
OSErr theError;
long dirId;
short vRef;
Boolean display;
GetZoneAndResFile (&navZoneAndResFile);
SetZoneAndResFile (&optionsPtr->zoneAndResFile);
display = true;
theInfo = (NavFileOrFolderInfo*) info;
if (theItem->descriptorType == typeFSS)
if (!theInfo->isFolder) {
AEGetDescData(theItem, &spec, sizeof(FSSpec));
if (theInfo->fileAndFolder.fileInfo.finderInfo.fdType == 'adrp') {
PCopy (name, spec.name);
vRef = spec.vRefNum;
dirId = spec.parID;
if (theError = MyResolveAlias (&vRef, &dirId, name, nil))
return (false);
return (CanOpen (TypeToOpen, vRef, dirId, name));
}
return (CanOpen (TypeToOpen, spec.vRefNum, spec.parID, spec.name));
}
SetZoneAndResFile (&navZoneAndResFile);
return (display);
}
OSErr SFURLAppNav (PStr proto, AliasHandle *alias, SFTypeList types, Str255 prompt, FSSpec *spec, URLHookOptionsPtr optionsPtr, Boolean *good)
{
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
NavTypeListHandle typeList;
NavEventUPP eventUPP;
OSErr theError,
disposeError;
short numTypes;
*good = false;
Zero (theReply);
numTypes = CountOSTypes (types, 0);
typeList = NewNavTypeList (CREATOR, numTypes, types);
eventUPP = NewNavEventUPP (SFURLAppNavProc);
theError = NavGetDefaultDialogOptions (&dialogOptions);
if (theError)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
if (HaveOSX ())
dialogOptions.dialogOptionFlags |= kNavSupportPackages;
PCopy (dialogOptions.message, prompt);
GetZoneAndResFile (&optionsPtr->zoneAndResFile);
PushCursor(arrowCursor); // Have to do this to clear the watch cursor for OS X
theError = NavChooseFile (nil, &theReply, &dialogOptions, eventUPP, nil, nil, typeList, optionsPtr);
PopCursor(); // Have to do this to clear the watch cursor for OS X
SetZoneAndResFile (&optionsPtr->zoneAndResFile);
if (theError && theError != userCanceledErr)
WarnUser (NAV_GENERAL_ERR, theError);
if (!theError) {
if (*good = theReply.validRecord)
theError = GetSpecFromNthDesc (&theReply.selection, 1, spec);
}
disposeError = NavDisposeReply (&theReply);
if (!theError)
theError = disposeError;
}
if (eventUPP)
DisposeNavEventUPP (eventUPP);
ZapHandle (typeList);
return (theError);
}
pascal void SFURLAppNavProc (NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, URLHookOptionsPtr optionsPtr)
{
ZoneAndResFileRec navZoneAndResFile;
DialogPtr theDialog;
OSErr theError;
GetZoneAndResFile (&navZoneAndResFile);
SetZoneAndResFile (&optionsPtr->zoneAndResFile);
theDialog = callBackParms->window ? GetDialogFromWindow (callBackParms->window) : nil;
theError = noErr;
switch (callBackSelector) {
case kNavCBEvent:
theError = HandleURLAppNavEvent (theDialog, callBackParms->eventData.eventDataParms.event, callBackParms->context, optionsPtr);
break;
case kNavCBCustomize :
NegotiateCustomRectSize (&callBackParms->customRect, 400, 40);
break;
case kNavCBStart :
theError = AddDialogItemsToNav (URL_DITL, callBackParms->context);
if (!theError)
if (optionsPtr->protocol) {
optionsPtr->permanently = False;
SetDItemState (theDialog, GetCustomNavItem (callBackParms->context, rNavURLAppAlwaysUseThisApp), false);
HiliteControl (GetDItemCtl (theDialog, GetCustomNavItem (callBackParms->context, rNavURLAppAlwaysUseThisApp)), 255);
}
else
SetDItemState (theDialog, GetCustomNavItem (callBackParms->context, rNavURLAppAlwaysUseThisApp), optionsPtr->permanently);
break;
}
SetZoneAndResFile (&navZoneAndResFile);
}
OSErr HandleURLAppNavEvent (DialogPtr navDialog, EventRecord *event, NavDialogRef context, URLHookOptionsPtr optionsPtr)
{
GrafPtr savedPort;
WindowPtr win;
Point mouseLoc;
OSErr theError;
short partCode,
firstItem,
foundItem;
theError = noErr;
switch (event->what) {
case mouseDown:
partCode = FindWindow (event->where, &win);
if (partCode == inContent && navDialog == GetDialogFromWindow (win)) {
GetPort (&savedPort);
SetPort (GetDialogPort(navDialog)); // Hack for Nav Services 1.0
GetMouse (&mouseLoc);
SetPort (savedPort);
foundItem = FindDialogItem (navDialog, mouseLoc);
if (foundItem >= 0) {
firstItem = GetCustomNavItem (context, 1);
if (foundItem - firstItem + 2 == rNavURLAppAlwaysUseThisApp)
SetDItemState (navDialog, GetCustomNavItem (context, rNavURLAppAlwaysUseThisApp), optionsPtr->permanently = !optionsPtr->permanently);
}
}
break;
case updateEvt:
DoUpdate ((WindowPtr) event->message, event);
break;
}
return (theError);
}