diff --git a/DSAppleEvents.c b/DSAppleEvents.c new file mode 100644 index 0000000..8e112ba --- /dev/null +++ b/DSAppleEvents.c @@ -0,0 +1 @@ +/****************************************************************************** ** ** Project Name: DropShell ** File Name: DSAppleEvents.c ** ** Description: Generic AppleEvent handling routines ** ** This is the set of routines for handling the required Apple events. ** You should NEVER have to modify this file!!! ** Simply add code in DSUserProcs to the routines called by these. ** ******************************************************************************* ** A U T H O R I D E N T I T Y ******************************************************************************* ** ** Initials Name ** -------- ----------------------------------------------- ** LDR Leonard Rosenthol ** MTC Marshall Clow ** SCS Stephan Somogyi ** ******************************************************************************* ** R E V I S I O N H I S T O R Y ******************************************************************************* ** ** Date Time Author Description ** -------- ----- ------ --------------------------------------------- ** 11/24/91 LDR Added a handler for 'pdoc' as per DTS recommendation ** This caused some reorg & userProc routine changes ** I also created a new common AEVT doc extractor ** Cleaned up error handling by adding FailErr ** Cleaned up the placement of braces ** Added the passing of a userDataHandle to the odoc/pdoc routines ** 10/29/91 SCS Changes for THINK C 5 ** 10/28/91 LDR Officially renamed DropShell (from QuickShell) ** Added a bunch of comments for clarification ** 10/06/91 00:02 MTC Converted to MPW C ** 04/09/91 00:02 LDR Added to Projector ** ******************************************************************************/ #include "DSGlobals.h" #include "DSUserProcs.h" #include "DSAppleEvents.h" /* This routine does all initialization for AEM, including the creation and then population of the dispatch table. */ #pragma segment Initialize pascal void InitAEVTStuff () { OSErr aevtErr; aevtErr = noErr; if ( aevtErr == noErr ) aevtErr = AEInstallEventHandler ( kCoreEventClass, kAEOpenApplication, NewAEEventHandlerProc((ProcPtr)HandleOAPP), 0, false ); if ( aevtErr == noErr ) aevtErr = AEInstallEventHandler ( kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerProc((ProcPtr)HandleODOC), 0, false ); if ( aevtErr == noErr ) aevtErr = AEInstallEventHandler ( kCoreEventClass, kAEPrintDocuments, NewAEEventHandlerProc((ProcPtr)HandlePDOC), 0, false ); if ( aevtErr == noErr ) aevtErr = AEInstallEventHandler ( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerProc((ProcPtr)HandleQuit), 0, false ); if ( aevtErr == noErr ) InstallOtherEvents (); if ( aevtErr != noErr ) ; // report an error if you are so included } /* This routine is a utility routine for checking that all required parameters in the Apple event have been used. */ #pragma segment Main OSErr GotRequiredParams ( AppleEvent *theAppleEvent ) { DescType typeCode; Size actualSize; OSErr retErr, err; err = AEGetAttributePtr ( theAppleEvent, keyMissedKeywordAttr, typeWildCard, &typeCode, NULL, 0, &actualSize ); if ( err == errAEDescNotFound ) // we got all the required params: all is ok retErr = noErr; else if ( err == noErr ) retErr = errAEEventNotHandled; else retErr = err; return retErr; } /* This is another routine useful for showing debugging info. It calls the ErrorAlert routine from DSUtils to put up the error message. */ void FailErr(OSErr err) { if (err != noErr) ErrorAlert(kErrStringID, kAEVTErr, err); } /* This routine is the handler for the oapp (Open Application) event. It first checks the number of parameters to make sure we got them all (even though we don't want any) and then calls the OpenApp userProc in QSUserProcs. Finally it checks to see if the caller wanted a reply & sends one, setting any error. */ #pragma segment Main pascal OSErr HandleOAPP ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) { #pragma unused ( handlerRefcon ) OSErr err; FailErr(err = GotRequiredParams ( theAppleEvent )); // let's show the user the splash screen //ShowWindow(gSplashScreen); OpenApp (); // pass it on to the app specific routine if ( reply->dataHandle != NULL ) /* a reply is sought */ FailErr(err = AEPutParamPtr ( reply, 'errs', 'TEXT', "Opening", 7 )); return err; } /* This routine is the handler for the quit (Quit Application) event. It first checks the number of parameters to make sure we got them all (even though we don't want any) and then calls the QuitApp userProc in QSUserProcs. Finally it checks to see if the caller wanted a reply & sends one, setting any error. */ #pragma segment Main pascal OSErr HandleQuit ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) { #pragma unused ( handlerRefcon ) OSErr err; FailErr( err = GotRequiredParams ( theAppleEvent )); QuitApp (); // pass it on to the app specific routine if ( reply->dataHandle != NULL ) /* a reply is sought */ FailErr(err = AEPutParamPtr ( reply, 'errs', 'TEXT', "Qutting", 7 )); return err; } /* This routine is the low level processing routine for both the odoc (Open Document) and pdoc (Print Document) events. This routine is the key one, since this is how we get the list of files/folders/disks to process. The first thing to do is the get the list of files, and then make sure that's all the parameters (should be!). We then send call the PreflightDocs routine (from DSUserProcs), process each file in the list by calling OpenDoc (again in DSUserProcs), and finally call PostflightDocs (you know where) and setting a return value. */ #pragma segment Main pascal OSErr _HandleDocs ( AppleEvent *theAppleEvent, AppleEvent *reply, Boolean opening ) { #pragma unused ( reply ) #pragma unused ( handlerRefcon ) OSErr err; FSSpec myFSS; AEDescList docList; long index, itemsInList; Size actualSize; AEKeyword keywd; DescType typeCode; Handle userDataHandle; FailErr(err = AEGetParamDesc ( theAppleEvent, keyDirectObject, typeAEList, &docList )); FailErr(err = GotRequiredParams ( theAppleEvent )); if (PreFlightDocs (opening, &userDataHandle)) { // let the app do any preflighting it might need /* How many items do we have?. */ FailErr(err = AECountItems ( &docList, &itemsInList )); for ( index = 1; index <= itemsInList; index++ ) { FailErr(err = AEGetNthPtr ( &docList, index, typeFSS, &keywd, &typeCode, (Ptr) &myFSS, sizeof ( myFSS ), &actualSize )); OpenDoc( &myFSS, opening, userDataHandle ); // call the userProc } PostFlightDocs (opening, userDataHandle); // cleanup time } else err = errAEEventNotHandled; // tells AEM that we didn't handle it! FailErr(AEDisposeDesc ( &docList )); return err; } /* This routine is the handler for the odoc (Open Document) event. The odoc event simply calls the common _HandleDocs routines, which will do the dirty work of parsing the AEVT & calling the userProcs. */ #pragma segment Main pascal OSErr HandleODOC ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) { #pragma unused ( handlerRefcon ) return (_HandleDocs(theAppleEvent, reply, true)); // call the low level routine } /* This routine is the handler for the pdoc (Print Document) event. The pdoc event like the odoc simply calls the common _HandleDocs routines */ #pragma segment Main pascal OSErr HandlePDOC ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) { #pragma unused ( handlerRefcon ) return (_HandleDocs(theAppleEvent, reply, false)); // call the low level routine } /* This is the routine called by the main event loop, when a high level event is found. Since we only deal with Apple events, and not other high level events, we just pass everything onto the AEM via AEProcessAppleEvent */ #pragma segment Main pascal void DoHighLevelEvent ( EventRecord *event ) { FailErr ( AEProcessAppleEvent ( event ) ); } \ No newline at end of file diff --git a/DSAppleEvents.h b/DSAppleEvents.h new file mode 100644 index 0000000..08201fe --- /dev/null +++ b/DSAppleEvents.h @@ -0,0 +1 @@ +/****************************************************************************** ** ** Project Name: DropShell ** File Name: DSAppleEvents.h ** ** Description: Header w/prototypes for the generic AppleEvent handling routines ** ******************************************************************************* ** A U T H O R I D E N T I T Y ******************************************************************************* ** ** Initials Name ** -------- ----------------------------------------------- ** LDR Leonard Rosenthol ** MTC Marshall Clow ** SCS Stephan Somogyi ** ******************************************************************************* ** R E V I S I O N H I S T O R Y ******************************************************************************* ** ** Date Time Author Description ** -------- ----- ------ --------------------------------------------- ** 11/24/91 LDR Added new routines to this header ** 10/29/91 SCS Changes for THINK C 5 ** 10/28/91 LDR Officially renamed DropShell (from QuickShell) ** 10/06/91 00:02 MTC Converted to MPW C ** 04/09/91 00:02 LDR Added to Projector ** ******************************************************************************/ #pragma once #ifndef __DSAPPLEEVENTS_H__ #define __DSAPPLEEVENTS_H__ #include #include #include #include pascal void InitAEVTStuff(void); OSErr GotRequiredParams(AppleEvent *theAppleEvent); void FailErr(OSErr err); pascal OSErr _HandleDocs ( AppleEvent *theAppleEvent, AppleEvent *reply, Boolean opening ); pascal OSErr HandleOAPP(AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon); pascal OSErr HandleQuit(AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon); pascal OSErr HandleODOC(AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon); pascal OSErr HandlePDOC(AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon); pascal void DoHighLevelEvent(EventRecord *event); #endif __DSAPPLEEVENTS_H__ \ No newline at end of file diff --git a/DSDialogs.c b/DSDialogs.c new file mode 100644 index 0000000..5232d21 --- /dev/null +++ b/DSDialogs.c @@ -0,0 +1 @@ +#include #include #include #include #include #include "DSGlobals.h" #include "DSDialogs.h" #include "DSUtilsASG.h" enum { kPrefsDLOG = 149, kPrefsExtensionText = 1, kPrefsCreatorTypeText, kPrefsIconText, kPrefsListUser, kPrefsNewButton, kPrefsEditButton, kPrefsDeleteButton, kPrefsExtensionText2, kPrefsExtensionEdit, kPrefsExtensionStatic, kPrefsCreatorText, kPrefsCreatorEdit, kPrefsCreatorStatic, kPrefsTypeText, kPrefsTypeEdit, kPrefsTypeStatic, kPrefsApplicationText, kPrefsApplicationStatic, kPrefsIconText2, kPrefsIconUser, kPrefsDragAndDropText, kPrefsExampleButton, kPrefsTrashMenu }; static void InitPrefsList(void); static void AllocateIconGWorld(void); static void SetUpForDragging(void); static void UpdateSelection(void); static void UpdateButtons(void); static pascal void ListUserItem(DialogPtr theDialog, short theItem); static pascal void IconUserItem(DialogPtr theDialog, short theItem); static OSErr FindIcon(OSType creator, OSType type); static void EraseGWorld(GWorldPtr theGWorld); static void StartEditing(void); static void StopEditing(void); static void SaveChanges(void); static void SaveResults(void); static void SetExampleType(OSType creator, OSType type); static pascal OSErr DragTracker(DragTrackingMessage message, WindowPtr theWindow, void *handlerRefCon, DragReference theDragRef); static pascal OSErr DragReceiver(WindowPtr theWindow, void *handlerRefCon, DragReference theDragRef); static Boolean IsValidDrag(DragReference theDragRef, HFSFlavor *theFile); static UserItemUPP gListUserItem = nil, gIconUserItem = nil; static DragTrackingHandlerUPP gDragTracker = nil; static DragReceiveHandlerUPP gDragReceiver = nil; static DialogPtr gPrefsDialog = nil; static ListHandle gPrefsList = nil; static GWorldPtr gTheGWorld = nil; static OSErr gIconType = kLargeIcon; static Rect gIconBounds = { 0, 0, 32, 32 }; static Boolean gEditing = false; static Str255 gApplicationName; static char gIconBuffer[1024]; void OpenUserDialog(short menuID, short menuItem) { if (gPrefsDialog) { SelectWindow(gPrefsDialog); return; } gPrefsDialog = GetNewDialog(kPrefsDLOG, nil, (WindowPtr)-1); if (gPrefsDialog) { SetWindowPosition(gPrefsDialog, (*gPrefs)->prefsLoc); ShowWindow(gPrefsDialog); InitPrefsList(); if (gPrefsList) { AllocateIconGWorld(); if (!gIconUserItem) gIconUserItem = NewUserItemProc(IconUserItem); SetItemHandle(gPrefsDialog, kPrefsIconUser, (Handle)gIconUserItem); EnableItem(GetMHandle(kFileNum), 2); SetUpForDragging(); StopEditing(); UpdateSelection(); UpdateButtons(); SetCtlValue((ControlHandle)GetItemHandle(gPrefsDialog, kPrefsTrashMenu), (*gPrefs)->trashSource + 1); } } } void InitPrefsList(void) { Rect itemRect, listRect = { 0, 0, 0, 1 }; Handle itemHandle; Point cellSize; short itemType; GetDItem(gPrefsDialog, kPrefsListUser, &itemType, &itemHandle, &itemRect); itemRect.right -= 16; cellSize.v = 32 + 2; cellSize.h = itemRect.right - itemRect.left; gPrefsList = LNew(&itemRect, &listRect, cellSize, 128, gPrefsDialog, false, false, false, true); if (gPrefsList) { int count = *(short *)*gMapResource, offset = 0; Cell theCell = { 0, 0 }; (*gPrefsList)->selFlags = lOnlyOne + lNoNilHilite; LAddRow(count, 32767, gPrefsList); offset += 2; while (count--) { char data[16]; BlockMove(*gMapResource + offset, data, 16); LSetCell(data, 16, theCell, gPrefsList); theCell.v++; offset += 16; } } if (!gListUserItem) gListUserItem = NewUserItemProc(ListUserItem); SetItemHandle(gPrefsDialog, kPrefsListUser, (Handle)gListUserItem); LDoDraw(true, gPrefsList); } void AllocateIconGWorld(void) { short depth; if (((CGrafPtr)gPrefsDialog)->portVersion & 0xc000) { GDHandle mostDevice; GrafPtr oldPort; Rect globalRect; GetPort(&oldPort); SetPort(gPrefsDialog); globalRect = gPrefsDialog->portRect; LocalToGlobal((Point *)&globalRect.top); LocalToGlobal((Point *)&globalRect.bottom); mostDevice = GetMaxDevice(&globalRect); depth = (*(*mostDevice)->gdPMap)->pixelSize; if (depth == 4) gIconType = kLarge4BitIcon, depth = 4; else if (depth > 4) gIconType = kLarge8BitIcon, depth = 8; else gIconType = kLargeIcon, depth = 1; SetPort(oldPort); } else gIconType = kLargeIcon, depth = 1; OSErr theErr = NewGWorld(&gTheGWorld, depth, &gIconBounds, nil, nil, 0); if (theErr != noErr || !gTheGWorld) theErr = NewGWorld(&gTheGWorld, depth, &gIconBounds, nil, nil, useTempMem); } void SetUpForDragging(void) { long resp; if (Gestalt(gestaltDragMgrAttr, &resp) == noErr && (resp & (1L << gestaltDragMgrPresent)) #if defined(powerc) || defined(__powerc) && (Ptr)InstallTrackingHandler != kUnresolvedSymbolAddress #endif ) { if (!gDragTracker) { gDragTracker = NewDragTrackingHandlerProc(DragTracker); gDragReceiver = NewDragReceiveHandlerProc(DragReceiver); } InstallTrackingHandler(gDragTracker, gPrefsDialog, 0); InstallReceiveHandler(gDragReceiver, gPrefsDialog, 0); } } void DoUserDialog(DialogPtr theDialog, short itemHit) { static char defaultData[16] = { 4,'.','N','E','W',0, '?','?','?','?', '?','?','?','?', 0,0 }; static Str255 lastExtension, lastCreator, lastType; static short lastSelStart, lastSelEnd, lastItem; StandardFileReply reply; Cell theCell, tempCell; EventRecord theEvent; SFTypeList theTypes; Str255 string; switch (itemHit) { case kPrefsListUser: if (gEditing) SaveChanges(), StopEditing(); EventAvail(0, &theEvent); SetPort(theDialog); GlobalToLocal(&theEvent.where); Boolean dClick = LClick(theEvent.where, theEvent.modifiers, gPrefsList); UpdateSelection(); UpdateButtons(); if (dClick) StartEditing(); break; case kPrefsNewButton: if (gEditing) SaveChanges(), StopEditing(); theCell.v = LAddRow(1, 32767, gPrefsList); theCell.h = 0; tempCell.h = tempCell.v = 0; while (LGetSelect(true, &tempCell, gPrefsList)) LSetSelect(false, tempCell, gPrefsList); LSetCell(defaultData, 16, theCell, gPrefsList); LSetSelect(true, theCell, gPrefsList); LAutoScroll(gPrefsList); UpdateSelection(); StartEditing(); break; case kPrefsEditButton: if (!gEditing) StartEditing(); else { SaveChanges(), StopEditing(); UpdateSelection(); } break; case kPrefsDeleteButton: if (gEditing) StopEditing(); theCell.h = theCell.v = 0; LGetSelect(true, &theCell, gPrefsList); LDelRow(1, theCell.v, gPrefsList); if (theCell.v >= (*gPrefsList)->dataBounds.bottom) theCell.v--; LSetSelect(true, theCell, gPrefsList); UpdateSelection(); UpdateButtons(); lastItem = kPrefsDeleteButton; break; case kPrefsExtensionEdit: GetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionEdit), string); if (*string > 5) { SysBeep(1); SetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionEdit), lastExtension); SelIText(gPrefsDialog, kPrefsExtensionEdit, lastSelStart, lastSelEnd); } break; case kPrefsCreatorEdit: GetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorEdit), string); if (*string > 4) { SysBeep(1); SetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorEdit), lastCreator); SelIText(gPrefsDialog, kPrefsCreatorEdit, lastSelStart, lastSelEnd); } break; case kPrefsTypeEdit: GetIText(GetItemHandle(gPrefsDialog, kPrefsTypeEdit), string); if (*string > 4) { SysBeep(1); SetIText(GetItemHandle(gPrefsDialog, kPrefsTypeEdit), lastType); SelIText(gPrefsDialog, kPrefsTypeEdit, lastSelStart, lastSelEnd); } break; case kPrefsTrashMenu: (*gPrefs)->trashSource = GetCtlValue((ControlHandle)GetItemHandle(gPrefsDialog, kPrefsTrashMenu)) - 1; UpdateButtons(); break; case kPrefsExampleButton: StandardGetFile(nil, -1, theTypes, &reply); if (reply.sfGood) { FInfo theInfo; FSpGetFInfo(&reply.sfFile, &theInfo); SetExampleType(theInfo.fdCreator, theInfo.fdType); } break; } if (itemHit != lastItem && (lastItem == kPrefsExtensionEdit || lastItem == kPrefsTypeEdit || lastItem == kPrefsCreatorEdit) && (itemHit == kPrefsExtensionEdit || itemHit == kPrefsTypeEdit || itemHit == kPrefsCreatorEdit)) { SaveChanges(); UpdateSelection(); } GetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionEdit), lastExtension); GetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorEdit), lastCreator); GetIText(GetItemHandle(gPrefsDialog, kPrefsTypeEdit), lastType); lastSelStart = (*((DialogPeek)gPrefsDialog)->textH)->selStart; lastSelEnd = (*((DialogPeek)gPrefsDialog)->textH)->selEnd; lastItem = itemHit; } void UpdateButtons(void) { if (gPrefsList) { Cell theCell = { 0, 0 }; Boolean selected = LGetSelect(true, &theCell, gPrefsList); HiliteDControl(gPrefsDialog, kPrefsNewButton, 0); if (gEditing) SetCTitle((ControlHandle)GetItemHandle(gPrefsDialog, kPrefsEditButton), "\pDone"); else SetCTitle((ControlHandle)GetItemHandle(gPrefsDialog, kPrefsEditButton), "\pEdit"); HiliteDControl(gPrefsDialog, kPrefsEditButton, (selected || gEditing) ? 0 : 255); HiliteDControl(gPrefsDialog, kPrefsDeleteButton, selected ? 0 : 255); } else { HiliteDControl(gPrefsDialog, kPrefsNewButton, 255); HiliteDControl(gPrefsDialog, kPrefsEditButton, 255); HiliteDControl(gPrefsDialog, kPrefsDeleteButton, 255); } } void CloseUserDialog(DialogPtr theDialog) { Point thePoint; if (gPrefsDialog && theDialog == gPrefsDialog) { DisableItem(GetMHandle(kFileNum), 2); if (gEditing) SaveChanges(), StopEditing(); SaveResults(); if (gPrefsList) LDispose(gPrefsList), gPrefsList = nil; if (gTheGWorld) DisposeGWorld(gTheGWorld), gTheGWorld = nil; GetWindowPosition(gPrefsDialog, &thePoint); (*gPrefs)->prefsLoc = thePoint; SavePrefs(); DisposeDialog(gPrefsDialog); gPrefsDialog = nil; } } void UserDialogActivate(DialogPtr theDialog, Boolean nowActive) { ProcessSerialNumber thePSN, selfPSN = { 0, kCurrentProcess }; GetFrontProcess(&thePSN); Boolean inBackground; SameProcess(&selfPSN, &thePSN, &inBackground); inBackground = !inBackground; if (gPrefsDialog && theDialog == gPrefsDialog) { LActivate(nowActive && !inBackground, gPrefsList); } } void UpdateSelection(void) { short dataLen = 16, itemType; Cell theCell = { 0, 0 }; Handle itemHandle; Rect itemRect; char data[16]; if (!gPrefsList) return; SetPort(gPrefsDialog); if (LGetSelect(true, &theCell, gPrefsList)) { char string[5] = "\p "; LGetCell(data, &dataLen, theCell, gPrefsList); SetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionStatic), (StringPtr)data); *(long *)&string[1] = *(long *)&data[10]; SetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorStatic), (StringPtr)string); *(long *)&string[1] = *(long *)&data[6]; SetIText(GetItemHandle(gPrefsDialog, kPrefsTypeStatic), (StringPtr)string); if (FindIcon(*(long *)&data[10], *(long *)&data[6]) != noErr) EraseGWorld(gTheGWorld); SetIText(GetItemHandle(gPrefsDialog, kPrefsApplicationStatic), gApplicationName); } else { SetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionStatic), "\p"); SetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorStatic), "\p"); SetIText(GetItemHandle(gPrefsDialog, kPrefsTypeStatic), "\p"); SetIText(GetItemHandle(gPrefsDialog, kPrefsApplicationStatic), "\p"); EraseGWorld(gTheGWorld); } GetDItem(gPrefsDialog, kPrefsIconUser, &itemType, &itemHandle, &itemRect); InvalRect(&itemRect); GetDItem(gPrefsDialog, kPrefsListUser, &itemType, &itemHandle, &itemRect); } pascal void ListUserItem(DialogPtr theDialog, short theItem) { Handle itemHandle; short itemType; Rect itemRect; SetPort(theDialog); GetDItem(gPrefsDialog, theItem, &itemType, &itemHandle, &itemRect); if (gPrefsList) LUpdate(theDialog->visRgn, gPrefsList); InsetRect(&itemRect, -1, -1); itemRect.right--; FrameRect(&itemRect); } pascal void IconUserItem(DialogPtr theDialog, short theItem) { PixMapHandle thePixMap = GetGWorldPixMap(gTheGWorld); Handle itemHandle; short itemType; Rect itemRect; SetPort(theDialog); GetDItem(gPrefsDialog, theItem, &itemType, &itemHandle, &itemRect); if (gEditing) PenPat(&qd.black); else PenPat(&qd.white); FrameRect(&itemRect); InsetRect(&itemRect, 4, 4); PenPat(&qd.black); LockPixels(thePixMap); CopyBits((BitMap *)*thePixMap, &(*gPrefsList)->port->portBits, &gIconBounds, &itemRect, srcCopy, nil); UnlockPixels(thePixMap); } OSErr FindIcon(OSType creator, OSType type) { ParamBlockRec vol; DTPBRec dt; Str255 path; OSErr theErr; if (!gTheGWorld) return memFullErr; *gApplicationName = 0; vol.volumeParam.ioCompletion = nil; vol.volumeParam.ioVolIndex = 1; vol.volumeParam.ioNamePtr = path; while (true) { vol.volumeParam.ioVRefNum = 0; theErr = PBGetVInfoSync(&vol); if (theErr != noErr) break; dt.ioVRefNum = vol.volumeParam.ioVRefNum; dt.ioNamePtr = path; theErr = PBDTGetPath(&dt); if (theErr != noErr) continue; dt.ioCompletion = nil; dt.ioDTBuffer = gIconBuffer; dt.ioDTReqCount = 1024; dt.ioIconType = gIconType; dt.ioFileCreator = creator; dt.ioFileType = type; theErr = PBDTGetIconSync(&dt); if (theErr == noErr && gTheGWorld) { PixMapHandle thePixMap = GetGWorldPixMap(gTheGWorld); char *src, *dst; short row, size; LockPixels(thePixMap); src = gIconBuffer; dst = GetPixBaseAddr(thePixMap); size = 32 * (*thePixMap)->pixelSize / 8; for (row = 0; row < 32; row++) { BlockMove(src, dst, size); src += size; dst += (*thePixMap)->rowBytes & 0x3fff; } UnlockPixels(thePixMap); dt.ioNamePtr = gApplicationName; dt.ioIndex = 0; PBDTGetAPPLSync(&dt); return noErr; } vol.volumeParam.ioVolIndex++; } return theErr; } void EraseGWorld(GWorldPtr theGWorld) { GDHandle oldDevice; GWorldPtr oldPort; PixMapHandle thePixMap = GetGWorldPixMap(theGWorld); LockPixels(thePixMap); GetGWorld(&oldPort, &oldDevice); SetGWorld(gTheGWorld, nil); EraseRect(&gTheGWorld->portRect); SetGWorld(oldPort, oldDevice); UnlockPixels(thePixMap); } void StartEditing(void) { Handle itemHandle; Str255 theString; short itemType; Rect itemRect; GetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionStatic), theString); SetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionEdit), theString); GetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorStatic), theString); SetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorEdit), theString); GetIText(GetItemHandle(gPrefsDialog, kPrefsTypeStatic), theString); SetIText(GetItemHandle(gPrefsDialog, kPrefsTypeEdit), theString); HideDItem(gPrefsDialog, kPrefsExtensionStatic); ShowDItem(gPrefsDialog, kPrefsExtensionEdit); SelIText(gPrefsDialog, kPrefsExtensionEdit, 0, 32767); HideDItem(gPrefsDialog, kPrefsCreatorStatic); ShowDItem(gPrefsDialog, kPrefsCreatorEdit); HideDItem(gPrefsDialog, kPrefsTypeStatic); ShowDItem(gPrefsDialog, kPrefsTypeEdit); if (gDragTracker) ShowDItem(gPrefsDialog, kPrefsDragAndDropText); HiliteDControl(gPrefsDialog, kPrefsExampleButton, 0); gEditing = true; SetPort(gPrefsDialog); GetDItem(gPrefsDialog, kPrefsIconUser, &itemType, &itemHandle, &itemRect); InvalRect(&itemRect); UpdateButtons(); } void StopEditing(void) { Handle itemHandle; Str255 theString; short itemType; Rect itemRect; GetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionEdit), theString); SetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionStatic), theString); GetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorEdit), theString); SetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorStatic), theString); GetIText(GetItemHandle(gPrefsDialog, kPrefsTypeEdit), theString); SetIText(GetItemHandle(gPrefsDialog, kPrefsTypeStatic), theString); HideDItem(gPrefsDialog, kPrefsExtensionEdit); ShowDItem(gPrefsDialog, kPrefsExtensionStatic); HideDItem(gPrefsDialog, kPrefsCreatorEdit); ShowDItem(gPrefsDialog, kPrefsCreatorStatic); HideDItem(gPrefsDialog, kPrefsTypeEdit); ShowDItem(gPrefsDialog, kPrefsTypeStatic); HideDItem(gPrefsDialog, kPrefsDragAndDropText); HiliteDControl(gPrefsDialog, kPrefsExampleButton, 255); gEditing = false; SetPort(gPrefsDialog); GetDItem(gPrefsDialog, kPrefsIconUser, &itemType, &itemHandle, &itemRect); InvalRect(&itemRect); UpdateButtons(); } void SaveChanges(void) { Cell theCell = { 0, 0 }; Str255 theString; char data[16]; short i; GetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionEdit), theString); for (i = 1; i <= *theString; i++) if (theString[i] >= 'a' && theString[i] <= 'z') theString[i] += 'A' - 'a'; SetIText(GetItemHandle(gPrefsDialog, kPrefsExtensionEdit), theString); data[0] = (*theString <= 5) ? *theString : 5; *(long *)&data[1] = *(long *)&theString[1]; data[5] = theString[5]; GetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorEdit), theString); *(long *)&data[10] = *(long *)&theString[1]; GetIText(GetItemHandle(gPrefsDialog, kPrefsTypeEdit), theString); *(long *)&data[6] = *(long *)&theString[1]; LGetSelect(true, &theCell, gPrefsList); LSetCell(data, 16, theCell, gPrefsList); } void SaveResults(void) { if (gPrefsList) { short count = (*gPrefsList)->dataBounds.bottom - (*gPrefsList)->dataBounds.top; Handle theHandle = NewHandle(2 + count * 16); if (theHandle) { char data[16]; Cell theCell = { 0, 0 }; *(short *)*theHandle = count; for (theCell.v = 0; theCell.v < count; theCell.v++) { short len = 16; LGetCell(data, &len, theCell, gPrefsList); BlockMove(data, *theHandle + 2 + theCell.v * 16, 16); } DisposeHandle(gMapResource); gMapResource = theHandle; } else SysBeep(1); } } pascal OSErr DragTracker(DragTrackingMessage message, WindowPtr theWindow, void *handlerRefCon, DragReference theDragRef) { static Boolean hilited = false; HFSFlavor theFile; if (message == dragTrackingInWindow || message == dragTrackingLeaveWindow) if (IsValidDrag(theDragRef, &theFile)) { Handle itemHandle; GrafPtr oldPort; short itemType; Rect itemRect; Point where; GetDItem(gPrefsDialog, kPrefsIconUser, &itemType, &itemHandle, &itemRect); GetDragMouse(theDragRef, &where, nil); GetPort(&oldPort); SetPort(gPrefsDialog); GlobalToLocal(&where); SetPort(oldPort); if (PtInRect(where, &itemRect) && message == dragTrackingInWindow) { RgnHandle theRgn = NewRgn(); if (theRgn) { InsetRect(&itemRect, 1, 1); RectRgn(theRgn, &itemRect); ShowDragHilite(theDragRef, theRgn, true), hilited = true; DisposeRgn(theRgn); } } else if (hilited || message == dragTrackingLeaveWindow) HideDragHilite(theDragRef), hilited = false; } return noErr; } pascal OSErr DragReceiver(WindowPtr theWindow, void *handlerRefCon, DragReference theDragRef) { Boolean wasAliased, isFolder; HFSFlavor theFile; OSErr theErr; if (IsValidDrag(theDragRef, &theFile)) { Handle itemHandle; GrafPtr oldPort; short itemType; Rect itemRect; Point where; GetDItem(gPrefsDialog, kPrefsIconUser, &itemType, &itemHandle, &itemRect); GetDragMouse(theDragRef, &where, nil); GetPort(&oldPort); SetPort(gPrefsDialog); GlobalToLocal(&where); SetPort(oldPort); if (PtInRect(where, &itemRect)) { theErr = ResolveAliasFile(&theFile.fileSpec, true, &isFolder, &wasAliased); if (theErr == noErr && !isFolder) { SetExampleType(theFile.fileCreator, theFile.fileType); return noErr; } } } return dragNotAcceptedErr; } void SetExampleType(OSType creator, OSType type) { char string[5] = "\p "; *(long *)&string[1] = type; SetIText(GetItemHandle(gPrefsDialog, kPrefsTypeEdit), (StringPtr)string); *(long *)&string[1] = creator; SetIText(GetItemHandle(gPrefsDialog, kPrefsCreatorEdit), (StringPtr)string); SaveChanges(); UpdateSelection(); } Boolean IsValidDrag(DragReference theDragRef, HFSFlavor *theFile) { Size theSize = sizeof(HFSFlavor); ItemReference theItem; unsigned short count; OSErr theErr; if (!gEditing) return false; theErr = CountDragItems(theDragRef, &count); if (theErr == noErr && count == 1) { theErr = GetDragItemReferenceNumber(theDragRef, 1, &theItem); if (theErr == noErr) { theErr = GetFlavorData(theDragRef, theItem, flavorTypeHFS, theFile, &theSize, 0); if (theErr == noErr) return true; } } return false; } \ No newline at end of file diff --git a/DSDialogs.h b/DSDialogs.h new file mode 100644 index 0000000..c1cd4b1 --- /dev/null +++ b/DSDialogs.h @@ -0,0 +1 @@ +#pragma once #ifndef __DSDIALOGS__ #define __DSDIALOGS__ void OpenUserDialog(short menuID, short menuItem); void DoUserDialog(DialogPtr theDialog, short itemHit); void CloseUserDialog(DialogPtr theDialog); void UserDialogActivate(DialogPtr theDialog, Boolean nowActive); #endif \ No newline at end of file diff --git a/DSGlobals.h b/DSGlobals.h new file mode 100644 index 0000000..4af82cf --- /dev/null +++ b/DSGlobals.h @@ -0,0 +1 @@ +/****************************************************************************** ** ** Project Name: DropShell ** File Name: DSGlobals.h ** ** Description: Globals used by DropShell ** ******************************************************************************* ** A U T H O R I D E N T I T Y ******************************************************************************* ** ** Initials Name ** -------- ----------------------------------------------- ** LDR Leonard Rosenthol ** MTC Marshall Clow ** SCS Stephan Somogyi ** ******************************************************************************* ** R E V I S I O N H I S T O R Y ******************************************************************************* ** ** Date Time Author Description ** -------- ----- ------ --------------------------------------------- ** 12/09/91 LDR Added gSplashScreen ** 11/24/91 LDR Added some new #defs & a #inc for DSUtils ** 10/29/91 SCS Changes for THINK C 5 ** 10/28/91 LDR Officially renamed DropShell (from QuickShell) ** 10/06/91 00:02 MTC Converted to MPW C ** 04/09/91 00:03 LDR Added to Projector ** ******************************************************************************/ #pragma once #ifndef __DSGLOBALS_H__ #define __DSGLOBALS_H__ #ifndef THINK_C #include #include #include #include #include #include #include #include #include #endif #include #include #include #include #define kAppleNum 128 #define kFileNum 129 #define kErrStringID 100 #define kCantRunErr 1 #define kAEVTErr 2 extern Boolean gDone, gOApped, gHasAppleEvents, gWasEvent; extern EventRecord gEvent; extern MenuHandle gAppleMenu, gFileMenu; extern WindowPtr gSplashScreen; #endif __DSGLOBALS_H__ \ No newline at end of file diff --git a/DSUserProcs.c b/DSUserProcs.c new file mode 100644 index 0000000..2098619 --- /dev/null +++ b/DSUserProcs.c @@ -0,0 +1 @@ +/****************************************************************************** ** ** Project Name: DropShell ** File Name: DSUserProcs.c ** ** Description: Specific AppleEvent handlers used by the DropBox ** ******************************************************************************* ** A U T H O R I D E N T I T Y ******************************************************************************* ** ** Initials Name ** -------- ----------------------------------------------- ** LDR Leonard Rosenthol ** MTC Marshall Clow ** SCS Stephan Somogyi ** ******************************************************************************* ** R E V I S I O N H I S T O R Y ******************************************************************************* ** ** Date Time Author Description ** -------- ----- ------ --------------------------------------------- ** 01/25/92 LDR Removed the use of const on the userDataHandle ** 12/09/91 LDR Added the new SelectFile userProc ** Added the new Install & DisposeUserGlobals procs ** Modified PostFlight to only autoquit on odoc, not pdoc ** 11/24/91 LDR Added the userProcs for pdoc handler ** Cleaned up the placement of braces ** Added the passing of a userDataHandle ** 10/29/91 SCS Changes for THINK C 5 ** 10/28/91 LDR Officially renamed DropShell (from QuickShell) ** Added a bunch of comments for clarification ** 10/06/91 00:02 MTC Converted to MPW C ** 04/09/91 00:02 LDR Added to Projector ** ******************************************************************************/ #include "DSGlobals.h" #include "DSUserProcs.h" #include "DSDialogs.h" #include "DSUtilsASG.h" #include #include #include #include #include #include //================================================================================================================================== // Globals //================================================================================================================================== typedef struct odocQueue { struct odocQueue *next; FSSpec spec; Handle data; } odocQueue, *odocQueuePtr; enum { cacheData = 0x10, dontCache = 0x20 }; static Boolean gAborted = false; static char *gInputBuffer = nil, *gOutputBuffer = nil; static char *gInputBufferBase = nil, *gOutputBufferBase = nil; static char *gInputBufferPtr = nil, *gOutputBufferPtr = nil; static ParamBlockRec gInputPB1, gInputPB2; static ParamBlockRec gOutputPB1, gOutputPB2; static HParamBlockRec gCloseOutputPB; static HParamBlockRec gOpenOutputPB; static HParamBlockRec gDeleteInputPB; static short gInputRefNum = 0, gOutputRefNum = 0; static Size gInputBufferLeft = 0, gOutputBufferCount = 0; static Size gTotalSize = 0, gBytesRead = 0, gLinesProcessed = 0; static Boolean gLastInputBuffer = false, gFoundEndCode = false; static odocQueuePtr godocQueue = nil; static IOCompletionUPP gGenericCompletion = nil; Boolean gProcessing = false, gHasCQD = false; enum { kInputBufferSize = 32L * 1024L, kOutputBufferSize = 32L * 1024L, kExtensionMapType = 'E2Ty', kExtensionMapID = 0 }; static OSErr OpenInputFile(FSSpecPtr theSpec); static OSErr FillInputBuffer(void); static OSErr CloseInputFile(FSSpecPtr theSpec); static char *GetNextInputLine(Size *length, Boolean *badUU); static char *GetNextUUInputLine(void); static OSErr OpenOutputFile(FSSpecPtr inSpec, FSSpecPtr outSpec, char *outName); static OSErr CreateOutputFile(FSSpecPtr outSpec, char *outName, OSType creator, OSType type); static void MapOutputExtension(char *outname, OSType *creator, OSType *type); static OSErr FlushOutputBuffer(void); static OSErr CloseOutputFile(FSSpecPtr theSpec); static OSErr DecodeUULine(char *line); static void AddToQueue(FSSpec *theSpec, Handle userData); static void ProcessQueue(void); static void DisableMenus(void); static void EnableMenus(void); #if defined(powerc) || defined(__powerc) static pascal void GenericCompletion(ParmBlkPtr paramBlock); #else static pascal void GenericCompletion(void); #endif //================================================================================================================================== // This routine is called during init time. // // It allows you to install more AEVT Handlers beyond the standard four //================================================================================================================================== #pragma segment Main pascal void InstallOtherEvents(void) { } //================================================================================================================================== // This routine is called when an OAPP event is received. // // Currently, all it does is set the gOApped flag, so you know that // you were called initally with no docs, and therefore you shouldn't // quit when done processing any following odocs. //================================================================================================================================== #pragma segment Main pascal void OpenApp(void) { gOApped = true; } //================================================================================================================================== // This routine is called when an QUIT event is received. // We simply set the global done flag so that the main event loop can // gracefully exit. We DO NOT call ExitToShell for two reasons: // 1) It is a pretty ugly thing to do, but more importantly // 2) The Apple event manager will get REAL upset! //================================================================================================================================== #pragma segment Main pascal void QuitApp(void) { gDone = true; } //================================================================================================================================== // This routine is the first one called when an ODOC or PDOC event is received. // In this routine you would place code used to setup structures, etc. // which would be used in a 'for all docs' situation (like "Archive all // dropped files") // // Obviously, the opening boolean tells you whether you should be opening // or printing these files based on the type of event recieved. // // userDataHandle is a handle that you can create & use to store your own // data structs. This dataHandle will be passed around to the other // odoc/pdoc routines so that you can get at your data without using // globals - just like the new StandardFile. // // We also return a boolean to tell the caller if you support this type // of event. By default, our dropboxes don't support the pdoc, so when // opening is FALSE, we return FALSE to let the caller send back the // proper error code to the AEManager. //================================================================================================================================== #pragma segment Main pascal Boolean PreFlightDocs(Boolean opening, Handle *userDataHandle) { if (!opening) return false; if (gProcessing) return true; gAborted = false; ParamText("\p", "\p", "\p", "\p"); if (GenericProgress(codecProgressOpen, 0, 0) == codecAbortErr) return false; DisableMenus(); return true; // we support opening, but not printing - see above } //================================================================================================================================== // This routine is called for each file passed in the ODOC event. // // In this routine you would place code for processing each file/folder/disk that // was dropped on top of you. //================================================================================================================================== #pragma segment Main pascal void OpenDoc(FSSpecPtr myFSSPtr, Boolean opening, Handle userDataHandle) { FSSpec outFile; OSErr theErr; char *line; if (gAborted) return; if (gProcessing) { AddToQueue(myFSSPtr, userDataHandle); return; } ParamText("\p", "\p", "\p", "\p"); UpdateDialogItem(FrontWindow(), progressText2); GenericProgress(codecProgressForceUpdatePercent, 0, 0); theErr = OpenInputFile(myFSSPtr); if (theErr == noErr) { gLinesProcessed = 0; do { Size length; Boolean bad; line = GetNextInputLine(&length, &bad); if (!(++gLinesProcessed & 0x3f)) if (GenericProgress(codecProgressUpdatePercent, FixRatio((gBytesRead - gInputBufferLeft) >> 8, gTotalSize >> 8), 0) == codecAbortErr) { gAborted = true; line = nil; break; } } while (line && (line[0] != 'b' || line[1] != 'e' || line[2] != 'g' || line[3] != 'i' || line[4] != 'n' || line[5] != ' ')); if (line) { theErr = OpenOutputFile(myFSSPtr, &outFile, &line[10]); if (theErr == noErr) { do { line = GetNextUUInputLine(); if (line) theErr = DecodeUULine(line); } while (theErr == noErr && line); if (theErr != codecAbortErr) FlushOutputBuffer(); else gAborted = true; CloseOutputFile(&outFile); } GenericProgress(codecProgressForceUpdatePercent, 0x10000, 0); } CloseInputFile(myFSSPtr); ForceFolderUpdate(outFile.vRefNum, outFile.parID); } } //================================================================================================================================== // This routine is the last routine called as part of an ODOC event. // // In this routine you would place code to process any structures, etc. // that you setup in the PreflightDocs routine. // // If you created a userDataHandle in the PreFlightDocs routines, this is // the place to dispose of it since the Shell will NOT do it for you! //================================================================================================================================== #pragma segment Main pascal void PostFlightDocs(Boolean opening, Handle userDataHandle) { if (gProcessing) return; ProcessQueue(); GenericProgress(codecProgressClose, 0, 0); EnableMenus(); if (opening && !gOApped) gDone = true; } //================================================================================================================================== // This routine is called when the user chooses "Select FileÉ" from the // File Menu. // // Currently it simply calls the new StandardGetFile routine to have the // user select a single file (any type, numTypes = -1) and then calls the // SendODOCToSelf routine in order to process it. // // The reason we send an odoc to ourselves is two fold: 1) it keeps the code // cleaner as all file openings go through the same process, and 2) if events // are ever recordable, the right things happen (this is called Factoring!) // // Modification of this routine to only select certain types of files, selection // of multiple files, and/or handling of folder & disk selection is left // as an exercise to the reader. //================================================================================================================================== pascal void SelectFile(void) { StandardFileReply stdReply; SFTypeList theTypeList; UserDialogActivate(FrontWindow(), false); StandardGetFile(NULL, -1, theTypeList, &stdReply); UserDialogActivate(FrontWindow(), true); if (stdReply.sfGood) SendODOCToSelf(&stdReply.sfFile); } //================================================================================================================================== // This routine is called during the program's initialization and gives you // a chance to allocate or initialize any of your own globals that your // dropbox needs. // // You return a boolean value which determines if you were successful. // Returning false will cause DropShell to exit immediately. //================================================================================================================================== pascal Boolean InitUserGlobals(void) { long resp; if (Gestalt(gestaltQuickdrawVersion, &resp) == noErr && resp) gHasCQD = true; gInputBufferBase = gInputBuffer = (char *)NewPtr(kInputBufferSize * 2); if (!gInputBuffer) return false; gOutputBufferBase = gOutputBuffer = (char *)NewPtr(kOutputBufferSize * 2); if (!gOutputBuffer) return false; gInputPB1.ioParam.ioBuffer = gInputBufferBase + 256; gInputPB1.ioParam.ioReqCount = kInputBufferSize - 512; gInputPB1.ioParam.ioCompletion = gGenericCompletion; gInputPB1.ioParam.ioPosMode = fsAtMark + dontCache; gInputPB1.ioParam.ioPosOffset = 0; gInputPB2.ioParam.ioBuffer = gInputBufferBase + kInputBufferSize + 256; gInputPB2.ioParam.ioReqCount = kInputBufferSize - 512; gInputPB2.ioParam.ioCompletion = gGenericCompletion; gInputPB2.ioParam.ioPosMode = fsAtMark + dontCache; gInputPB2.ioParam.ioPosOffset = 0; gOutputPB1.ioParam.ioBuffer = gOutputBufferBase; gOutputPB1.ioParam.ioCompletion = gGenericCompletion; gOutputPB1.ioParam.ioPosMode = fsAtMark + dontCache; gOutputPB1.ioParam.ioPosOffset = 0; gOutputPB2.ioParam.ioBuffer = gOutputBufferBase + kOutputBufferSize; gOutputPB2.ioParam.ioCompletion = gGenericCompletion; gOutputPB2.ioParam.ioPosMode = fsAtMark + dontCache; gOutputPB2.ioParam.ioPosOffset = 0; gDeleteInputPB.ioParam.ioResult = 0; LoadPrefs(); return true; } //================================================================================================================================== // This routine is called during the program's cleanup and gives you // a chance to deallocate any of your own globals that you allocated // in the above routine. //================================================================================================================================== pascal void DisposeUserGlobals(void) { if (gInputBuffer) DisposePtr(gInputBuffer); if (gOutputBuffer) DisposePtr(gOutputBuffer); CloseUserDialog(FrontWindow()); SavePrefs(); } //================================================================================================================================== //================================================================================================================================== //================================================================================================================================== //================================================================================================================================== //================================================================================================================================== //================================================================================================================================== pascal void HandleFileMenu(short id) { if (id == 2) CloseUserDialog(FrontWindow()); else if (id == 4) OpenUserDialog(0, id); } OSErr OpenInputFile(FSSpecPtr theSpec) { static HParamBlockRec openPB; static ParamBlockRec eofPB; OSErr theErr; // set up the local parameter block to do an asynchronous open openPB.fileParam.ioCompletion = gGenericCompletion; openPB.fileParam.ioNamePtr = theSpec->name; openPB.fileParam.ioVRefNum = theSpec->vRefNum; openPB.ioParam.ioPermssn = fsRdPerm; openPB.fileParam.ioDirID = theSpec->parID; openPB.fileParam.ioResult = 1; theErr = PBHOpenDFAsync(&openPB); // give time to other applications as this is happening while (openPB.fileParam.ioResult == 1) GiveTime(); theErr = openPB.fileParam.ioResult; // save the resulting reference number in a global gInputPB1.ioParam.ioRefNum = gInputPB2.ioParam.ioRefNum = gInputRefNum = openPB.ioParam.ioRefNum; if (theErr == noErr) { // set up the other local parameter block to do an asychronous GetEOF eofPB.ioParam.ioCompletion = gGenericCompletion; eofPB.ioParam.ioRefNum = gInputRefNum; eofPB.ioParam.ioResult = 1; theErr = PBGetEOFAsync(&eofPB); // again, give up time to other apps while this happens while (eofPB.ioParam.ioResult == 1) GiveTime(); theErr = eofPB.ioParam.ioResult; if (theErr == noErr) { // if everything went ok, store the size in a global, and reset the input reading parameter blocks gTotalSize = (long)eofPB.ioParam.ioMisc; gBytesRead = gLinesProcessed = 0; gLastInputBuffer = gFoundEndCode = false; gInputBufferLeft = 0; gInputPB1.ioParam.ioResult = gInputPB2.ioParam.ioResult = 0; gInputPB1.ioParam.ioActCount = gInputPB2.ioParam.ioActCount = 0; // fill the two input buffers right away return FillInputBuffer(); } } return theErr; } OSErr FillInputBuffer(void) { Boolean buffer1 = (!gBytesRead || gInputBufferPtr < gInputBufferBase + kInputBufferSize); Boolean firstTime = !gBytesRead; static ParmBlkPtr pb, oppPB; OSErr theErr = noErr; // get pointers to the parameter block for this buffer and for the opposite buffer pb = (buffer1) ? &gInputPB1 : &gInputPB2; oppPB = (buffer1) ? &gInputPB2 : &gInputPB1; // copy remaining bytes in this buffer to beginning of next buffer if (gInputBufferLeft) BlockMove(gInputBufferPtr, oppPB->ioParam.ioBuffer - gInputBufferLeft, gInputBufferLeft); gInputBufferPtr = oppPB->ioParam.ioBuffer - gInputBufferLeft; // wait for next buffer to finish, if necessary while (oppPB->ioParam.ioResult == 1) GiveTime(); if (!firstTime && oppPB->ioParam.ioResult == eofErr && !oppPB->ioParam.ioActCount) gLastInputBuffer = true; // adjust the count of bytes left and bytes read gInputBufferLeft += oppPB->ioParam.ioActCount; gBytesRead += oppPB->ioParam.ioActCount; // now begin loading into the buffer we just skipped out of pb->ioParam.ioResult = 1; theErr = PBReadAsync(pb); if (theErr == eofErr) theErr = noErr; // if this is the first time reading, then we wait for this buffer to load, and load in a second buffer if (firstTime) { gInputBufferPtr = pb->ioParam.ioBuffer; while (pb->ioParam.ioResult == 1) GiveTime(); gInputBufferLeft = pb->ioParam.ioActCount; gBytesRead = pb->ioParam.ioActCount; oppPB->ioParam.ioResult = 1; theErr = PBReadAsync(oppPB); if (theErr == eofErr) theErr = noErr; } return theErr; } OSErr CloseInputFile(FSSpecPtr theSpec) { static Boolean first = true; static HParamBlockRec pb; static CMovePBRec cpb; OSErr theErr; if (!first) while (pb.ioParam.ioResult == 1) GiveTime(); else first = false; pb.ioParam.ioCompletion = gGenericCompletion; pb.ioParam.ioRefNum = gInputRefNum; pb.ioParam.ioResult = 1; theErr = PBCloseAsync((ParmBlkPtr)&pb); if (gFoundEndCode) { if ((*gPrefs)->trashSource == kTrashSource) { while (pb.ioParam.ioResult == 1) GiveTime(); FSSpec destSpec = *theSpec; theErr = FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder, &destSpec.vRefNum, &destSpec.parID); if (theErr == noErr) { short count = 1; Str63 oldName; BlockMove(theSpec->name, oldName, *theSpec->name + 1); do { cpb.ioCompletion = gGenericCompletion; cpb.ioNamePtr = theSpec->name; cpb.ioVRefNum = theSpec->vRefNum; cpb.ioNewName = nil; cpb.ioNewDirID = destSpec.parID; cpb.ioDirID = theSpec->parID; cpb.ioResult = 1; theErr = PBCatMoveAsync(&cpb); while (cpb.ioResult == 1) GiveTime(); if (cpb.ioResult == dupFNErr) { Str63 newName; if (*theSpec->name < 28) theSpec->name[*theSpec->name + 1] = 0; else theSpec->name[29] = 0; sprintf((char *)&newName[1], "%s %d", &oldName[1], ++count); *newName = strlen((char *)&newName[1]); pb.fileParam.ioCompletion = gGenericCompletion; pb.fileParam.ioNamePtr = theSpec->name; pb.fileParam.ioVRefNum = theSpec->vRefNum; pb.fileParam.ioDirID = theSpec->parID; pb.ioParam.ioMisc = (Ptr)newName; theErr = PBHRenameAsync(&pb); while (pb.ioParam.ioResult == 1) GiveTime(); BlockMove(newName, theSpec->name, *newName + 1); } } while (cpb.ioResult == dupFNErr); } } else if ((*gPrefs)->trashSource == kDeleteSource) { while (pb.ioParam.ioResult == 1) GiveTime(); pb.fileParam.ioCompletion = gGenericCompletion; pb.fileParam.ioNamePtr = theSpec->name; pb.fileParam.ioVRefNum = theSpec->vRefNum; pb.fileParam.ioDirID = theSpec->parID; theErr = PBHDeleteAsync(&pb); while (pb.ioParam.ioResult == 1) GiveTime(); } } return theErr; } char *GetNextInputLine(Size *length, Boolean *badUU) { OSErr theErr = noErr; if (gInputBufferLeft <= 0 && gLastInputBuffer) return nil; else if (gInputBufferLeft < 256) theErr = FillInputBuffer(); if (theErr == noErr) { register char *start = gInputBufferPtr; register char *p = gInputBufferPtr; register char *end = gInputBufferPtr + gInputBufferLeft; register char c; do { c = *p++; if (c == 0x0d || c == 0x0a) break; if (c < ' ' || c > '`') *badUU = true; } while (p < end); p[-1] = 0; *length = p - start - 1; do { c = *p++; } while (c == 0x0d && c == 0x0a && p < end); p--; gInputBufferPtr = p; gInputBufferLeft -= (p - start); return start; } return nil; } char *GetNextUUInputLine(void) { register char *line; Boolean bad = false; Size length; do { register int count, min, max; line = GetNextInputLine(&length, &bad); if (!line) return nil; if (line[0] == 'e' && line[1] == 'n' && line[2] == 'd' && line[3] == 0) { gFoundEndCode = true; return nil; } if (bad) { bad = false; continue; } count = (line[0] - ' '); if (count < 0 || count > 45) continue; min = ((count << 2) + 2) / 3; max = (((min + 3) >> 2) << 2) + 1; length--; if (length < min || length > max) continue; return line; } while (line); return nil; } OSErr DecodeUULine(char *line) { register int count = (*line++) - ' '; register int i; register char *src, *dst; register char c1, c2; OSErr theErr = noErr; if ((gOutputBufferCount + count + 256 + 3) > kOutputBufferSize) theErr = FlushOutputBuffer(); for (i = 0, src = line, dst = gOutputBufferPtr; i < count; i += 3) { c1 = (*src++ - ' ') & 0x3f; c2 = (*src++ - ' ') & 0x3f; *dst++ = ((c1 << 2) | (c2 >> 4)) & 0xff; c1 = (*src++ - ' ') & 0x3f; *dst++ = ((c2 << 4) | (c1 >> 2)) & 0xff; c2 = (*src++ - ' ') & 0x3f; *dst++ = ((c1 << 6) | c2) & 0xff; } gOutputBufferCount += count; gOutputBufferPtr = dst; if (!(++gLinesProcessed & 0x3f)) theErr = GenericProgress(codecProgressUpdatePercent, FixRatio((gBytesRead - gInputBufferLeft) >> 8, gTotalSize >> 8), 0); return theErr; } OSErr OpenOutputFile(FSSpecPtr inSpec, FSSpecPtr outSpec, char *outName) { register char *p = outName; register int count = 0; OSErr theErr = noErr; OSType creator, type; register char c; // first figure out the creator and type based on the output filename (assume the eol is already set up) MapOutputExtension(outName, &creator, &type); // now copy the filename into the FSSpec, limiting the length to 31 characters *outSpec = *inSpec; outSpec->name[31] = 0; do { c = *p++; } while (c && ++count < 32); p[-1] = 0; strncpy((char *)&outSpec->name[1], outName, 30); *outSpec->name = strlen((char *)&outSpec->name[1]); ParamText(outSpec->name, "\p", "\p", "\p"); UpdateDialogItem(FrontWindow(), progressText2); // do the actual creation, avoiding namespace conflicts theErr = CreateOutputFile(outSpec, outName, creator, type); // assuming everything else went okay, we finally open the file for writing if (theErr == noErr) { gOpenOutputPB.ioParam.ioCompletion = gGenericCompletion; gOpenOutputPB.fileParam.ioNamePtr = outSpec->name; gOpenOutputPB.fileParam.ioVRefNum = outSpec->vRefNum; gOpenOutputPB.ioParam.ioPermssn = fsRdWrPerm; gOpenOutputPB.fileParam.ioDirID = outSpec->parID; gOpenOutputPB.ioParam.ioResult = 1; theErr = PBHOpenDFAsync(&gOpenOutputPB); } // reset all the output-related variables, and return gOutputBufferPtr = gOutputBuffer; gOutputBufferCount = 0; gOutputPB1.ioParam.ioResult = gOutputPB2.ioParam.ioResult = 0; return theErr; } #define ASYNC_CREATE 1 OSErr CreateOutputFile(FSSpecPtr outSpec, char *outName, OSType creator, OSType type) { static HParamBlockRec createPB; static HParamBlockRec fInfoPB; register int count = 0; OSErr theErr = noErr; // here begins our loop to find a valid name for the new output file do { #if ASYNC_CREATE // now set up the local parameter block to do an asynchronous create createPB.ioParam.ioCompletion = gGenericCompletion; createPB.fileParam.ioNamePtr = outSpec->name; createPB.fileParam.ioVRefNum = outSpec->vRefNum; createPB.fileParam.ioDirID = outSpec->parID; createPB.ioParam.ioResult = 1; theErr = PBHCreateAsync(&createPB); // give time to other processes while this happens while (createPB.ioParam.ioResult == 1) GiveTime(); if (theErr == noErr) theErr = createPB.ioParam.ioResult; #else theErr = FSpCreate(outSpec, creator, type, 0); #endif // if a duplicate file name error occurred, bump up the last digit if (theErr == dupFNErr) { strncpy((char *)&outSpec->name[1], outName, 28); outSpec->name[29] = 0; *outSpec->name = strlen((char *)&outSpec->name[1]); outSpec->name[++*outSpec->name] = '.'; outSpec->name[++*outSpec->name] = '0' + count++; } } while (theErr == dupFNErr); #if ASYNC_CREATE // now get the default file information from the Finder if (theErr == noErr) { fInfoPB.ioParam.ioCompletion = gGenericCompletion; fInfoPB.fileParam.ioNamePtr = outSpec->name; fInfoPB.fileParam.ioVRefNum = outSpec->vRefNum; fInfoPB.fileParam.ioFDirIndex = 0; fInfoPB.fileParam.ioDirID = outSpec->parID; fInfoPB.ioParam.ioResult = 1; theErr = PBHGetFInfoAsync(&fInfoPB); } // give time to other processes while this happens while (fInfoPB.ioParam.ioResult == 1) GiveTime(); if (theErr == noErr) theErr = fInfoPB.ioParam.ioResult; // then set the type and creator for this file if (theErr == noErr) { fInfoPB.fileParam.ioFlFndrInfo.fdCreator = creator; fInfoPB.fileParam.ioFlFndrInfo.fdType = type; fInfoPB.fileParam.ioDirID = outSpec->parID; fInfoPB.ioParam.ioResult = 1; theErr = PBHSetFInfoAsync(&fInfoPB); } // give time to other processes while this happens while (fInfoPB.ioParam.ioResult == 1) GiveTime(); if (theErr == noErr) theErr = fInfoPB.ioParam.ioResult; #endif return theErr; } void MapOutputExtension(char *outName, OSType *creator, OSType *type) { *creator = '???\?'; *type = '???\?'; if (gMapResource) { int namelen = strlen(outName); char *p = *gMapResource; int count = *(short *)p, i; p += 2; while (count--) { int length = *p++; for (i = 0; i < length; i++) if (toupper(outName[namelen - length + i]) != p[i]) break; if (i == length) { *type = *(OSType *)&p[5]; *creator = *(OSType *)&p[9]; return; } p += 15; } } } OSErr FlushOutputBuffer(void) { Boolean buffer1 = (gOutputBufferPtr <= gOutputBufferBase + kOutputBufferSize); static ParmBlkPtr pb, oppPB; OSErr theErr = noErr; // point to the current parameter block and the opposing one pb = (buffer1) ? &gOutputPB1 : &gOutputPB2; oppPB = (buffer1) ? &gOutputPB2 : &gOutputPB1; // make sure that the asynchronous open of the file finished while (gOpenOutputPB.ioParam.ioResult == 1) GiveTime(); if (gOpenOutputPB.ioParam.ioResult != noErr) return gOpenOutputPB.ioParam.ioResult; gOutputRefNum = gOpenOutputPB.ioParam.ioRefNum; // now set up the parameter block for the write and do it pb->ioParam.ioRefNum = gOutputRefNum; pb->ioParam.ioReqCount = gOutputBufferCount; pb->ioParam.ioResult = 1; theErr = PBWriteAsync(pb); // reset the counts and the buffer pointers gOutputBufferCount = 0; gOutputBufferPtr = oppPB->ioParam.ioBuffer; // then make sure that the previous write from the next buffer is all done and return while (oppPB->ioParam.ioResult == 1) GiveTime(); if (oppPB->ioParam.ioResult != noErr) theErr = oppPB->ioParam.ioResult; return theErr; } OSErr CloseOutputFile(FSSpecPtr theSpec) { OSErr theErr; gCloseOutputPB.ioParam.ioCompletion = gGenericCompletion; gCloseOutputPB.ioParam.ioRefNum = gOutputRefNum; theErr = PBCloseAsync((ParmBlkPtr)&gCloseOutputPB); if (gAborted) { static Str255 name; BlockMove(theSpec->name, name, *theSpec->name + 1); while (gCloseOutputPB.ioParam.ioResult == 1) GiveTime(); gCloseOutputPB.fileParam.ioCompletion = gGenericCompletion; gCloseOutputPB.fileParam.ioNamePtr = name; gCloseOutputPB.fileParam.ioVRefNum = theSpec->vRefNum; gCloseOutputPB.fileParam.ioDirID = theSpec->parID; gCloseOutputPB.ioParam.ioResult = 1; theErr = PBHDeleteAsync(&gCloseOutputPB); } return theErr; } void AddToQueue(FSSpec *theSpec, Handle userData) { odocQueuePtr odoc = (odocQueuePtr)NewPtrClear(sizeof(odocQueue)), o; if (odoc) { if (godocQueue) { for (o = godocQueue; o->next; o = o->next); if (o) o->next = odoc; } else godocQueue = odoc; odoc->spec = *theSpec; odoc->data = userData; } } void ProcessQueue(void) { odocQueuePtr o; while (o = godocQueue) { OpenDoc(&o->spec, true, o->data); godocQueue = o->next; DisposePtr((Ptr)o); } } void DisableMenus(void) { MenuHandle theMenu = GetMHandle(kAppleNum); DisableItem(theMenu, 1); theMenu = GetMHandle(kFileNum); DisableItem(theMenu, 0); DrawMenuBar(); } void EnableMenus(void) { MenuHandle theMenu = GetMHandle(kAppleNum); EnableItem(theMenu, 1); theMenu = GetMHandle(kFileNum); EnableItem(theMenu, 0); DrawMenuBar(); } #if defined(powerc) || defined(__powerc) pascal void GenericCompletion(ParmBlkPtr paramBlock) #else pascal void GenericCompletion(void) #endif { } \ No newline at end of file diff --git a/DSUserProcs.h b/DSUserProcs.h new file mode 100644 index 0000000..0507654 --- /dev/null +++ b/DSUserProcs.h @@ -0,0 +1 @@ +/****************************************************************************** ** ** Project Name: DropShell ** File Name: DSUserProcs.h ** ** Description: Header w/prototypes for specific AppleEvent handlers ** used by the DropShell ** ******************************************************************************* ** A U T H O R I D E N T I T Y ******************************************************************************* ** ** Initials Name ** -------- ----------------------------------------------- ** LDR Leonard Rosenthol ** MTC Marshall Clow ** SCS Stephan Somogyi ** ******************************************************************************* ** R E V I S I O N H I S T O R Y ******************************************************************************* ** ** Date Time Author Description ** -------- ----- ------ --------------------------------------------- ** 01/25/92 LDR Removed the use of const on the userDataHandle ** 12/09/91 LDR Added SelectFile & UserGlobals prototypes ** 11/24/91 LDR Added new routines & changed ones ** 10/29/91 SCS Changes for THINK C 5 ** 10/28/91 LDR Officially renamed DropShell (from QuickShell) ** Added a bunch of comments for clarification ** 10/06/91 00:02 MTC Converted to MPW C ** 04/09/91 00:02 LDR Original Version ** ******************************************************************************/ #pragma once #ifndef __DSUSERPROCS_H__ #define __DSUSERPROCS_H__ #include "DSGlobals.h" #include "DSUtils.h" extern Boolean gProcessing, gHasCQD; pascal void InstallOtherEvents (void); pascal void OpenApp (void); pascal void QuitApp (void); pascal Boolean PreFlightDocs ( Boolean opening, Handle *userDataHandle ); pascal void OpenDoc ( FSSpecPtr myFSSPtr, Boolean opening, Handle userDataHandle ); pascal void PostFlightDocs ( Boolean opening, Handle userDataHandle ); pascal void SelectFile ( void ); pascal Boolean InitUserGlobals(void); pascal void DisposeUserGlobals(void); pascal void HandleFileMenu(short id); #endif __DSUSERPROCS_H__ \ No newline at end of file diff --git a/DSUtils.c b/DSUtils.c new file mode 100644 index 0000000..4bf6f46 --- /dev/null +++ b/DSUtils.c @@ -0,0 +1 @@ +/****************************************************************************** ** ** Project Name: DropShell ** File Name: DSUtils.c ** ** Description: Utility routines that may be useful to DropBoxes ** ******************************************************************************* ** A U T H O R I D E N T I T Y ******************************************************************************* ** ** Initials Name ** -------- ----------------------------------------------- ** LDR Leonard Rosenthol ** ******************************************************************************* ** R E V I S I O N H I S T O R Y ******************************************************************************* ** ** Date Time Author Description ** -------- ----- ------ --------------------------------------------- ** 12/09/91 LDR Added the Apple event routines ** 11/24/91 LDR Original Version ** ******************************************************************************/ #include "DSGlobals.h" #include "DSUtils.h" /* This routine is used to properly center an Alert before showing. It is per Human Interface specs by putting it in the top 1/3 of screen. NOTE: This same technique can be used with DLOG resources as well. */ #pragma segment Main void CenterAlert ( short theID ) { short theX, theY; AlertTHndl theAlertHandle; Rect screen, alrt; theAlertHandle = (AlertTHndl) GetResource ( 'ALRT', theID ); if ( theAlertHandle != NULL ) { HLock ((Handle) theAlertHandle ); alrt = (*theAlertHandle)->boundsRect; screen = qd.screenBits.bounds; theX = (( screen.right - screen.left ) - (alrt.right - alrt.left )) >> 1; theY = (( screen.bottom - screen.top ) + GetMBarHeight () - (alrt.bottom - alrt.top)) >> 1; theY -= ( screen.bottom - screen.top ) >> 2; /* this moves it up for better viewing! */ OffsetRect ( &(*theAlertHandle)->boundsRect, theX - alrt.left, theY - alrt.top ); } SetCursor ( &qd.arrow ); // change this for code resources! } /* This routine is just a quick & dirty error reporter */ #pragma segment Main void ErrorAlert ( short stringListID, short stringIndexID, short errorID ) { #define kAlertID 200 Str255 param, errorStr; NumToString ( errorID, errorStr ); GetIndString ( param, stringListID, stringIndexID ); ParamText ( param, errorStr, NULL, NULL ); CenterAlert ( kAlertID ); (void) Alert ( kAlertID, NULL ); } /*** These routines use the Process Manager to give you information about yourself ***/ #pragma segment Main void GetAppName(Str255 appName) { OSErr err; ProcessInfoRec info; ProcessSerialNumber curPSN; err = GetCurrentProcess(&curPSN); info.processInfoLength = sizeof(ProcessInfoRec); // ALWAYS USE sizeof! info.processName = appName; // so it returned somewhere info.processAppSpec = NULL; // I don't care! err = GetProcessInformation(&curPSN, &info); } #pragma segment Main void GetAppFSSpec(FSSpec *appSpec) { OSErr err; Str255 appName; ProcessInfoRec info; ProcessSerialNumber curPSN; err = GetCurrentProcess(&curPSN); info.processInfoLength = sizeof(ProcessInfoRec); // ALWAYS USE sizeof! info.processName = appName; // so it returned somewhere info.processAppSpec = appSpec; // so it can get returned! err = GetProcessInformation(&curPSN, &info); } /* ¥¥¥ Apple event routines begin here ¥¥¥ */ /* This routine will create a targetDesc for sending to self. We take IM VI's advice and use the typePSN form with kCurrentProcess as the targetPSN. */ OSErr GetTargetFromSelf (AEAddressDesc *targetDesc) { ProcessSerialNumber psn; psn.highLongOfPSN = 0; psn.lowLongOfPSN = kCurrentProcess; return( AECreateDesc(typeProcessSerialNumber, (Ptr)&psn, sizeof(ProcessSerialNumber), targetDesc) ); } /* This routine will create a targetDesc using the apps signature */ OSErr GetTargetFromSignature (OSType processSig, AEAddressDesc *targetDesc) { return( AECreateDesc(typeApplSignature, (Ptr)&processSig, sizeof(processSig), targetDesc) ); } /* This routine will create a targetDesc by bringing up the PPCBrowser */ OSErr GetTargetFromBrowser(Str255 promptStr, AEAddressDesc *targetDesc) { OSErr err; TargetID theTarget; PortInfoRec portInfo; err = PPCBrowser(promptStr, "\p", false, &theTarget.location, &portInfo, NULL, "\p"); if (err == noErr) { theTarget.name = portInfo.name; err = AECreateDesc(typeTargetID, (Ptr)&theTarget, sizeof(TargetID), targetDesc); } return( err ); } /* This routine is the low level routine used by the SendODOCToSelf routine. It gets passed the list of files (in an AEDescList) to be sent as the data for the 'odoc', builds up the event and sends off the event. It is broken out from SendODOCToSelf so that a SendODOCListToSelf could easily be written and it could then call this routine - but that is left as an exercise to the reader. Read the comments in the code for the order and details */ void _SendDocsToSelf (AEDescList *aliasList) { OSErr err; AEAddressDesc theTarget; AppleEvent openDocAE, replyAE; /* First we create the target for the event. We call another utility routine for creating the target. */ err = GetTargetFromSelf(&theTarget); if (err == noErr) { /* Next we create the Apple event that will later get sent. */ err = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments, &theTarget, kAutoGenerateReturnID, kAnyTransactionID, &openDocAE); if (err == noErr) { /* Now add the aliasDescList to the openDocAE */ err = AEPutParamDesc(&openDocAE, keyDirectObject, aliasList); if (err == noErr) { /* and finally send the event Since we are sending to ourselves, no need for reply. */ err = AESend(&openDocAE, &replyAE, kAENoReply + kAECanInteract, kAENormalPriority, 3600, NULL, NULL); /* NOTE: Since we are not requesting a reply, we do not need to need to dispose of the replyAE. It is there simply as a placeholder. */ } /* Dispose of the aliasList descriptor We do this instead of the caller since it needs to be done before disposing the AEVT */ err = AEDisposeDesc(aliasList); } /*and of course dispose of the openDoc AEVT itself*/ err = AEDisposeDesc(&openDocAE); } } /* This is the routine called by SelectFile to send a single odoc to ourselves. It calls the above low level routine to do the dirty work of sending the AEVT - all we do here is build a AEDescList of the file to be opened. */ void SendODOCToSelf (FSSpec *theFileSpec) { OSErr err; AEDescList aliasList; AEDesc aliasDesc; AliasHandle aliasH; /*Create the descList to hold the list of files*/ err = AECreateList(NULL, 0, false, &aliasList); if (err == noErr) { /* First we setup the type of descriptor */ aliasDesc.descriptorType = typeAlias; /* Now we add the file to descList by creating an alias and then adding it into the descList using AEPutDesc */ err = NewAlias(NULL, theFileSpec, &aliasH); aliasDesc.dataHandle = (Handle)aliasH; err = AEPutDesc(&aliasList, 0, &aliasDesc); DisposHandle((Handle)aliasH); /*Now call the real gut level routine to do the dirty work*/ _SendDocsToSelf(&aliasList); /*_SendDocsToSelf will dispose of aliasList for me*/ } } void SendQuitToSelf (void) { OSErr err; AEAddressDesc theTarget; AppleEvent quitAE, replyAE; /* First we create the target for the event. We call another utility routine for creating the target. */ err = GetTargetFromSelf(&theTarget); if (err == noErr) { /* Next we create the Apple event that will later get sent. */ err = AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &theTarget, kAutoGenerateReturnID, kAnyTransactionID, &quitAE); if (err == noErr) { /* and finally send the event Since we are sending to ourselves, no need for reply. */ err = AESend(&quitAE, &replyAE, kAENoReply + kAECanInteract, kAENormalPriority, 3600, NULL, NULL); /* NOTE: Since we are not requesting a reply, we do not need to need to dispose of the replyAE. It is there simply as a placeholder. */ } /* and of course dispose of the quit AEVT itself */ err = AEDisposeDesc(&quitAE); } } \ No newline at end of file diff --git a/DSUtils.h b/DSUtils.h new file mode 100644 index 0000000..9ccb9ea --- /dev/null +++ b/DSUtils.h @@ -0,0 +1 @@ +/****************************************************************************** ** ** Project Name: DropShell ** File Name: DSUtils.h ** ** Description: header w/protos for DSUtils ** ******************************************************************************* ** A U T H O R I D E N T I T Y ******************************************************************************* ** ** Initials Name ** -------- ----------------------------------------------- ** LDR Leonard Rosenthol ** ******************************************************************************* ** R E V I S I O N H I S T O R Y ******************************************************************************* ** ** Date Time Author Description ** -------- ----- ------ --------------------------------------------- ** 12/09/91 LDR Added protos for new routines ** 11/24/91 LDR original version ** ******************************************************************************/ #pragma once #ifndef __DSUTILS_H__ #define __DSUTILS_H__ #ifndef THINK_C #include #include #include #include #include #include #include #include #include #include #endif #include #include #include #include "DSGlobals.h" void CenterAlert ( short theID ); void ErrorAlert ( short stringListID, short stringIndexID, short errorID ); void GetAppName(Str255 appName); void GetAppFSSpec(FSSpec *appSpec); OSErr GetTargetFromSelf (AEAddressDesc *targetDesc); OSErr GetTargetFromSignature (OSType processSig, AEAddressDesc *targetDesc); OSErr GetTargetFromBrowser (Str255 promptStr, AEAddressDesc *targetDesc); void _SendDocsToSelf (AEDescList *aliasList); void SendODOCToSelf (FSSpec *theFileSpec); void SendQuitToSelf (void); #endif __DSUTILS_H__ \ No newline at end of file diff --git a/DSUtilsASG.c b/DSUtilsASG.c new file mode 100644 index 0000000..f1d8c35 --- /dev/null +++ b/DSUtilsASG.c @@ -0,0 +1 @@ +#include #include #include #include "DSDialogs.h" #include "DSUserProcs.h" #include "DSUtilsASG.h" PrefsHandle gPrefs = nil; Prefs gDefaultPrefs = { kVersion, false, false, { 0, 0 }, { 0, 0 } }; Handle gMapResource = nil; extern void DoMenu ( long retVal ); #define kPrefsFileName "\puuUndo Preferences" static Fixed gProgressPercent = 0; //===================================================================================== // pascal OSErr GenericProgress(short theMessage, Fixed thePercent, long refCon) //===================================================================================== // Generic progress procedure, which displays a dialog with a progress bar and a cancel // button. This function can be called as often as desired; the display is never // updated more than 6 times/second. //===================================================================================== pascal OSErr GenericProgress(short theMessage, Fixed thePercent, long refCon) { static UserItemUPP gUpdateBar = nil; static DialogPtr gTheDialog = nil; static long gLastTime; OSErr theErr = noErr; GrafPtr oldPort; Point thePoint; long ticks; GetPort(&oldPort); switch (theMessage) { case codecProgressOpen: if (gTheDialog = GetNewDialog(rProgressDialog, nil, (WindowPtr)-1)) { SetPort(gTheDialog); // set up the user item handler, either for indefinite progress // (thePercent == kIndefProgress) or for standard progress if (!gUpdateBar) gUpdateBar = NewUserItemProc((ProcPtr)UpdateBar); SetItemHandle(gTheDialog, progressUser, (Handle)gUpdateBar); SetWindowPosition(gTheDialog, (*gPrefs)->progressLoc); UserDialogActivate(FrontWindow(), false); ShowWindow(gTheDialog); DrawDialog(gTheDialog); } // reset the last time so that we draw right away, and update the initial // percentage gLastTime = 0; theErr = GenericProgress(codecProgressForceUpdatePercent, 0, refCon); break; case codecProgressForceUpdatePercent: gLastTime = 0; case codecProgressUpdatePercent: if (gTheDialog) { // update our user item no more than 3x a second; do this by setting the // gProgressPercent global, and forcing an update of the bar userItem if ((TickCount() - gLastTime) < 10L) break; gLastTime = TickCount(); gProgressPercent = thePercent; UpdateDialogItem(gTheDialog, progressUser); } break; case codecProgressClose: // display the final percentage and delay for an instant GenericProgress(codecProgressForceUpdatePercent, 0x10000, refCon); Delay(1, &ticks); GetWindowPosition(gTheDialog, &thePoint); (*gPrefs)->progressLoc = thePoint; if (gTheDialog) DisposeDialog(gTheDialog); gTheDialog = nil; UserDialogActivate(FrontWindow(), true); break; } // restore the original port and check for aborts SetPort(oldPort); if (theErr == noErr && theMessage != codecProgressForceUpdatePercent) theErr = (CheckAbort()) ? codecAbortErr : noErr; // if there was an abort signalled, and the dialog is visible, hide it immediately // to give better user feedback if (theErr == codecAbortErr && gTheDialog && ((WindowPeek)gTheDialog)->visible) HideWindow(gTheDialog); return theErr; } /* * CheckAbort() * * Purpose: Scans the event queue for an aborting keypress * Inputs: none * Returns: true if we should abort * */ Boolean CheckAbort() { static long gLastTime = 0; Boolean aborted = false, callWNE = false; EventRecord theEvent; GrafPtr oldPort; // if we're not in the background, ensure that we get at least 2 ticks of continuous // operation before giving up a full amount of time to others if ((TickCount() - gLastTime) >= 5) callWNE = true; GetPort(&oldPort); SetPort(FrontWindow()); // get the next event; if sleepTime is zero, check event availability first if (callWNE || EventAvail(everyEvent, &theEvent)) { // handle any pending events normally while (WaitNextEvent(everyEvent, &theEvent, 0, nil)) { // dispatch the event as appropriate switch (theEvent.what) { case mouseDown: aborted = HandleAbortMouseDown(&theEvent); break; case keyDown: case autoKey: aborted = HandleAbortKeyDown(&theEvent); break; case updateEvt: SetPort((WindowPtr)theEvent.message); BeginUpdate((WindowPtr)theEvent.message); if (((WindowPeek)theEvent.message)->windowKind == dialogKind) UpdtDialog((DialogPtr)theEvent.message, ((DialogPtr)theEvent.message)->visRgn); EndUpdate((WindowPtr)theEvent.message); break; case kHighLevelEvent: gProcessing = true; AEProcessAppleEvent(&theEvent); gProcessing = false; break; } } if (callWNE) gLastTime = TickCount(); } SetPort(oldPort); return aborted; } Boolean GiveTime() { EventRecord theEvent; GrafPtr oldPort; GetPort(&oldPort); SetPort(FrontWindow()); // handle any pending events normally while (WaitNextEvent(everyEvent & ~(mDownMask + keyDownMask + autoKeyMask), &theEvent, 0, nil)) { // dispatch the event as appropriate switch (theEvent.what) { case updateEvt: SetPort((WindowPtr)theEvent.message); BeginUpdate((WindowPtr)theEvent.message); if (((WindowPeek)theEvent.message)->windowKind == dialogKind) UpdtDialog((DialogPtr)theEvent.message, ((DialogPtr)theEvent.message)->visRgn); EndUpdate((WindowPtr)theEvent.message); break; case kHighLevelEvent: gProcessing = true; AEProcessAppleEvent(&theEvent); gProcessing = false; break; } } SetPort(oldPort); return true; } Boolean HandleAbortMouseDown(EventRecord *theEvent) { Boolean aborted = false; // handle dialog events properly; if button 3 was hit on the progress dialog, // we abort the drawing if (IsDialogEvent(theEvent)) { DialogPtr theDialog; short itemHit; SetCursor(&qd.arrow); if (DialogSelect(theEvent, &theDialog, &itemHit)) { if (GetWRefCon(theDialog) == kProgressDialogID && itemHit == progressCancel) return true; return false; } } // otherwise, do it the hard way.... else { WindowPtr theWindow; short thePart = FindWindow(theEvent->where, &theWindow); // track dragging and menus as normal; check specially for clicks in // the slide show controls floating window, and beep about everything else switch (thePart) { case inDrag: if (theWindow == FrontWindow()) DragWindow(theWindow, theEvent->where, &qd.screenBits.bounds); break; case inMenuBar: SetCursor(&qd.arrow); DoMenu ( MenuSelect ( theEvent->where )); break; default: SysBeep(1); break; } } return aborted; } Boolean HandleAbortKeyDown(EventRecord *theEvent) { char theChar = theEvent->message & charCodeMask; DialogPtr theDialog = FrontWindow(); Boolean aborted = false; if (GetWRefCon(theDialog) == kProgressDialogID) { // if escape, set the proper abort flag if (((theEvent->modifiers & cmdKey) && theChar == '.') || theChar == 0x1b) { long ticks; aborted = true; HiliteDControl(theDialog, progressCancel, 1); Delay(8, &ticks); HiliteDControl(theDialog, progressCancel, 0); } } return aborted; } //===================================================================================== // pascal void UpdateBar(DialogPtr theDialog, short theItem) //===================================================================================== // Draws the progress bar for the generic progress dialog, according to the percentage // stored in the global gProgressPercent. //===================================================================================== pascal void UpdateBar(DialogPtr theDialog, short theItem) { static RGBColor gBarBackground = { 0xcccc, 0xcccc, 0xffff }; static RGBColor gBarForeground = { 0x4000, 0x4000, 0x4000 }; static RGBColor gBlack = { 0x0000, 0x0000, 0x0000 }; static RGBColor gWhite = { 0xffff, 0xffff, 0xffff }; Boolean useBW = !(((CGrafPtr)theDialog)->portVersion & 0xc000) || (*((CGrafPtr)theDialog)->portPixMap)->pixelSize == 1; Rect itemRect, leftRect, rightRect; Handle itemHandle; GrafPtr oldPort; Fixed thePercent; short itemType; GetPort(&oldPort); SetPort(theDialog); thePercent = gProgressPercent; GetDItem(theDialog, theItem, &itemType, &itemHandle, &itemRect); PenSize(1, 1); if (useBW) PenPat(&qd.black); else RGBForeColor(&gBlack); FrameRect(&itemRect); InsetRect(&itemRect, 1, 1); rightRect = leftRect = itemRect; leftRect.right = itemRect.left + FixRound(FixMul(thePercent, Long2Fix(itemRect.right - itemRect.left))); rightRect.left = leftRect.right; if (leftRect.right > leftRect.left) { if (useBW) PenPat(&qd.black); else RGBForeColor(&gBarForeground); PaintRect(&leftRect); } if (rightRect.left < rightRect.right) { if (useBW) PenPat(&qd.white); else RGBForeColor(&gBarBackground); PaintRect(&rightRect); } if (useBW) PenPat(&qd.black); else RGBForeColor(&gBlack); SetPort(oldPort); } void SetItemHandle(DialogPtr theDialog, short theItem, Handle newHandle) { Handle itemHandle; short itemType; Rect itemRect; GetDItem(theDialog, theItem, &itemType, &itemHandle, &itemRect); SetDItem(theDialog, theItem, itemType, newHandle, &itemRect); } Handle GetItemHandle(DialogPtr theDialog, short theItem) { Handle itemHandle; short itemType; Rect itemRect; GetDItem(theDialog, theItem, &itemType, &itemHandle, &itemRect); return itemHandle; } void UpdateDialogItem(DialogPtr theDialog, short theItem) { Handle itemHandle; RgnHandle theRgn; short itemType; Rect itemRect; GrafPtr oldPort; if (theRgn = NewRgn()) { GetPort(&oldPort); SetPort(theDialog); GetDItem(theDialog, theItem, &itemType, &itemHandle, &itemRect); RectRgn(theRgn, &itemRect); UpdateDialog(theDialog, theRgn); SetPort(oldPort); DisposeRgn(theRgn); } } void HiliteDControl(DialogPtr theDialog, short theItem, short theVal) { Handle itemHandle; short itemType; Rect itemRect; GrafPtr oldPort; GetPort(&oldPort); SetPort(theDialog); GetDItem(theDialog, theItem, &itemType, &itemHandle, &itemRect); HiliteControl((ControlHandle)itemHandle, theVal); SetPort(oldPort); } //================================================================================================================================== // OSErr LoadPrefsHandle(OSType type, short index, Handle *theHandle) //================================================================================================================================== // Reads the specified resource from the Pathmaker preferences file. //================================================================================================================================== OSErr LoadPrefsHandle(OSType type, short index, Handle *theHandle) { return LoadHandleFromResource(GetPrefsSpec(), type, index, theHandle); } //================================================================================================================================== // OSErr SavePrefsHandle(OSType type, short index, Handle theHandle, short *id) //================================================================================================================================== // Saves the specified handle to the Pathmaker preferences file as a resource. //================================================================================================================================== OSErr SavePrefsHandle(OSType type, short index, Handle theHandle, short *id) { return SaveHandleAsResource(GetPrefsSpec(), 'pref', type, index, theHandle, id); } //================================================================================================================================== // OSErr LoadHandleFromResource(FSSpec *file, OSType type, short index, Handle *theHandle) // OSErr LoadHandleFromResource(short theFile, OSType type, short index, Handle *theHandle) //================================================================================================================================== // Reads the specified resource from the given file file and detaches it. //================================================================================================================================== OSErr LoadHandleFromResource(FSSpec *file, OSType type, short index, Handle *theHandle) { OSErr theErr = noErr; short theFile = FSpOpenResFile(file, fsRdPerm); if (theFile != -1) { theErr = LoadHandleFromResource(theFile, type, index, theHandle); CloseResFile(theFile); } else theErr = ResError(); return theErr; } OSErr LoadHandleFromResource(short theFile, OSType type, short index, Handle *theHandle) { short oldRes = CurResFile(); UseResFile(theFile); OSErr theErr = noErr; if (*theHandle = Get1Resource(type, index)) DetachResource(*theHandle); else theErr = fnfErr; // need to signal something since ResError() == 0 UseResFile(oldRes); return theErr; } //================================================================================================================================== // OSErr SaveHandleAsResource(FSSpec *file, OSType fileType, OSType type, short index, Handle *theHandle, short *id) // OSErr SaveHandleAsResource(short theFile, OSType type, short index, Handle theHandle, short *id) //================================================================================================================================== // Writes the specified handle to the given file as a resource. //================================================================================================================================== OSErr SaveHandleAsResource(FSSpec *file, OSType fileType, OSType type, short index, Handle theHandle, short *id) { OSErr theErr = noErr; FSpCreateResFile(file, kCreator, fileType, 0); short theFile = FSpOpenResFile(file, fsRdWrPerm); if (theFile != -1) { theErr = SaveHandleAsResource(theFile, type, index, theHandle, id); CloseResFile(theFile); } else theErr = ResError(); return theErr; } OSErr SaveHandleAsResource(short theFile, OSType type, short index, Handle theHandle, short *id) { short oldRes = CurResFile(); UseResFile(theFile); OSErr theErr = noErr; if (id) *id = index; Handle oldResource = Get1Resource(type, index); if (oldResource) { RmveResource(oldResource); theErr = ResError(); if (theErr == noErr) { DisposeHandle(oldResource); UpdateResFile(theFile); } else ReleaseResource(oldResource); } if (theErr == noErr) { AddResource(theHandle, type, index, (StringPtr)"\p"); theErr = ResError(); if (theErr == noErr) { WriteResource(theHandle); theErr = ResError(); if (theErr == noErr) { UpdateResFile(theFile); theErr = ResError(); } DetachResource(theHandle); } } UseResFile(oldRes); return theErr; } //================================================================================================================================== // FSSpec *GetPrefsSpec(void) //================================================================================================================================== // Returns an FSSpec pointing to our preferences file. //================================================================================================================================== FSSpec *GetPrefsSpec(void) { static FSSpec theSpec = { 0, 0, "\p" }; if (!*theSpec.name) { FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &theSpec.vRefNum, &theSpec.parID); FSMakeFSSpec(theSpec.vRefNum, theSpec.parID, kPrefsFileName, &theSpec); } return &theSpec; } void LoadPrefs(void) { Handle theHandle; OSErr theErr = LoadPrefsHandle('Pref', 0, &theHandle); if (theErr != noErr) { theErr = PtrToHand(&gDefaultPrefs, &theHandle, sizeof(Prefs)); theErr = SavePrefsHandle('Pref', 0, theHandle); } gPrefs = (PrefsHandle)theHandle; theErr = LoadPrefsHandle('E2Ty', 0, &theHandle); if (theErr != noErr) { theHandle = Get1Resource('E2Ty', 0); if (theHandle) { DetachResource(theHandle); theErr = SavePrefsHandle('E2Ty', 0, theHandle); } } gMapResource = theHandle; } void SavePrefs(void) { OSErr theErr = SavePrefsHandle('Pref', 0, (Handle)gPrefs); if (theErr == noErr) theErr = SavePrefsHandle('E2Ty', 0, gMapResource); } void SetWindowPosition(WindowPtr theWindow, Point thePosition) { Point centerPos; Rect monRect; if (gHasCQD) monRect = (*GetMainDevice())->gdRect; else monRect = qd.screenBits.bounds; centerPos.h = (monRect.right - monRect.left) - (theWindow->portRect.right - theWindow->portRect.left); centerPos.v = (monRect.bottom - monRect.top) - (theWindow->portRect.bottom - theWindow->portRect.top); centerPos.h /= 2; centerPos.v /= 3; centerPos.h += monRect.left; centerPos.v += monRect.top + 20; if (thePosition.h || thePosition.v) { Rect newRect = theWindow->portRect; RgnHandle theRgn; OffsetRect(&newRect, thePosition.h - newRect.left, thePosition.v - newRect.top); if (theRgn = NewRgn()) { SetRectRgn(theRgn, newRect.left + 4, newRect.top - 20 + 4, newRect.right - 4, newRect.top - 4); SectRgn(theRgn, GetGrayRgn(), theRgn); if (EmptyRgn(theRgn)) thePosition = centerPos; DisposeRgn(theRgn); } } else thePosition = centerPos; MoveWindow(theWindow, thePosition.h, thePosition.v, false); } void GetWindowPosition(WindowPtr theWindow, Point *thePosition) { GrafPtr oldPort; GetPort(&oldPort); SetPort(theWindow); thePosition->h = thePosition->v = 0; LocalToGlobal(thePosition); SetPort(oldPort); } void ForceFolderUpdate(long dirID, short vRefNum) { static CInfoPBRec block; block.hFileInfo.ioCompletion = nil; block.hFileInfo.ioNamePtr = nil; block.hFileInfo.ioVRefNum = vRefNum; block.hFileInfo.ioDirID = dirID; block.hFileInfo.ioFDirIndex = -1; PBGetCatInfoAsync(&block); while (block.hFileInfo.ioResult == 1) GiveTime(); GetDateTime(&block.dirInfo.ioDrMdDat); PBSetCatInfoAsync(&block); // while (block.hFileInfo.ioResult == 1) GiveTime(); } \ No newline at end of file diff --git a/DSUtilsASG.h b/DSUtilsASG.h new file mode 100644 index 0000000..3d8d0a3 --- /dev/null +++ b/DSUtilsASG.h @@ -0,0 +1 @@ +#pragma once #ifndef __DSUTILSASG__ #define __DSUTILSASG__ #if defined(powerc) || defined (__powerc) #pragma options align=mac68k #endif typedef struct Prefs { short version; char trashSource; Boolean makeComments; Point progressLoc; Point prefsLoc; } Prefs, *PrefsPtr, **PrefsHandle; #if defined(powerc) || defined(__powerc) #pragma options align=reset #endif extern PrefsHandle gPrefs; extern Handle gMapResource; enum { kCreator = 'uuUN', kVersion = 0x0100 }; enum { kDontTrashSource, kTrashSource, kDeleteSource }; enum { rProgressDialog = 137, kProgressDialogID = 'Jpgd', progressText1 = 1, progressUser, progressCancel, progressText2, codecProgressForceUpdatePercent = -125 }; extern pascal OSErr GenericProgress(short theMessage, Fixed thePercent, long refCon); extern Boolean CheckAbort(); extern Boolean GiveTime(); extern Boolean HandleAbortMouseDown(EventRecord *theEvent); extern Boolean HandleAbortKeyDown(EventRecord *theEvent); extern pascal void UpdateBar(DialogPtr theDialog, short theItem); extern void SetItemHandle(DialogPtr theDialog, short theItem, Handle newHandle); extern Handle GetItemHandle(DialogPtr theDialog, short theItem); extern void UpdateDialogItem(DialogPtr theDialog, short theItem); extern void HiliteDControl(DialogPtr theDialog, short theItem, short theVal); extern FSSpec *GetPrefsSpec(void); extern OSErr SaveHandleAsResource(short theFile, OSType type, short index, Handle theHandle, short *id); extern OSErr SaveHandleAsResource(FSSpec *file, OSType fileType, OSType type, short index, Handle theHandle, short *id); extern OSErr LoadHandleFromResource(short theFile, OSType type, short index, Handle *theHandle); extern OSErr LoadHandleFromResource(FSSpec *file, OSType type, short index, Handle *theHandle); extern OSErr SavePrefsHandle(OSType type, short index, Handle theHandle, short *id = nil); extern OSErr LoadPrefsHandle(OSType type, short index, Handle *theHandle); extern void LoadPrefs(void); extern void SavePrefs(void); extern void SetWindowPosition(WindowPtr theWindow, Point thePosition); extern void GetWindowPosition(WindowPtr theWindow, Point *thePosition); extern void ForceFolderUpdate(long dirID, short vRefNum); #endif \ No newline at end of file diff --git a/DropShell.c b/DropShell.c new file mode 100644 index 0000000..d68cd6a --- /dev/null +++ b/DropShell.c @@ -0,0 +1 @@ +/****************************************************************************** ** ** Project Name: DropShell ** File Name: DropShell.c ** ** Description: Main application code for the QuickShell ** ******************************************************************************* ** A U T H O R I D E N T I T Y ******************************************************************************* ** ** Initials Name ** -------- ----------------------------------------------- ** LDR Leonard Rosenthol ** MTC Marshall Clow ** SCS Stephan Somogyi ** ******************************************************************************* ** R E V I S I O N H I S T O R Y ******************************************************************************* ** ** Date Time Author Description ** -------- ----- ------ --------------------------------------------- ** 12/09/91 LDR Added support for new "Select FileÉ" menu item ** Quit now sends AEVT to self to be politically correct ** Added support for the new gSplashScreen ** 11/24/91 LDR Added support for the Apple Menu (duh!) ** 10/29/91 SCS Changes for THINK C 5 ** 10/28/91 LDR Officially renamed DropShell (from QuickShell) ** Added a bunch of comments for clarification ** 10/06/91 00:02 MTC Converted to MPW C ** 04/09/91 00:02 LDR Added to Projector ** ******************************************************************************/ #include #include "DSGlobals.h" #include "DSUserProcs.h" #include "DSAppleEvents.h" #include "DSDialogs.h" #include "DropShell.h" #ifndef THINK_C #include #include #include #include #endif Boolean gDone, gOApped, gHasAppleEvents, gWasEvent; EventRecord gEvent; MenuHandle gAppleMenu, gFileMenu; WindowPtr gSplashScreen; extern void _DataInit(); /* Simple routine installed by InitDialogs for the Resume button in Bomb boxes */ #pragma segment Main void Panic () { ExitToShell (); } #pragma segment Initialize void InitToolbox (void) { #ifdef applec UnloadSeg ((Ptr) _DataInit ); #endif InitGraf ( &qd.thePort ); InitFonts (); InitWindows (); InitMenus (); TEInit (); InitDialogs (nil); InitCursor (); FlushEvents ( everyEvent, 0 ); // how about some memory fun! Two should be enough! MoreMasters (); MoreMasters (); } /* Let's setup those global variables that the DropShell uses. If you add any globals for your own use, init them in the InitUserGlobals routine in DSUserProcs.c */ #pragma segment Initialize Boolean InitGlobals (void) { long aLong; gDone = false; gOApped = false; // probably not since users are supposed to DROP things! gHasAppleEvents = Gestalt ( gestaltAppleEventsAttr, &aLong ) == noErr; gSplashScreen = NULL; return(InitUserGlobals()); // call the user proc } /* Again, nothing fancy. Just setting up the menus. If you add any menus to your DropBox - insert them here! */ #pragma segment Initialize void SetUpMenus (void) { gAppleMenu = GetMenu ( kAppleNum ); AddResMenu ( gAppleMenu, 'DRVR' ); InsertMenu ( gAppleMenu, 0 ); gFileMenu = GetMenu ( kFileNum ); InsertMenu ( gFileMenu, 0 ); DrawMenuBar (); } /* This routine is called during startup to display a splash screen. This was recommend by the Blue Team HI person, John Sullivan, who feels that all apps should display something so that users can easily tell what is running, and be able to switch by clicking. Thanks John! */ #pragma segment Initialize void InstallSplashScreen(void) { #define windowPicID 128 PicHandle picH; if (!gSplashScreen) { // show the splash screen window picH = GetPicture(windowPicID); if (picH) { gSplashScreen = GetNewWindow(windowPicID, NULL, (WindowPtr)-1L); if (gSplashScreen) { SetWindowPic(gSplashScreen, picH); // Don't show it here, since we only want to it for oapp launches! // ShowWindow(gSplashScreen); } } } } /* --------------- Standard Event Handling routines ---------------------- */ #pragma segment Main void ShowAbout () { UserDialogActivate(FrontWindow(), false); (void) Alert ( 128, NULL ); UserDialogActivate(FrontWindow(), true); } #pragma segment Main void DoMenu ( long retVal ) { short menuID, itemID; Str255 itemStr; menuID = HiWord ( retVal ); itemID = LoWord ( retVal ); switch ( menuID ) { case kAppleNum: if ( itemID == 1 ) ShowAbout (); /* Show the about box */ else { GetItem(GetMHandle(kAppleNum), itemID, itemStr); OpenDeskAcc(itemStr); } break; case kFileNum: if ( itemID == 1 ) SelectFile(); // call file selection userProc else if (itemID == 6) SendQuitToSelf(); // send self a 'quit' event else HandleFileMenu(itemID); break; default: break; } HiliteMenu(0); // turn it off! } #pragma segment Main void DoMouseDown ( EventRecord *curEvent ) { WindowPtr whichWindow; short whichPart; whichPart = FindWindow ( curEvent->where, &whichWindow ); switch ( whichPart ) { case inMenuBar: DoMenu ( MenuSelect ( curEvent->where )); break; case inSysWindow: SystemClick ( curEvent, whichWindow ); break; case inDrag: #ifndef THINK_C DragWindow ( whichWindow, curEvent->where, &qd.screenBits.bounds ); #else DragWindow ( whichWindow, curEvent->where, &screenBits.bounds ); #endif case inGoAway: if (TrackGoAway(whichWindow, curEvent->where)) CloseUserDialog(whichWindow); break; default: break; } } #pragma segment Main void DoKeyDown ( EventRecord *curEvent ) { if ( curEvent->modifiers & cmdKey ) DoMenu ( MenuKey ((char) curEvent->message & charCodeMask )); } #pragma segment Main void main ( ) { InitToolbox (); #if __profile__ ProfilerInit(collectDetailed, microsecondsTimeBase, 0, 0); ProfilerSetStatus(true); #endif if ( InitGlobals () ) { // if we succeeding in initting self if ( !gHasAppleEvents ) ErrorAlert ( kErrStringID, kCantRunErr, 0 ); else { InitAEVTStuff (); SetUpMenus (); //InstallSplashScreen (); while ( !gDone ) { gWasEvent = WaitNextEvent ( everyEvent, &gEvent, 60, NULL ); if (IsDialogEvent(&gEvent) && (gEvent.what != keyDown || !(gEvent.modifiers & cmdKey))) { short itemHit; DialogPtr theDialog; if (DialogSelect(&gEvent, &theDialog, &itemHit)) DoUserDialog(theDialog, itemHit); if (gEvent.what == activateEvt) UserDialogActivate((WindowPtr)gEvent.message, gEvent.modifiers & activeFlag); else if (gEvent.what == osEvt && (gEvent.message >> 24) == suspendResumeMessage) UserDialogActivate((WindowPtr)FrontWindow(), gEvent.message & resumeFlag); } else if ( gWasEvent ) { switch ( gEvent.what ) { case kHighLevelEvent: DoHighLevelEvent ( &gEvent ); break; case mouseDown: DoMouseDown ( &gEvent ); break; case keyDown: case autoKey: DoKeyDown ( &gEvent ); break; case osEvt: SetCursor(&qd.arrow); break; default: break; } } } } DisposeUserGlobals(); // call the userproc to clean itself up } #if __profile__ ProfilerTerm(); ProfilerDump("\pProfile.out"); #endif } \ No newline at end of file diff --git a/DropShell.h b/DropShell.h new file mode 100644 index 0000000..1d90152 --- /dev/null +++ b/DropShell.h @@ -0,0 +1 @@ +#pragma once void Panic(void); void InitToolbox(void); Boolean InitGlobals(void); void SetUpMenus(void); void InstallSplashScreen(void); void ShowAbout(void); void DoMenu(long retVal); void DoMouseDown(EventRecord *curEvent); void DoKeyDown(EventRecord *curEvent); \ No newline at end of file diff --git a/DropShell.make b/DropShell.make new file mode 100644 index 0000000..c2c917f --- /dev/null +++ b/DropShell.make @@ -0,0 +1 @@ +##***************************************************************************** ## ## Project Name: DropShell ## File Name: DropShell.make ## ## Description: Makefile for DropShell ## This makefile was created with & must be built by ## MPW's Build Menu. Simply use the BuildÉ option, ## specifying DropShell as the thing to build. ## You can also use the BuildProgram command. ## ##***************************************************************************** ## A U T H O R I D E N T I T Y ##***************************************************************************** ## ## Initials Name ## -------- ----------------------------------------------- ## LDR Leonard Rosenthol ## ##***************************************************************************** ## R E V I S I O N H I S T O R Y ##***************************************************************************** ## ## Date Time Author Description ## -------- ----- ------ --------------------------------------------- ## 11/24/91 LDR Cleaned it up a bit using some more vars ## Added the DSUtils stuff ## 10/28/91 LDR Modified for the C version of DropShell ## 10/28/91 LDR Officially renamed DropShell (from QuickShell) ## And added some comments ## 04/08/91 23:57 LDR Original Version ## ##***************************************************************************** # File: DropShell.make # Target: DropShell # Sources: DSGlobals.h # DSAppleEvents.h # DropShell.h # DSAppleEvents.c # DropShell.c # DropShell.r # DSUserProcs.c # DSUtils.c ObjectDir = ":Objects:" LIBS = ¶ "{Libraries}"Runtime.o ¶ "{Libraries}"Interface.o ¶ "{CLibraries}"CSANELib.o ¶ "{CLibraries}"CInterface.o OBJECTS = ¶ {ObjectDir}DSUtils.c.o ¶ {ObjectDir}DSUserProcs.c.o ¶ {ObjectDir}DSAppleEvents.c.o ¶ {ObjectDir}DropShell.c.o "{ObjectDir}"DSUtils.c.o Ä DropShell.make DSUtils.c DSUtils.h DSGlobals.h C -o {ObjectDir} DSUtils.c "{ObjectDir}"DSUserProcs.c.o Ä DropShell.make DSUserProcs.c DSUserProcs.h DSGlobals.h C -o {ObjectDir} DSUserProcs.c "{ObjectDir}"DSAppleEvents.c.o Ä DropShell.make DSAppleEvents.c DSAppleEvents.h DSGlobals.h C -o {ObjectDir} DSAppleEvents.c "{ObjectDir}"DropShell.c.o Ä DropShell.make DropShell.c DSAppleEvents.c DSAppleEvents.h DSGlobals.h C -o {ObjectDir} DropShell.c DropShell ÄÄ DropShell.make {OBJECTS} Link -w -t APPL -c '????' ¶ {OBJECTS} {LIBS} ¶ -o DropShell DropShell ÄÄ DropShell.make DropShell.r Rez DropShell.r -append -o DropShell \ No newline at end of file diff --git a/DropShell.r b/DropShell.r new file mode 100644 index 0000000..831b8bb --- /dev/null +++ b/DropShell.r @@ -0,0 +1 @@ +/****************************************************************************** ** ** Project Name: DropShell ** File Name: DropShell.r ** ** Description: Resource definitions for DropShell ** ** Since this is a shell, there are a LOT of places where the ** specifics of the dropbox (and it's name) need to be placed. ** Currently a set of question marks (???) are there. Use Search ** & Replace to replace them with your specific functionality. ** ******************************************************************************* ** A U T H O R I D E N T I T Y ******************************************************************************* ** ** Initials Name ** -------- ----------------------------------------------- ** LDR Leonard Rosenthol ** ******************************************************************************* ** R E V I S I O N H I S T O R Y ******************************************************************************* ** ** Date Time Author Description ** -------- ----- ------ --------------------------------------------- ** 12/10/91 LDR Added an 'aete' for scripting support ** since it needs special #include, conditionaled it ** 12/09/91 LDR Added new Select FileÉ menu item in File menu ** and associated help resources & comments ** 11/24/91 LDR Added some new comments about Balloon resources ** Added new string for AEVT errors ** 10/28/91 LDR Officially renamed DropShell (from QuickShell) ** And added some comments ** 04/09/91 00:00 LDR Original Version ** ******************************************************************************/ /* We must do this define in order to get some extra flags! */ #define SystemSevenOrLater 1 #include "Types.r" #include "SysTypes.r" #include "BalloonTypes.r" /* comment the next line to NOT build the AETE */ #define BuildAETE 1 #ifdef BuildAETE #include "AEUserTermTypes.r" #endif /* First thing we need is the Finder Help Balloon */ /* Change the ??? to whatever your dropbox does (or completely change it!) */ resource 'hfdr' (-5696) { HelpMgrVersion, 0, 0, 0, { HMStringItem { "Drag any file onto this icon, and I will magically ???", }, } }; /* Splash Screen Goes Here */ /* This is displayed all the time the program is running, so you do NOT want to give it a close box. Apple's Blue HIG folks recommend having this so that users can easily see what is running. It is recommended that you put some about info, as well as simply instructions about using the product. */ resource 'WIND' (128) { {79,171,104,470}, movableDBoxProc, visible, nogoAway, 0x0, "DropShell Splash Screen", centerMainScreen }; resource 'PICT' (128, "Splash") { 972, {0,0,25,299}, $"1101 A030 39A0 0082 0100 0A00 0000 0000" $"1901 3099 0026 0000 0000 0019 0130 0000" $"0000 0019 012B 0000 0000 0019 012B 0000" $"000A 0000 0000 0019 012B 1602 FFFF C0FB" $"0001 3FFC F800 0201 FFFE FB00 01FF E0FA" $"001D 02FF FFC0 FB00 017F FEFD 0001 01FC" $"FE00 0401 FFFF 01F8 FD00 01FF F8FA 0026" $"FEFF 228F EFC3 FF80 00FF FEFF E007 8007" $"FF1F FF00 01FF FF07 FE03 FC00 00FF FC3F" $"FE7F 03F7 FFE0 2725 5FFF FF8F EFC7 FFC0" $"00FF FEFF F00F C00F FF9F FF80 01FF FF1F" $"FF87 FF00 00FF FE3F FF7F 03F7 FFE0 2725" $"5FFD DF87 EFC7 FFC0 01FF FEFF F80F C01F" $"FFDF FF80 00FE 7F9F FFC7 FF80 00FF FE3F" $"FF7F 83E6 FFE0 2725 5FF0 9F03 EFCF FFC0" $"01FC 6E7F FC1F C03F FFCF 9F80 00FC 63BF" $"EFC7 FF80 00FF FF1F 3F3F 83E2 FF60 2725" $"03F0 9F03 E7CF CDC0 00FC 4E7F FE1F E03E" $"2BCF 0980 00F8 033F EFE7 FFC0 007E FF9E" $"133F C3C2 FE00 2725 03F0 1F03 E78F C4C0" $"00FC 467C BE3F E07C 2BEF 0180 00F8 007C" $"6CE3 CFC0 007C CF9E 033F E3C0 9E00 2725" $"03F0 1F03 E787 E4C0 007E 067C 1E3C F078" $"02EF 8000 007C 0078 00F3 CDE0 007C 0F9F" $"003F E3C0 1E00 2725 03E0 0F03 E787 F0C0" $"003F 003C 1E7C 7078 0067 FE00 007F FC78" $"00F3 C0E0 007C 0F8F FC3F E3C0 1C00 2725" $"01E0 0E01 E783 F800 001F 803C 1E78 7870" $"0027 FE00 007F FC70 0073 E0C0 007C 0F0F" $"FC3F F3C0 1C00 2725 01E0 0F01 E7C1 FC00" $"000F E01C 3EF0 3870 0003 C400 003E 1870" $"0071 E0C0 003C 0F07 883C F3C0 1C00 2725" $"01E0 0FFF E3C0 FE00 0007 F03C 7AF0 1C70" $"0007 8400 003C 1870 0033 E180 007C 1E0F" $"081E 7380 1C00 2725 01E0 0FFF C380 7F00" $"0007 F83F F0FF FC30 0003 8400 007C 1030" $"0033 E280 007C 7A07 081E 3B80 3C00 2725" $"01E0 0F6B 8380 7F00 0003 781F D0FF FC38" $"0003 8000 003C 0030 0021 FC40 003F F207" $"001E 3F80 3C00 2725 01E0 0701 8384 2F80" $"00C2 3C1E 50FF 7C18 0003 8000 003C 0018" $"0061 FE00 003F F207 001E 1F80 3C00 2725" $"01E0 0703 8306 0780 00E0 3C1C 00E0 1C1C" $"00C1 8600 003C 001C 0061 8F00 0038 F003" $"0C1E 0F80 3C00 2725 01E0 0601 8307 0780" $"0070 3C18 00E0 1C1F 0381 FC00 001C 000F" $"01A1 8F00 0038 7803 F816 0380 0C00 2725" $"01E0 0401 8303 8F00 0078 7818 00E0 0C0F" $"FF01 FC00 001E 000F FF21 8380 0018 7803" $"F816 0300 0C00 2725 00E0 0601 8101 FE00" $"003F F018 00E0 0809 F901 E400 001E 0009" $"FF01 C080 0018 7C03 C806 0300 1800 2719" $"00E0 0401 8100 F800 001F E010 00C0 0800" $"F000 8000 000E 0000 7C01 FE00 081C 0C01" $"0006 0000 1000 2410 00C0 0000 8000 5000" $"000F C010 0040 0800 60FD 0004 0E00 0018" $"01FE 0004 1C06 0000 02FD 0016 0100 C0FD" $"0004 4000 0005 80F7 0000 0CFA 0001 1804" $"FA00 1401 0080 FD00 0340 0000 04F6 0000" $"04FA 0000 10F9 0006 F800 0004 E500 A000" $"83FF" }; /* Menus & Menu Help Resources Go Here */ /* You will probably want to put the application name here! */ resource 'MENU' (128, "Apple") { 128, textMenuProc, 0x7FFFFFFD, enabled, apple, { /* array: 2 elements */ /* [1] */ "About ???É", noIcon, noKey, noMark, plain, /* [2] */ "-", noIcon, noKey, noMark, plain } }; /* You'll probably want to replace Select File with something particular about your app */ resource 'MENU' (129, "File") { 129, textMenuProc, allEnabled, enabled, "File", { /* array: 2 elements */ /* [1] */ "Select FileÉ", noIcon, "O", noMark, plain, /* [2] */ "Quit", noIcon, "Q", noMark, plain } }; /* These are the help resources for the menus. You should not need to change these unless you add or delete menu items. If you wish to change the text that is displayed, see the STR# with the same ID */ resource 'hmnu' (128, purgeable) { /* Apple */ HelpMgrVersion, 0, 0, 0, HMSkipItem { /* no missing items */ }, { /* array HMenuArray: 2 elements */ HMSkipItem { /* skip the menu title */ }, HMStringResItem { /* About Item */ 128, 1, 128, 1, 0, 0, 0, 0 } } }; resource 'hmnu' (129, purgeable) { /* File */ HelpMgrVersion, 0, 0, 0, HMSkipItem { /* no missing items */ }, { /* array HMenuArray: 4 elements */ HMStringResItem { /* Menu Title */ 129, 1, 129, 2, 129, 3, 129, 4 }, HMStringResItem { /* Open FileÉ */ 129, 5, 129, 6, 0, 0, 0, 0 }, HMStringResItem { /* Quit */ 129, 7, 129, 8, 0, 0, 0, 0 }, } }; /* How about the About box? */ /* Of course, you will modify this for your application */ resource 'ALRT' (128, purgeable) { {75, 79, 198, 354}, 128, { /* array: 4 elements */ /* [1] */ OK, visible, silent, /* [2] */ OK, visible, silent, /* [3] */ OK, visible, silent, /* [4] */ OK, visible, silent }, alertPositionMainScreen }; resource 'DITL' (128, purgeable) { { /* array DITLarray: 3 elements */ /* [1] */ {97, 198, 117, 270}, Button { enabled, "OK" }, /* [2] */ {8, 8, 87, 271}, StaticText { disabled, "DropShell - the simplest program possible with the DS Technology!" }, /* [3] */ {0, 0, 0, 0}, HelpItem { disabled, HMScanhdlg { 1 } } } }; /* This is the help resource for the about box. Assuming you change the contents of the about box, you will NEED to change this resource. I suggest that you consult the Help Manager chapter of IM VI for an explanation of how these resources work. */ resource 'hdlg' (128, purgeable) { HelpMgrVersion, 0, hmSaveBitsNoWindow, 0, 3, /* need bits saved in Alert, since the OK ring & icons are not updated */ HMSkipItem { /* no missing items */ }, { HMStringResItem { /* OK button */ {0,0}, /* default tip */ {0,0,0,0}, /* alternate rect */ 128, 2, /* res ID's as normal */ 0, 0, 0, 0, 0, 0 }, HMStringResItem { /* StatText */ {0,0}, /* default tip */ {0,0,0,0}, /* alternate rect */ 0, 0, 128, 3, /* stat text is disabled! */ 0, 0, 0, 0 } } }; /* Here are the STR#'s that the Help text is stored in! */ resource 'STR#' (128,purgeable) { /* help items for Apple Menu */ { /* array StringArray: 3 elements */ /* [1] */ "Everything you always wanted to know about ???, but were afraid to ask", /* [2] */ "Click here to dismiss this informative dialog.", /* [3] */ "This text describes the product, its author, and why!" } }; resource 'STR#' (129,purgeable) { /* help items for File Menu */ { /* array StringArray: ? elements */ /* [1] */ "Use this menu to select a file and to exit the program.", /* [2] */ "Use this menu to select a file and to exit the program. " "This menu is unavailable now.", /* [3] */ "Use this menu to select a file and to exit the program. " "This menu is unavailable until you respond to the alert box or dialog box.", /* [4] */ "This command is unavailable until you respond to the alert box or dialog box.", /* [5] */ "Use this command to select a file.", /* [6] */ "Use this command to select a file. " "Not available for some strange reason.", /* [7] */ "Use this command to exit the program.", /* [8] */ "Use this command to exit the program. " "Not available for some strange reason." } }; /* Error Alert Stuff */ resource 'ALRT' (200, purgeable) { {100, 120, 224, 452}, 200, { /* array: 4 elements */ /* [1] */ OK, visible, silent, /* [2] */ OK, visible, silent, /* [3] */ OK, visible, silent, /* [4] */ OK, visible, silent }, alertPositionMainScreen }; resource 'DITL' (200, purgeable) { { /* array DITLarray: 4 elements */ /* [1] */ {92, 246, 112, 318}, Button { enabled, "OK" }, /* [2] */ {11, 73, 75, 318}, StaticText { disabled, "^0" }, /* [3] */ {11, 11, 43, 43}, Icon { disabled, 0 }, /* [4] */ {96, 11, 112, 100}, StaticText { disabled, "Error #^1" } } }; /* This is the help resource for the ErrorAlert above. You should not need to change this. */ resource 'hdlg' (200, purgeable) { HelpMgrVersion, 0, hmSaveBitsNoWindow, 0, 3, /* need bits saved in Alert, since the OK ring & icons are not updated */ HMSkipItem { /* no missing items */ }, { HMStringResItem { /* OK button */ {0,0}, /* default tip */ {0,0,0,0}, /* alternate rect */ 200, 1, /* res ID's as normal */ 0, 0, 0, 0, 0, 0 }, HMStringResItem { /* StatText - error message*/ {0,0}, /* default tip */ {0,0,0,0}, /* alternate rect */ 0, 0, 200, 2, /* stat text is disabled! */ 0, 0, 0, 0 }, HMSkipItem { /* icon */ }, HMStringResItem { /* StatText - error ID*/ {0,0}, /* default tip */ {0,0,0,0}, /* alternate rect */ 0, 0, 200, 3, /* stat text is disabled! */ 0, 0, 0, 0 }, } }; resource 'STR#' (100) { /* error alert messages! */ { /* array StringArray: 1 elements */ /* [1] */ "This application requires Apple events. Please upgrade to System 7.0 or later to use.", /* [2] */ "An error occured during Apple event processing." } }; resource 'STR#' (200, purgeable) { /* help info for the Error Dialog */ { /* array StringArray: 3 elements */ /* [1] */ "Click here to dismiss this informative dialog.", /* [2] */ "This text describes the error that occured.", /* [3] */ "This error ID gives more specific details of the error." } }; resource 'vers' (1) { 0x01, /* major revision */ 0x00, /* minor revision */ release, 0x00, /* non-final release version */ verUS, /* country code */ "1.0", /* short vers string */ "Version 1.0" }; resource 'SIZE' (-1) { reserved, acceptSuspendResumeEvents, reserved, canBackground, multiFinderAware, backgroundAndForeground, dontGetFrontClicks, ignoreChildDiedEvents, is32BitCompatible, isHighLevelEventAware, localAndRemoteHLEvents, notStationeryAware, dontUseTextEditServices, reserved, reserved, reserved, 102400, 102400 }; resource 'SIZE' (0) { reserved, acceptSuspendResumeEvents, reserved, canBackground, multiFinderAware, backgroundAndForeground, dontGetFrontClicks, ignoreChildDiedEvents, is32BitCompatible, isHighLevelEventAware, localAndRemoteHLEvents, notStationeryAware, dontUseTextEditServices, reserved, reserved, reserved, 102400, 102400 }; resource 'FREF' (128) { 'APPL', 0, "" }; resource 'FREF' (129) { '****', 1, "" }; /* if you plan to support folders & disks then uncomment the next two lines and add the appropriate lines to the BNDL as well! resource 'FREF' (130) { 'fold', 2, "" }; resource 'FREF' (131) { 'disk', 3, "" }; */ /* Don't forget to give yourself a REAL creator type! */ /* Then modify the BNDL & the next resource for the old vers string */ resource 'BNDL' (128, "Bundle") { '????', 0, { /* array TypeArray: 2 elements */ /* [1] */ 'FREF', { /* array IDArray: 2 elements */ /* [1] */ 0, 128, /* [2] */ 1, 129 }, /* [2] */ 'ICN#', { /* array IDArray: 2 elements */ /* [1] */ 0, 128, /* [2] */ 1, 0 } } }; data '????' (0) { "" }; #ifdef BuildAETE /* This is the KEY resource in supporting AEVTs - it is used by scripting/macro systems such as Control Tower & QuicKeys in determing what events an application supports. In this one, we simply tell it we support the required events, since that is all we do. If you add additional events, you MUST consult the AETE documentation distributed by Apple. Make sure you find the RELEASE NOTES, since this resource has undergone a NUMBER of chanes. */ resource 'aete' (0, "Apple Events Terminology") { 0x00, /* major version number in BCD */ 0x90, /* minor version number in BCD. From 7/9/91 version of AEUT docs */ english, /* descriptions are in english */ roman, /* use roman language script system */ { /* array Suites: 1 elements */ /******************************************************* [1] -- The part that show we do the required AppleEvents *******************************************************/ "Required Suite", "Events that every application should support", 'reqd', 1, /* suite level */ 1, /* suite version */ { /* array Events: 0 elements */ }, { /* array Classes: 0 elements */ }, { /* array ComparisonOps: 0 elements */ }, { /* array Enumerations: 0 elements */ }, } }; #endif \ No newline at end of file diff --git a/IC2Resource.sit b/IC2Resource.sit new file mode 100644 index 0000000..33830da Binary files /dev/null and b/IC2Resource.sit differ diff --git a/QuickProfiler.c b/QuickProfiler.c new file mode 100644 index 0000000..ccb7f8f --- /dev/null +++ b/QuickProfiler.c @@ -0,0 +1 @@ +// // When __PROFILE is called, stack frame looks like this: // // sp+08 Caller's return address // sp+04 Address of function name string // sp Return address to start of function // // After we save registers and such, stack frame looks like this: // // sp+28 Caller's return address // sp+24 Address of function name string // sp+20 Return address to start of function // sp+16 saved A0 // sp+12 saved A1 // sp+08 saved D0 // sp+04 saved D1 // sp saved D2 // // Upon returning from __PROFILE, stack frame should look like this: // // sp Address of __PROFILEEND // // #include #include #include #include #include #pragma profile off #if defined(powerc) || defined(__powerc) extern void Profile_GetTime(UnsignedWide *time); #else #define Profile_GetTime(time) Microseconds(time) #endif #define THREAD_OFFSET 100 #define NAME_LENGTH 23 #define MAX_FUNCS 1000 #define MAX_THREADS 50 #define STACK_DEPTH 100 typedef struct { char name[NAME_LENGTH+1]; int calls; long address; UnsignedWide totalTime; } FunctionInfo; typedef struct { FunctionInfo *callerFunction; long returnAddress; } StackFrame; typedef struct { StackFrame stack[STACK_DEPTH]; StackFrame *stackPtr; FunctionInfo *currentFunction; UnsignedWide startTime; } ThreadInfo; static ThreadInfo gThread[MAX_THREADS]; static FunctionInfo gFunction[MAX_FUNCS]; static long gProfilerOn = false; static ThreadInfo *gCurrentThread; static FunctionInfo *gCallerFunction; static long gSaveArea[5]; static FunctionInfo *GetIndex(char *functionName:__A0, long address:__D0):__A0; static void StartTime(FunctionInfo *function:__A0, int call:__D0); static void StopTime(int index:__D0); static void GetThreadInfo(void); static void PushFrame(long returnAddress:__D0); static long PopFrame(void):__D0; static asm pascal void __PROFILEEND(void); #pragma parameter AddToWide(__A0, __A1) void AddToWide(UnsignedWide *a, UnsignedWide *b) = { 0x2011, // move.l 0(a1),d0 ;d0 = hi 0x2229, 0x0004, // move.l 4(a1),d1 ;d1 = lo 0x2410, // move.l 0(a0),d2 ;d2 = hi 0xD2A8, 0x0004, // add.l 4(a0),d1 ;add lo parts 0xD580, // addx.l d0,d2 ;add hi parts with carry 0x2082, // move.l d2,0(a0) ;store high result 0x2141, 0x0004 // move.l d1,4(a0) ;store low result }; #pragma parameter SubFromWide(__A1, __A0) void SubFromWide(UnsignedWide *a, UnsignedWide *b) = { 0x2011, // move.l 0(a1),d0 ;d0 = hi 0x2229, 0x0004, // move.l 4(a1),d1 ;d1 = lo 0x2410, // move.l 0(a0),d2 ;d2 = hi 0x92A8, 0x0004, // sub.l 4(a0),d1 ;add lo parts 0x9580, // subx.l d0,d2 ;add hi parts with carry 0x2082, // move.l d2,0(a0) ;store high result 0x2141, 0x0004 // move.l d1,4(a0) ;store low result }; pascal OSErr ProfilerInit(ProfilerCollectionMethod method, ProfilerTimeBase timeBase, short numFunctions, short stackDepth) { int i; // initialize the function table memset(gFunction, 0, sizeof(gFunction)); strcpy(gFunction[0].name, "Root_Function"); gFunction[0].address = -1; gFunction[0].calls = 1; // initialize the thread table memset(gThread, 0, sizeof(gThread)); for (i = 0; i < MAX_THREADS; i++) gThread[i].stackPtr = gThread[i].stack; Profile_GetTime(&gThread[0].startTime); gThread[0].currentFunction = gFunction; // make sure we're turned off gProfilerOn = false; return noErr; } pascal void ProfilerSetStatus(short on) { gProfilerOn = (on) ? true : false; } pascal OSErr ProfilerDump(StringPtr filename) { /* static char cfile[256]; FunctionInfo *function; double temp; FILE *f; int i; BlockMoveData(filename + 1, cfile, *filename); cfile[*filename] = 0; f = fopen(cfile, "w"); if (f) { for (i = 0, function = gFunction; i < MAX_FUNCS; i++, function++) if (function->address) { temp = ((double)function->totalTime.hi * 65536.0 * 65536.0) + (double)function->totalTime.lo; fprintf(f, "%-40s %15.0lf ticks, %15.2lf ticks/call, %7d calls\n", function->name, temp, temp/function->calls, function->calls); } fclose(f); } return noErr; */ } pascal void ProfilerTerm(void) { gProfilerOn = false; } static void StartTime(FunctionInfo *function:__A0, int call:__D0) { UnsignedWide thisTime, diff; FunctionInfo *last; // get the current time Profile_GetTime(&thisTime); // if there was another function being timed, update that timer last = gCurrentThread->currentFunction; if (last) { diff = gCurrentThread->startTime; SubFromWide(&thisTime, &diff); AddToWide(&last->totalTime, &diff); } // if we have a new function to track, reset the start time and increment the calling count if (function) { if (call) function->calls++; gCurrentThread->startTime = thisTime; } // update the current thread's function pointer gCurrentThread->currentFunction = function; } static FunctionInfo *GetIndex(char *functionName:__A0, long address:__D0):__A0 { FunctionInfo *function; register int i; // loop over all functions, looking for an address match or an empty space for (i = 0, function = gFunction; i < MAX_FUNCS; i++, function++) { long a = function->address; if (!a) break; else if (a == address) return function; } // if we have space for a new function, create an entry if (i < MAX_FUNCS) { strncpy(function->name, functionName, NAME_LENGTH); function->address = address; return function; } // return nil if nothing could be found return nil; } void GetThreadInfo(void) { // get a pointer to the current thread's info ThreadID thread; GetCurrentThread(&thread); gCurrentThread = gThread + thread - THREAD_OFFSET; } void PushFrame(long returnAddress:__D0) { // get a pointer to the next empty stack frame, and increment the stack pointer StackFrame *sp = gCurrentThread->stackPtr++; // save the current function and return address sp->callerFunction = gCurrentThread->currentFunction; sp->returnAddress = returnAddress; } long PopFrame(void):__D0 { // get a pointer to the next empty stack frame, and increment the stack pointer StackFrame *sp = --gCurrentThread->stackPtr; // save the current function and return address gCallerFunction = sp->callerFunction; return sp->returnAddress; } // When __PROFILE is called, stack frame looks like this: // // sp+0c Return space for Pascal function // sp+08 Caller's return address // sp+04 Address of function name string // sp Return address to start of function // // After we save registers and such, stack frame looks like this: // // sp+32 Return space for Pascal function // sp+28 Caller's return address // sp+24 Address of function name string // sp+20 Return address to start of function // sp+16 saved A0 // sp+12 saved A1 // sp+08 saved D0 // sp+04 saved D1 // sp saved D2 // // Upon returning from __PROFILE, stack frame should look like this: // // sp+04 Return space for Pascal function // sp Address of __PROFILEEND asm pascal void __PROFILE(char *functionName) { tst.l gProfilerOn // is the profiler on? bne.s @doProfile // if so, go do the work move.l (sp),4(sp) // otherwise, copy return address over parameter addq.l #4,sp // adjust the stack pointer accordingly rts // return @doProfile: _Debugger clr.l gProfilerOn // clear the profiler flag -- we are not reentrant movem.l a0/a1/d0-d2,-(sp) // save registers jsr GetThreadInfo // get the current thread info into a global move.l 28(sp),d0 // get the return address in d0 jsr PushFrame // and push it onto the stack move.l 24(sp),a0 // now get the function name move.l 20(sp),d0 // and the function address move.l d0,24(sp) // (copy the address onto the stack for later) jsr GetIndex // and return an index into our function table moveq.l #1,d0 // set the call flag jsr StartTime // start the timer for this baby (and stop the last one) lea __PROFILEEND,a0 // point a0 to our profile end routine move.l a0,28(sp) // store that in place of the real return address movem.l (sp)+,a0/a1/d0-d2 // restore the registers addq.l #4,sp // skip over stack space for the functionName parameter move.l #1,gProfilerOn // turn the profiler flag back on rts // and return } // sequence of events: // // call to __PROFILE // clears profilerOn flag to prevent re-entrancy // gets a pointer to the current thread info in gCurrentThread // saves the currentFunction pointer and the real return address as a stack frame // calculates a pointer to the new currentFunction // stops the timer for the previous currentFunction and starts it for the new one // replaces the real return address with a return into __PROFILEEND // resets profilerOn flag // returns to the beginning of the function // // return to __PROFILEEND // clears profilerOn flag to prevent re-entrancy // gets a pointer to the current thread info in gCurrentThread // pops the stack frame, returning real return address and pointer to caller function // saves real return address on the stack for returning // stops the timer for the previous function and starts it back up for the caller function // resets profilerOn flag // returns to the calling function // // When __PROFILEEND is called, stack frame looks like this: // // sp Return space for Pascal function // // Upon returning from __PROFILE, stack frame should look like this: // // sp Address of __PROFILEEND asm pascal void __PROFILEEND(void) { _Debugger clr.l gProfilerOn // clear the profiler flag -- we are not reentrant movem.l a0/d0,gSaveArea // save a0/d0 jsr GetThreadInfo // get the current thread info into a global jsr PopFrame // pop the frame, returning the real return address move.l d0,gSaveArea+8 // save return address in a safe place move.l gCallerFunction,a0 // get pointer to new current function clr.l d0 // clear the call flag jsr StartTime // now start the timer movem.l gSaveArea,a0/d0 // restore a0/d0 move.l gSaveArea+8,a1 // get return address in a1 move.l #1,gProfilerOn // turn the profiler flag back on jmp (a1) // return to the caller } \ No newline at end of file diff --git a/uuUndo & NewsWatcher.TXT b/uuUndo & NewsWatcher.TXT new file mode 100644 index 0000000..5b98201 --- /dev/null +++ b/uuUndo & NewsWatcher.TXT @@ -0,0 +1 @@ +>A uudecode utility must have the following attributes to be usable with >NewsWatcher: Sounds like I already got most of them last night! I'll mail you an alpha version later today for your preliminary evaluation. >No splash screen or registration dialog or other display that the user must >dismiss, at least not when launched with an ODOC event. Check. >No standard file prompt for destination file name and location. Use the >file name from the uuencode header line, and put the decoded result file in >the same folder as the input file. Check. >Append digits or something if necessary to guarantee unique destination >file name. Check. >Launchable in background. Do not assume you start out in the foreground. Check. >Use Notification Manager properly if you are in the background before >presenting error alerts or other dialogs. Check -- right now there *are* no error alerts or dialogs. (This may change, of course :-) >Must be drag-and-drop, of course: Drop a text file on the utility, and it >decodes. Check. >Must be able to accept additional ODOC events while busy, and queue them. >In NewsWatcher, if the helper app is already running, I just send it an >ODOC event. I don't queue them up at my end. This is important, for >example, if a user extracts binaries from several articles at once in a >subject window. In this case, I will likely be creating temp.n.uu files and >firing ODOC events at you faster than you can process them. I think this works, but will check it. All WaitNextEvent() calls during the processing have an event filter of (everyEvent - highLevelEventMask), so things should queue up in the OSEvent queue until I get back to the main event loop. >Must delete input file after successful decode, or at least offer this as a >configurable option. If any kind of error occurs during decoding, however, >the input file must not be deleted. Not done yet, but planned as an option. >Must be able to handle multiple-part uuencoded text with intermixed headers >and other junk, with or without the special "flag lines" indicating where >the parts begin and end. You can, however, assume that parts are in the >correct order. Check. (This was one of the really cool features of my unix version :-) >The following attributes are highly desirable: >Should quit when finished if launched with ODOC event. Check. >Should have configurable preferences for file name suffix -> file type and >creator mapping. (I did manage to convince Leonard to make JPEGView the >default for gif and jpeg files in Expander, by the way. With Expander, you >have to attack StuffIt Engine with ResEdit to change this mapping.) Not yet, but planned. At first, I'll probably just do something similar to the Stuffit Engine until I have time for going all out. >Should run like a bat out of Hell in native mode, of course. :-) Check -- faster than Leonard's, even :-) >Should call WaitNextEvent every 4-6 ticks while busy, no more and no less, >to cooperate well with other apps. I'm using a sleep time of 3 ticks while busy, and making sure I don't call WaitNextEvent any more often than every 2 ticks. It *still* runs like a bat out of hell, though. >Should *not* slow down when run in the background. Check. >A progress dialog is fine, but handle update events properly and reasonably >quickly. Movable modal progress dialogs are best. Check. >Should move the decoded result file to the same location in the Finder >window as the input file after deleting the input file. This makes dragging >and dropping from NewsWatcher to a Finder window nicer, because the final >decoded file ends up where you dropped it. Hadn't thought of that, but it's a good idea. I'll put it in. Thanks for the checklist -- it makes my job a lot easier. Maybe you can even distribute my app with NewsWatcher if you like it enough. I don't really care what you do with it; I'm writing it for my own reasons, anyway :-) Aaron \ No newline at end of file diff --git a/uuUndo 1_0 Æ’/uuUndo notes b/uuUndo 1_0 Æ’/uuUndo notes new file mode 100644 index 0000000..acf39fa --- /dev/null +++ b/uuUndo 1_0 Æ’/uuUndo notes @@ -0,0 +1 @@ +uuUndo a fast, free batch uudecoding utility written by Aaron Giles (giles@med.cornell.edu) version 1.0, 7 November 1994 Overview This is the first official release of uuUndo, a fast, free batch uudecoding utility for the Macintosh. Although uuUndo was originally designed to work with John NorstadÕs NewsWatcher, it does quite nicely in its own right as a standalone drag and drop smart uudecoder. uuUndo is shipped as a fat binary, running incredibly quickly on PowerPC machines, and makes full use of asynchronous I/O to ensure smooth operation in the background. Because it is based on Leonard RosenthalÕs DropShell code, uuUndo is also fully scriptable and recordable. uuUndo requires System 7. If you find any bugs or have any comments, please email me at giles@med.cornell.edu. Thank you. About uuencoding uuUndo works with uuencoded files. uuencoding is a common format on non-Macintosh systems which accomplishes the same thing that BinHex does on the Macintosh; that is, it is designed to allow a binary data file to be stored as text and thus included in mail and news messages. Unlike the BinHex format, however, there is no integrity checking built into uuencoding, so there is absolutely no guarantee that a file has been decoded properly. Be careful! Design Limitations uuUndo was designed from the start to accept uuencoded text files Ñ possibly interspersed with header lines and other miscellaneous junk Ñ and translate them into their original binary format. As such, it assumes that the file contains ALL of the original uuencoded data stored in the proper order. What this means is that you cannot save several parts of a uuencoded file to three separate files and expect uuUndo to know what to do with them. It also means that you cannot save a bunch of unordered parts to the same file and expect to get the proper result. You must save all parts of a uuencoded file to the same file and in order. If you are using NewsWatcher, this is done for you automatically. Operation uuUndoÕs general operation should be pretty straightforward: you can either drag and drop uuencoded files onto the uuUndo icon in the Finder, or you can manually open up the uuUndo application, choose uuDecode File... from the File menu, and choose which file you wish to decode. (Quick tip: If you are using NewsWatcher to decode a number of binaries, you might want to consider launching uuUndo first, before extracting. This prevents uuUndo from quitting and relaunching between files, and makes overall operation much smoother.) Duplicate Files If the file which is created through the uudecoding process already exists, a new name is algorithmically generated by adding a Ò.0Ó to the end of the file name. In the event of a further conflict, the last character incremented until a unique name is found. Preferences for Extension Mapping Unlike BinHex files, uuencoded files do not contain all the information needed to determine what type of file they are, or which application should be used to open them. In order to get around this, uuUndo comes with some flexible extension mapping capabilities; this means that uuUndo will examine the last part of the file's name (the ÒextensionÓ), and from that guess which icon to give the new file. For example, filenames ending in Ò.GIFÓ get automatically mapped to JPEGViewÕs GIF icon. Although uuUndo contains an internal list of which extensions should get mapped to which icons, it is often useful to be able to add your own extension types or to change which icon a given file extension will map to. To do this in uuUndo, you will need to open up uuUndoÕs Preferences screen. Do this by choosing Preferences... from the File menu. What you get when you do this is a window dominated by a list box on the left hand side. This list contains all the extensions uuUndo currently knows about, along with the creator and type codes, and the icon associated with that extension. You can scroll through this list, select items (one at a time only), and generally do what youÕre used to doing with lists of this sort. Note that when you click on an item in the list, the extension, creator code, type code, and icon all get displayed in the area at the right; in addition, the name of the application matching the given creator code is displayed. But wait a second...I know what youÕre thinking! YouÕre thinking, ÒYou keep talking about these creator and type codes Ñ but what exactly ARE they?Ó Well, briefly, every file in the Macintosh file system has a pair of these four-character codes, and together these codes serve to indicate to the Finder which icon it should display. For a full explanation, see the section Creator and Type Codes, later in this document. To modify the mapping of an extension which already is in the list, you need only double-click on the extension in the list, or else select the extension and click the Edit button. When you do this, boxes will appear around the items on the right (except for the application name, which is just displayed for informational purposes). You can then type in the extension (up to 5 characters total, including the period), and the creator and type codes. Click the Done button, or anywhere in the extension list to save your changes. But what if you donÕt know what the creator and type codes are? Well, thatÕs easy. When you begin editing an extension, you will see the Find Example Icon... button become enabled. Click this button, and you will be presented with the standard mechanism for selecting a file. Simply choose a file on your hard drive whose icon matches the one you wish to map the current extension to, and the creator and type values will automatically be extracted and set for you. In addition, if you are running System 7.5, or if you have the Macintosh Drag and Drop extension installed on your system and are running System 7.1.2 or later, you can accomplish the same thing in a more elegant way. Simply use the Finder to locate an example of the icon you wish, and drag the icon into the icon box in the uuUndo Preferences window. As you do this, the inside of the box will highlight, and when you let go of the mouse button, the creator and type of the current extension will be set to the creator and type of the icon you dragged from the Finder. You can also add new extensions onto the end of the list by clicking in the New button, or delete superfluous extensions by selecting an item in the list and clicking the Delete button. When you are finished, simply close the uuUndo Preferences window, and all your changes will be saved. Other Preferences By default, uuUndo will leave the original uuencoded file alone when it is finished. This allows you to try again in case the decoding didnÕt work quite right (remember that there is no way for uuUndo to know whether everything worked!) However, as a convenience, uuUndo can automatically move files into the trash can or delete them as they are processed. To use these features, choose a different option from the popup menu at the bottom of uuUndoÕs preferences screen. The three available options are leave source file alone, move source file to the trash, and delete source file immediately. Creator and Type Codes A creator code is associated with a particular application; for instance, JPEGViewÕs creator code is 'JVWR', and SimpleTextÕs creator code is 'ttxt'. On the other hand, a type code serves to indicate what type of data the file contains; for instance a text file has a type of 'TEXT', and a GIF file has a type of 'GIFf'. It is important to note that both creator and type codes are case-sensitive; that is, 'giff' is different than 'GIFf'. Another important thing to realize is that one creator code can be associated with several type codes, and vice-versa. As an example, consider the application SimpleText, which has a creator type of 'ttxt'. SimpleText can open three types of files (actually more, but weÕll leave it at three for this example): text files ('TEXT'), read-only text files ('ttro'), and pictures ('PICT'). To get SimpleTextÕs text file icon, you should set the creator and type to 'ttxt' and 'TEXT', respectively. To get SimpleTextÕs picture file icon, you use 'ttxt' and 'PICT'. On the other hand, SimpleText isnÕt the only application that can open text and picture files. You can open text files in BBEdit as well, and BBEditÕs creator code is 'R*ch' (remember, types are case sensitive!). So to get the text file icon for BBEdit instead of SimpleText, you would use a creator code of 'R*ch' instead of 'ttxt'. Change History 1.0: ¥ fixed another bug which would prevent files from being moved into the trash ¥ restored the option to delete files immediately 1.0b4: ¥ fixed a bug which would prevent files from being moved into the trash if the trash already contained a file with the same name 1.0b3: ¥ fixed a really stupid bug which would sometimes (often?) corrupt the extension mapping preferences; if you fell victim to this, you can get the defaults back by deleting your uuUndo preferences file (in the Preferences folder in your System Folder) 1.0b2: ¥ added the uuUndo preferences screen, allowing you to edit the extension mapping uuUndo does. The resource format is the same as before, but it is stored in the uuUndo Preferences file; uuUndoÕs default mapping is still stored in the applicationÕs resource fork ¥ changed the option to delete the source file to only move the file to the trash. This accomplishes mostly the same thing, and lets you get back to any damaged files to examine them by hand ¥ the progress window now displays the name of the output file, rather than the input file name ¥ fixed the bug which crashed non-Color QuickDraw-capable machines (though the new preferences code may still have some problems yet...) 1.0b1: ¥ first public release; essentially just 1.0a2 repackaged 1.0a2: ¥ IMPORTANT NOTE: If you are running System 7.5b2, and you have the Apple Menu Options control panel installed, you cannot drag and drop files from a mounted file server without crashing. I don't know why this is happening, but it is definitely a conflict caused by a patch the Apple Menu Options CP makes ¥ all disk I/O is now done asynchronously, and the central I/O during decoding is implemented using a fancy double-buffering scheme. This scheme enables us to be decoding one half of the input buffer as the other half is being read in, and similarly half of the output buffer can be written as the other half is being decoded into. I have realized an overall speed increase on my PowerPC of about 15%. But more importantly, all the asynchronous I/O allows other applications to operate more smoothly while uuUndo is churning away in the background ¥ The keyword "end" must now be on a line all by itself to be recognized. This prevents certain lines like "end part 1" from tricking uuUndo into thinking that it had reached the end of the uuencoded data ¥ File extension mapping is now done *before* long names get truncated, so that really long names have a chance of getting properly mapped ¥ The appropriate menus are now dimmed during a decode operation ¥ the cursor is reset to an arrow when a context switch is done 1.0a1: ¥ initial release \ No newline at end of file diff --git a/uuUndo To Do!.TXT b/uuUndo To Do!.TXT new file mode 100644 index 0000000..19b914e --- /dev/null +++ b/uuUndo To Do!.TXT @@ -0,0 +1 @@ +To do yet ========= Preferences: More features?: Debinhexing Test test test! \ No newline at end of file diff --git a/uuUndo.68k.µ b/uuUndo.68k.µ new file mode 100644 index 0000000..14cc475 Binary files /dev/null and b/uuUndo.68k.µ differ diff --git a/uuUndo.ppc.µ b/uuUndo.ppc.µ new file mode 100644 index 0000000..a551af0 Binary files /dev/null and b/uuUndo.ppc.µ differ diff --git a/uuUndoLDEF.68k.µ b/uuUndoLDEF.68k.µ new file mode 100644 index 0000000..6bf0cfc Binary files /dev/null and b/uuUndoLDEF.68k.µ differ diff --git a/uuUndoLDEF.c b/uuUndoLDEF.c new file mode 100644 index 0000000..65d670d --- /dev/null +++ b/uuUndoLDEF.c @@ -0,0 +1 @@ +#include #include static OSErr FindIcon(OSType creator, OSType type); static char gIconBuffer[1024]; static GWorldPtr gTheGWorld = nil; static OSType gIconType = kLargeIcon; static Rect gIconBounds = { 0, 0, 32, 32 }; pascal void main(short message, Boolean selected, Rect *cellRect, Cell theCell, short dataOffset, short dataLen, ListHandle theList) { long oldA4 = SetCurrentA4(); char cellData[16]; RgnHandle oldClip; PenState oldState; FontInfo theInfo; GrafPtr oldPort; Point thePoint; OSErr theErr; short depth; switch (message) { case lInitMsg: if (((CGrafPtr)(*theList)->port)->portVersion & 0xc000) { GDHandle mostDevice; GrafPtr oldPort; Rect globalRect; GetPort(&oldPort); SetPort((*theList)->port); globalRect = (*theList)->port->portRect; LocalToGlobal((Point *)&globalRect.top); LocalToGlobal((Point *)&globalRect.bottom); mostDevice = GetMaxDevice(&globalRect); depth = (*(*mostDevice)->gdPMap)->pixelSize; if (depth == 4) gIconType = kLarge4BitIcon, depth = 4; else if (depth > 4) gIconType = kLarge8BitIcon, depth = 8; else gIconType = kLargeIcon, depth = 1; SetPort(oldPort); } else gIconType = kLargeIcon, depth = 1; theErr = NewGWorld(&gTheGWorld, depth, &gIconBounds, nil, nil, 0); if (theErr != noErr || !gTheGWorld) theErr = NewGWorld(&gTheGWorld, depth, &gIconBounds, nil, nil, useTempMem); break; case lDrawMsg: GetPort(&oldPort); SetPort((*theList)->port); oldClip = NewRgn(); GetClip(oldClip); ClipRect(cellRect); GetPenState(&oldState); PenNormal(); EraseRect(cellRect); if (dataLen == 16) { LGetCell(cellData, &dataLen, theCell, theList); GetFontInfo(&theInfo); thePoint.h = cellRect->left + 5; thePoint.v = cellRect->top + ((34 - (theInfo.ascent + theInfo.leading + theInfo.descent)) / 2) + theInfo.ascent; MoveTo(thePoint.h, thePoint.v); DrawString((StringPtr)cellData); thePoint.h += 87; MoveTo(thePoint.h, thePoint.v); DrawText((StringPtr)cellData, 10, 4); DrawChar('/'); DrawText((StringPtr)cellData, 6, 4); } theErr = FindIcon(*(long *)&cellData[10], *(long *)&cellData[6]); if (theErr == noErr) { PixMapHandle thePixMap = GetGWorldPixMap(gTheGWorld); Rect dstRect = *cellRect; LockPixels(thePixMap); dstRect.right -= 5; dstRect.left = dstRect.right - 32; dstRect.top++; dstRect.bottom = dstRect.top + 32; CopyBits((BitMap *)*thePixMap, &(*theList)->port->portBits, &gIconBounds, &dstRect, srcCopy, nil); UnlockPixels(thePixMap); } if (selected) { LMSetHiliteMode(LMGetHiliteMode() & ~(1 << hiliteBit)); InvertRect(cellRect); } SetPenState(&oldState); SetClip(oldClip); DisposeRgn(oldClip); SetPort(oldPort); break; case lHiliteMsg: LMSetHiliteMode(LMGetHiliteMode() & ~(1 << hiliteBit)); InvertRect(cellRect); break; case lCloseMsg: if (gTheGWorld) DisposeGWorld(gTheGWorld), gTheGWorld = nil; break; } SetA4(oldA4); } OSErr FindIcon(OSType creator, OSType type) { ParamBlockRec vol; DTPBRec dt; Str255 path; OSErr theErr; if (!gTheGWorld) return memFullErr; vol.volumeParam.ioCompletion = nil; vol.volumeParam.ioVolIndex = 1; vol.volumeParam.ioNamePtr = path; while (true) { vol.volumeParam.ioVRefNum = 0; theErr = PBGetVInfoSync(&vol); if (theErr != noErr) break; dt.ioVRefNum = vol.volumeParam.ioVRefNum; dt.ioNamePtr = path; theErr = PBDTGetPath(&dt); if (theErr != noErr) continue; dt.ioCompletion = nil; dt.ioDTBuffer = gIconBuffer; dt.ioDTReqCount = 1024; dt.ioIconType = gIconType; dt.ioFileCreator = creator; dt.ioFileType = type; theErr = PBDTGetIconSync(&dt); if (theErr == noErr && gTheGWorld) { PixMapHandle thePixMap = GetGWorldPixMap(gTheGWorld); char *src, *dst; short row, size; LockPixels(thePixMap); src = gIconBuffer; dst = GetPixBaseAddr(thePixMap); size = 32 * (*thePixMap)->pixelSize / 8; for (row = 0; row < 32; row++) { BlockMove(src, dst, size); src += size; dst += (*thePixMap)->rowBytes & 0x3fff; } UnlockPixels(thePixMap); return noErr; } vol.volumeParam.ioVolIndex++; } } \ No newline at end of file diff --git a/uundo13.c b/uundo13.c new file mode 100644 index 0000000..49aa160 --- /dev/null +++ b/uundo13.c @@ -0,0 +1 @@ +/* * uundo.c: extract a multi-part uuencoded archive Version 1.3a * written by Aaron Giles 03/08/92 * * usage: * uundo [-hLloqv] file1 [file2 [...]] * uundo [-hLloqv] < file * * options: * -h header: write the article header of the earliest part to target.hdr * -L lower all: convert all characters in all filenames to lower case * -l lower: convert only all-upper-case filenames to lower case * -o overwrite: automatically overwrite existing files without permission * -q query: allow overwriting of existing files, but ask permission first * -v verbose: display part numbers and status info * * special features: * - does not require all parts to be in order* * - attempts to identify missing parts* * - works on uuencoded files included in shar archives * - can accept either single or multiple files on the command line * - can accept stream input from stdin * (* requires part numbering informating in the subject line of each part) * * bugs/requests/modifications to: * a-giles@uchicago.edu -or- * gile@midway.uchicago.edu -or- * giles@hep.uchicago.edu * * philosophy: * I will try my best to keep up with any bug reports (first priority) or * new feature requests. Note that my primary goal is robustness, so that * requests which would overly complicate things or result in "flaky" code * will very likely not be put in. * * copyright control: * This program is public domain, though copyrighted (c) 1992 by its * author, Aaron Giles. If you wish to distribute modified copies of * this program, please contact the author first. * * standard disclaimer: * Use this program at your own risk. The author takes no responsibility * for any loss of data or damage that results from using this program. * * known problems/limitations: * - cannot handle multiple target files in the same input file (sorry!) * - can't yet accept input piped from rn or trn * * to be added in future versions: * - support for saving header information * - support for piping, allowing use with rn/trn * * version history: * 1.0 (2/11/92) - initial release * 1.1 (2/13/92) - added stdin support for preliminary use with rn * no longer so restrictive on the first line of each part * added verbose option * added a warning to overwrite existing files * added an option to turn off this warning * now attempts to make file writeable before overwriting * fixed problem with "BEGIN..." lines fooling the decoder * 1.2 (2/21/92) - added force lowercase options * changed procedure dealing with conflicting filenames * added query option * now uses process ID in temporary names to avoid conflicts * output directory can be set via UUNDO environment variable * 1.21 (2/28/92) - fixed bug that would alter directory attributes if none * of the input files were found * 1.3 (3/08/92) - added support for maximum filename length * - refined handling of jumbled and unlabelled parts * - fixed the aforementioned bug -- again * - added option to write header of earliest part to a file */ #include #include #include #include #include #include /* * Platform-specific definitions */ #define PATHSEP '/' /* path separator character */ #define MAXNAMELEN 32 /* maximum length of a given filename */ #define TEMPNAME "/usr/tmp/uu" /* prefix for temporary files */ /* * Arbitrary global definitions */ #define MAXLEN 256 /* maximum length of strings/pathnames */ #define MAXPARTS 256 /* maximum number of parts in file */ #define BUFSIZE 32768 /* size of temporary buffer */ /* * Header storage structure */ typedef struct header_str *header_ptr; struct header_str { char *line; header_ptr next; }; /* * Global variables */ int debug = 0; /* debug flag */ int header = 0; /* header flag */ int Lower = 0; /* all lower case flag */ int lower = 0; /* lower case flag */ int overwrite = 0; /* overwrite flag */ int query = 0; /* query flag */ int verbose = 0; /* verbose flag */ int part, total; /* current part number and total count */ int pending[MAXPARTS]; /* table of pending entries */ char lastsubj[MAXLEN]; /* text of the last subject line read */ int partswritten; /* total number of parts written to target */ long int byteswritten; /* total number of bytes written to target */ FILE *targetfile; /* stream pointer for target file */ char targetname[MAXLEN]; /* name of target file */ char *targetpath; /* pointer to path for output directory */ int targetmode; /* file mode for final target */ header_ptr hdrroot = NULL; /* linked list of header lines */ header_ptr hdrlast = NULL; /* last group of header lines read */ int hdrpart; /* part associated with this header */ pid_t pid; /* process ID of this program */ /* * tempname: * put temporary file name for part num into string variable name */ char *tempname(int num, char *name) { strcpy(name, TEMPNAME); sprintf(name+strlen(name), "%ld.%03i", pid, num); return name; } /* * filename: * combine path, filename, and iteration, checking for length */ char *filename(char *dest, char *path, char *file, int iter) { char *p, *q; char suffix[4]; *dest = 0; if (path) strcat(dest, path); if ((!path) || (dest[strlen(dest) - 1] != PATHSEP)) { dest[strlen(dest) + 1] = 0; dest[strlen(dest)] = PATHSEP; } q = dest + strlen(dest); for (p = file + strlen(file); (*(p - 1) != PATHSEP) && (p > file); p--); strncat(dest, p, MAXNAMELEN); if (iter) { if (iter > 0) sprintf(suffix, ".%02i", iter); else strcpy(suffix, ".hdr"); if (strlen(q) > (MAXNAMELEN - 3)) strcpy(q + MAXNAMELEN - 3, suffix); else strcpy(q + strlen(q), suffix); } return dest; } /* * createfile: * attempt to create the named file */ FILE *createfile(char *name, char *type) { FILE *file; if (!(file = fopen(name, "w"))) fprintf(stderr, "uundo: unable to create %s file %s\n", type, name); return file; } /* * openfile: * attempt to open the named file for reading */ FILE *openfile(char *name, char *type) { FILE *file; if (!(file = fopen(name, "r"))) fprintf(stderr, "uundo: unable to open %s file %s\n", type, name); return file; } /* * deletefile: * remove the named file */ void deletefile(char *name) { chmod(name, 0666); if (unlink(name)) fprintf(stderr, "uundo: unable to remove file %s\n", name); } /* * checkfile: * check for existence of target file before overwriting */ int checkfile(char *name) { FILE *file; char s[MAXLEN]; int count = 0; while (file = fopen(filename(targetname, targetpath, name, count++), "r")) { fclose(file); if (overwrite) { deletefile(targetname); return 1; } else if (query) { fprintf(stderr, "uundo: %s already exists: overwrite? ", name); gets(s); if ((*s == 'Y') || (*s == 'y')) { deletefile(targetname); return 1; } } } return 1; } /* * readline: * input a line and strip the newline character */ int readline(FILE *file, char *s) { int l; if (!fgets(s, MAXLEN, file)) return 0; if ((l = strlen(s)) == (MAXLEN - 1)) { fprintf(stderr, "uundo: invalid input file format\n"); exit(1); } s[l - 1] = 0; return 1; } /* * addhdr: * add a header line to the end of the linked list */ header_ptr addhdr(header_ptr root, char *line) { header_ptr new, cur; if (new = (header_ptr)malloc(sizeof(struct header_str))) { new->next = NULL; if (new->line = (char *)malloc(strlen(line) + 1)) { strcpy(new->line, line); if (root) { for (cur = root; cur->next; cur = cur->next); cur->next = new; } else root = new; return root; } } fprintf(stderr, "uundo: out of memory!"); exit(1); } /* * killhdr: * kill the linked list of header lines */ header_ptr killhdr(header_ptr root) { header_ptr cur, next; if (!root) return NULL; for (cur = root; cur; cur = next) { free(cur->line); next = cur->next; free(cur); } return NULL; } /* * writehdr: * write out the linked list of headers */ void writehdr(header_ptr root) { FILE *hdrfile; char hdrname[MAXLEN]; if ((!header) || (!root)) return; filename(hdrname, targetpath, targetname, -1); if (!(hdrfile = createfile(hdrname, "header"))) exit(1); for ( ; root->next; root = root->next) fprintf(hdrfile, "%s\n", root->line); fclose(hdrfile); } /* * uuline: * determine if the given line is a correct uuencoded line */ int uuline(char *s, int full) { int l, len, max, min; if ((strncasecmp(s, "end", 3) == 0) || (strncasecmp(s, "begin", 5) == 0) || ((l = s[0] - ' ') & 0xc0)) return 0; min = ((l * 4) + 2) / 3; max = ((min + 3) / 4) * 4 + 1; len = strlen(s) - 1; if ((len < min) || (len > max)) return 0; if (!full && ((l == 45) || (l == 0))) return 1; for (s++; *s; s++) if ((*s < ' ') || (*s > '`')) return 0; return 1; } /* * headerstate: * determine whether we should be saving (1) or ignoring (0) header lines */ int headerstate(char *line, int oldstate) { if (oldstate) { if ((line[0] == '#') && (strstr(line, "/bin/sh"))) return 0; return 1; } else { while (!isspace(*line) && *line && (*line != ':')) line++; if (*line == ':') return 1; return 0; } } /* * decode: * decode a uuencoded line and output the binary data to ouf */ void decode(FILE *outf, char *line) { int count = line[0] - ' '; int i, j; if (debug) printf("%s\n", line); for (j = 1; line[j]; j++) line[j] = (line[j] - ' ') & 0x3f; for (i = 0, j = 1; i < count; i += 3, j += 4) { line[i] = ((line[j] << 2) | (line[j+1] >> 4)) & 0xff; line[i+1] = ((line[j+1] << 4) | (line[j+2] >> 2)) & 0xff; line[i+2] = ((line[j+2] << 6) | (line[j+3] )) & 0xff; } if (count) { if (outf == targetfile) byteswritten += count; if (count -= fwrite(line, 1, count, outf)) { fprintf(stderr, "uundo: unable to write to output file\n"); exit(1); } } } /* * unpend: * append pending file to the target */ void unpend(int number) { int i; FILE *inf; char inname[MAXLEN]; char buffer[BUFSIZE]; if (!(inf = openfile(tempname(number, inname), "temporary"))) exit(1); while (i = fread(buffer, 1, BUFSIZE, inf)) { if (fwrite(buffer, 1, i, targetfile) != i) { fprintf(stderr, "uundo: unable to append temporary file %s\n", inname); exit(1); } else byteswritten += i; } fclose(inf); deletefile(inname); pending[number] = 0; } /* * parse: * determine part number and total number from subject header line */ void parse(char *p) { char *q; int i; part = -1; for (; *p; p++) { switch (*p) { case 'P': case 'p': if (isalpha(*(p - 1)) || (strncasecmp(p, "part", 4)) || isalpha(*(p + 4))) continue; p += 4; break; case '(': case '{': case '[': p++; break; case 'O': case 'o': if (isalpha(*(p - 1)) || (strncasecmp(p, "of", 2)) || isalpha(*(p + 2))) continue; case '|': case '/': for (q = p; isspace(*(q - 1)); q--); if (!isdigit(*(q - 1))) continue; while (isdigit(*(q - 1))) q--; if (isalpha(*(q - 1))) continue; p = q; break; default: continue; } i = (int)strtol(p, &q, 10); if (p == q) { p--; continue; } part = i; for (p = q; isspace(*p); p++); switch (*p) { case '|': case '/': p++; break; case 'o': case 'O': if (strncasecmp(p, "of", 2) == 0) { p+=2; break; } default: p--; continue; } while (isspace(*p)) p++; i = (int)strtol(p, &q, 10); if (p == q) { p--; continue; } total = i; return; } } /* * initfile: * parse the uuencode header line and initialize the output file */ int initfile(char *p) { char *q; if (partswritten > 0) { fprintf(stderr, "uundo: found extra part 1, ignored\n"); return 1; } while (isspace(*p) && *p) p++; if (*p) targetmode = (int)strtol(p, (char **)NULL, 8); else targetmode = 0666; while (!isspace(*p) && *p) p++; while (isspace(*p) && *p) p++; if (!*p) { strcpy(p, "a.out"); fprintf(stderr, "uundo: filename not found, using a.out instead\n"); } for (q = p; *q && !islower(*q); q++); if (Lower || (lower && !*q)) for (q = p; *q; *q = tolower(*q), q++); if (!checkfile(p)) exit(1); if (!(targetfile = createfile(targetname, "output"))) exit(1); partswritten = 0; return 0; } /* * startpart: * figure out where to output the newly-found part */ FILE *startpart() { char name[MAXLEN]; FILE *outf; if (partswritten > 0) while (pending[partswritten + 1]) unpend(++partswritten); if ((part == -1) && (partswritten < 0)) { fprintf(stderr, "uundo: unnumbered part found out of order; ignored\n"); return (FILE *)NULL; } if ((part == -1) || (part == (partswritten + 1))) return targetfile; if ((part <= partswritten) || (pending[part])) { fprintf(stderr, "uundo: found extra part %i, ignored\n", part); return (FILE *)NULL; } pending[part] = 1; if (!(outf = createfile(tempname(part, name), "temporary"))) exit(1); return outf; } /* * init: * initialize pending array and do general setup */ int init(int argc, char *argv[]) { int i; char *p; pid = getpid(); targetpath = getenv("UUNDO"); strcpy(lastsubj, ""); for (i = 1; argv[i] && (*argv[i] == '-'); i++) { for (p = argv[i] + 1; *p; p++) { switch (*p) { case 'd': debug = 1; break; case 'h': header = 1; break; case 'L': Lower = 1; break; case 'l': lower = 1; break; case 'o': overwrite = 1; break; case 'q': query = 1; break; case 'v': verbose = 1; break; } } } return i; } /* * initglobals: * reset all global variables in anticipation of next file */ void initglobals() { int i; for (i = 0; i < MAXPARTS; pending[i++] = 0); part = -1; total = 0; partswritten = -2; byteswritten = 0; targetname[0] = 0; targetmode = 0; hdrroot = killhdr(hdrroot); hdrlast = killhdr(hdrlast); hdrpart = MAXPARTS; } /* * findstart: * parse the header and skip garbage before uuencoded data */ FILE *findstart(FILE *inf) { FILE *outf; char line[MAXLEN]; int ignore = 0, save = 0; while (readline(inf, line)) { if (!line[0]) continue; if ((header) && (save = headerstate(line, save))) hdrlast = addhdr(hdrlast, line); if (strncasecmp(line, "Subject:", 8) == 0) { ignore = 0; parse(line + 8); } else if (strncmp(line, "begin ", 6) == 0) { if (!(ignore = initfile(line + 6))) return targetfile; } else if (!ignore && uuline(line, 1) && (line[0] != '#') && (line[0] != '-') && (line[0] != ' ')) { if (outf = startpart()) { decode(outf, line); return outf; } else ignore = 1; } } return (FILE *)NULL; } /* * partstatus: * display current part number and keep correct header info. */ void partstatus() { if (part < 0) part = partswritten + 1; if (header) { if (part < hdrpart) { hdrroot = killhdr(hdrroot); hdrroot = hdrlast; hdrpart = part; hdrlast = NULL; } else hdrlast = killhdr(hdrlast); } if (verbose) { if (total) fprintf(stderr, "uundo: found part %i of %i\n", part, total); else fprintf(stderr, "uundo: found part %i\n", part); } } /* * cleanup: * clean up our little mess */ void cleanup() { char name[MAXLEN]; int i, found = 0; for (i = 0; i < MAXPARTS; found += pending[i++]); if ((partswritten < 0) && (found)) fprintf(stderr, "uundo: missing file part 1\n"); else if (partswritten >= 0) { while (pending[partswritten + 1]) unpend(++partswritten); if ((total) && (partswritten < total)) { fprintf(stderr, "uundo: missing part %i of %i; target removed\n", partswritten + 1, total); if (partswritten >= 0) deletefile(targetname); } else { if (targetmode) chmod(targetname, targetmode); if (verbose) fprintf(stderr, "Wrote file %s (%i parts, %ld bytes)\n", targetname, partswritten, byteswritten); } if (partswritten >= 0) fclose(targetfile); } for (i = 0; i < MAXPARTS; i++) if (pending[i]) deletefile(tempname(i, name)); if (header) { writehdr(hdrroot); hdrroot = killhdr(hdrroot); } } /* * main program loop */ int main(int argc, char *argv[]) { FILE *infile = stdin, *outfile; int fptr, start; char line[MAXLEN]; start = init(argc, argv); initglobals(); for (fptr = start; (fptr < argc) || ((fptr == start) && (argc == start)); fptr++) { if ((argc > start) && (!(infile = openfile(argv[fptr], "input")))) continue; while (outfile = findstart(infile)) { partstatus(); while (readline(infile, line)) { if (!uuline(line, 0)) break; decode(outfile, line); } if (outfile != targetfile) fclose(outfile); else partswritten++; } fclose(infile); } cleanup(); } \ No newline at end of file