home *** CD-ROM | disk | FTP | other *** search
Wrap
#include <Gestalt.h> /* Previously named <GestaltEqu.h> */ #include <Memory.h> #include <Quickdraw.h> #include <Resources.h> #include <Notification.h> #include "ShowInitAndName.h" /* IMPORTANT: Read "ShowInitAndName.h" first!! */ /* As soon as possible after your init is called, be sure to show an icon. This will signify to the user that your init is just starting to load. ShowInitAndName ( 128, "Loch Ness Extension", nil, noErr, false ); As the very last thing your init does, show an icon again (this time indicating you’re allDone). This will signify to the user that your init is finished loading. ShowInitAndName ( 128, "Loch Ness Extension", nil, noErr, true ); If you follow these guidelines, the user can see exactly when your init starts, and exactly when your init ends. That should visually absolve your init of pauses, freezes, or startup crashes which are actually THE FAULT of inits before and after yours. */ OSErr ShowInitAndName ( short iconResourceID, char *extensionNameInCFormat, char *statusMessageInCFormat, OSErr errorEncounteredIfAny, Boolean allDone ) { /* ShowInitAndName displays an icon and extension name at the screen location appropriate for the current system extension (init). All parameters are sent in [--->] short iconResourceID; The resource manager id of the icon representing your system extension (init). (You may pass kIconResourceIDNone for no icon.) The icon ('icl8', 'icl4', 'cicn', or 'ICN#') will be drawn in color if a color resource is available and the screen location is color. The icon with iconResourceID will be drawn crossed out if errorEncounteredIfAny ≠ noErr and allDone is true. char * extensionNameInCFormat; The name of your system extension (init). (You may pass nil for no name.) This is in c-language string format (string ends with 0 byte). The extension name will be displayed beneath the icon. char * statusMessageInCFormat; A status message like "Attempting to install driver", "Driver installed successfully", etc. (You may pass nil for no message.) This is in c-language string format (string ends with 0 byte). You should assume these messages will be rapidly displayed to the user in future systems. If errorEncounteredIfAny ≠ noErr, statusMessageInCFormat ≠ nil, and allDone is true, a notification manager routine will be called with the statusMessageInCFormat. Put more simply, an alert will appear with your message if your final call to ShowInitAndName includes an error code. OSErr errorEncounteredIfAny; The system error encountered (if any) as the extension is installing. (You may pass noErr for... uh... no error.) Boolean allDone; If not false, the screen position will be advanced to the next appropriate location in preparation for the next init. (You may pass false to keep in the same location.) Pass false to not advance the screen position (often used to draw different icons at the same location for animation.) returns OSErr; noErr (0) No error resNotFound (-192) iconResourceID not found ('icl8', 'icl4', 'cicn', or 'ICN#') [This routine may also return the result of MemError() or ResError().] *** This routine MOVES MEMORY *** See also: ShowInitAndPascalName for the exact same function with a Pascal string format filename. 96/01/11 v1.0.0 David Cook First version */ QDGlobalsPlusAStartForTheLocalA5 localQDGlobals; /* Our own personal QD globals. Needed for 68K code as well as InitGraf calls at 'INIT' time. */ #ifndef powerc long originalA5Register; /* Original contents of register A5 when this routine was started. 68K code only. */ #endif ShowInitOverrideUPP showInitOverride; /* Used to call the system based ShowInitAndName if available. */ GrafPort localPort; /* Port to draw the icon into if main screen is B&W */ CGrafPort localCPort; /* Port to draw the icon into if main screen is color */ short pixelDepthOfMainScreen; /* 2^pixelDepth = number of colors (or 1 for b&w). */ short horizontal; /* Left point for starting to draw text or icon */ short vertical; /* Top point for starting to draw icon */ short widthOfText; short largerOfTextOrIconWidth; short iconCenteringValue; Rect iconDestinationRect; Rect textCStringDestinationRect; OSErr result; /* The error result (or noErr) of this entire function */ #ifndef powerc /* 680x0-chip based Macs (68K) require some registers to be set in a certain way for globals and Quickdraw */ originalA5Register = SetA5 ( (long) &(localQDGlobals.setTheLocalA5ToHere) ); #endif result = noErr; /* This is very cool. This allows a system routine to show our init icon and name instead of us. This might be very useful in future operating systems where the init is shown differently. */ if ( Gestalt ( gestaltShowInitAndName, (long *) &showInitOverride ) == noErr ) { if ( showInitOverride != nil ) { result = CallShowInitOverrideProc ( showInitOverride,iconResourceID, extensionNameInCFormat, statusMessageInCFormat, errorEncounteredIfAny, allDone, 0 ); } } if ( result == kShowInitHasOverridden_NoNeedForLocalCode ) { result = noErr; } else if ( result == noErr ) { /* Initialize our localQDGlobals structure */ /* This prepares Quickdraw for all of our drawing calls */ InitGraf ( &localQDGlobals.thePort ); InitFonts ( ); pixelDepthOfMainScreen = GetMainScreenPixelDepth ( ); if ( pixelDepthOfMainScreen > 1 ) { OpenCPort ( &localCPort ); /* The main screen is color */ /* If we don’t open a color port, System 7’s PlotIconID will only draw B&W icons. */ } else { OpenPort ( &localPort ); /* The main screen (or Quickdraw) is B&W */ } /* We start with the assumption that we are drawing just the width of an icon */ largerOfTextOrIconWidth = kWidthOfIcon; widthOfText = GetPixelWidthOfTextCString ( extensionNameInCFormat ) + kTextCStringHorizontalBorderWhiteSpace; if ( widthOfText > kMaximumTotalPixelWidthOfAShowInit ) /* The absolute pixel width limit we’ll allow a filename to be */ { widthOfText = kMaximumTotalPixelWidthOfAShowInit; } if ( widthOfText > largerOfTextOrIconWidth ) { largerOfTextOrIconWidth = widthOfText; } /* Get the current horizontal and vertical location for drawing this init */ LMGetShowInitHorizontalAndVertical ( &horizontal, &vertical ); /* Knowing how much horizontal and vertical space we need, */ /* this makes sure the horizontal and vertical position won’t */ /* cause us to draw off the edge of the main screen. */ CorrectShowInitHorizontalAndVerticalIfNotOnMainScreen ( &horizontal, largerOfTextOrIconWidth, &vertical, kHeightOfIcon + kTextCStringHeight ); /* Where do we want to draw the icon? (assuming it has a regular width and height) */ iconDestinationRect.top = vertical; iconDestinationRect.bottom = iconDestinationRect.top + kHeightOfIcon; iconCenteringValue = ( ( largerOfTextOrIconWidth - kWidthOfIcon ) / 2 ); /* So we can base textCStringDestinationRect on a iconDestinationRect in case it is altered externally by force. */ iconDestinationRect.left = horizontal + iconCenteringValue; /* Center the icon horizontally */ iconDestinationRect.right = iconDestinationRect.left + kWidthOfIcon; if ( iconResourceID != kIconResourceIDNone ) /* Does the caller really want us to draw an icon? */ { DrawIcon ( iconResourceID, pixelDepthOfMainScreen, &iconDestinationRect ); } if ( extensionNameInCFormat != nil ) /* Is there a filename or message to display? */ { textCStringDestinationRect.top = iconDestinationRect.top + kHeightOfIcon; textCStringDestinationRect.bottom = textCStringDestinationRect.top + kTextCStringHeight; textCStringDestinationRect.left = iconDestinationRect.left - iconCenteringValue + ( ( largerOfTextOrIconWidth - widthOfText ) / 2 ); textCStringDestinationRect.right = textCStringDestinationRect.left + widthOfText - 1; /* Draw the filename or text message */ DrawTextCString ( extensionNameInCFormat, &textCStringDestinationRect ); if ( allDone == false ) { InvertRect ( &textCStringDestinationRect ); /* Hilite the text using invert. Note: Don’t use the hilite color, because text hilited with the hilite color appears to the user to be editable. */ } } if ( allDone != false && errorEncounteredIfAny != noErr ) { DrawCrossOut ( &iconDestinationRect, pixelDepthOfMainScreen ); /* We cross out the icon after the filename (or text message) has been drawn, */ /* because having the filename overwrite the cross out would look strange. */ if ( statusMessageInCFormat != nil ) { NotificationManagerMessage ( statusMessageInCFormat ); } } if ( pixelDepthOfMainScreen > 1 ) { CloseCPort ( &localCPort ); /* Make sure you close a color port using CloseCPort, just to be safe! */ } else { ClosePort ( &localPort ); } if ( allDone != false ) { /* Because of columns or not having enough room at the end of a row, we may have drawn in a different horizontal or vertical position than was actually stored, so update the storage to reflect our current drawing location. */ LMSetShowInitHorizontalAndVertical ( horizontal, vertical ); AdvanceShowInit ( largerOfTextOrIconWidth ); } } #ifndef powerc /* On 68K Macs, we started the routine by modifying a register. Let’s restore it. */ SetA5 ( originalA5Register ); #endif return ( result ); } OSErr ShowInitAndPascalName ( short iconResourceID, StringPtr extensionNameInPascalFormat, StringPtr statusMessageInPascalFormat, OSErr errorEncounteredIfAny, Boolean allDone ) { /* Accepts strings in Pascal format. Otherwise identical to ShowInitAndName. See ShowInitAndName for details */ char extensionNameInCFormat [ 255 + 1 ]; char statusMessageInCFormat [ 255 + 1 ]; /* Copy the strings to c-language format */ CopyCStringFromPascalStringWithLimit ( extensionNameInCFormat, extensionNameInPascalFormat, 255 ); CopyCStringFromPascalStringWithLimit ( statusMessageInCFormat, statusMessageInPascalFormat, 255 ); /* Pass into ShowInitAndName */ return ( ShowInitAndName ( iconResourceID, extensionNameInCFormat, statusMessageInCFormat, errorEncounteredIfAny, allDone ) ); } /******************************** STRING ROUTINES ***************************************/ unsigned long GetLengthOfCStringWithLimit ( char *pointerToCString, unsigned long countNoMoreThanThisManyCharacters ) { /* GetLengthOfCStringWithLimit can be used anywhere you need to get the length of a cstring. This function has a parameter which limits how many characters will be counted. The result is similar (but not identical) to: unsigned long finalLength; finalLength = strlen ( pointerToCString ); if ( finalLength > countNoMoreThanThisManyCharacters ) { finalLength = countNoMoreThanThisManyCharacters; } The difference is, if some idiot passes a Pascal string, or an unterminated c string, strlen will continue searching for the end of the string until it is lucky enough to run into a 0x00. This routine (which is slower), keeps track of the length, and stops when it reaches countNoMoreThanThisManyCharacters. Also, you don’t need to include any ANSI in your project to use this routine. Also, strlen ( nil ) bombs, but this doesn’t. char * pointerToCString; A c string which you want to know the length of. This is in c-language string format (string ends with 0x00). Pass nil to be returned a length of zero (no bombs!). unsigned long countNoMoreThanThisManyCharacters; A maximum limit to the length you’ll allow pointerToCString to be. returns an unsigned long, never greater than countNoMoreThanThisManyCharacters. * This routine does NOT move memory * See also: strlen in any ANSI library. 96/01/11 v1.0.0 David Cook First version */ unsigned long numberOfCharactersInCString; numberOfCharactersInCString = 0; if ( pointerToCString != nil ) { while ( numberOfCharactersInCString < countNoMoreThanThisManyCharacters && *pointerToCString != 0 ) { pointerToCString++; numberOfCharactersInCString++; } } return ( numberOfCharactersInCString ); } void CopyCStringFromPascalStringWithLimit ( char *pointerToDestinationCString, StringPtr pointerToSourcePascalString, Byte copyNoMoreThanThisManyCharacters ) { /* CopyCStringFromPascalStringWithLimit copies from a Pascal-language string format (byte length followed by characters) to a c-language string format (string ends with 0x00), converting as it copies. This has a parameter passed limit to the number of characters it is allowed to copy, at which point it properly ends the c string. char * pointerToDestinationCString; Space for a c string which you want copied from pointerToSourcePascalString. This is in c-language string format (string ends with 0x00). Make sure that the destination has enough space for 1 + copyNoMoreThanThisManyCharacters. char myBadCString [ 10 ]; CopyCStringFromPascalStringWithLimit ( myBadCString, "\p123456789012345", 10 ); NO! NO! NO! char myGoodCString [ 10 + 1 ]; CopyCStringFromPascalStringWithLimit ( myGoodCString, "\p123456789012345", 10 ); YES! YES! YES! Pass nil to do nothing (but no bombs!). StringPtr pointerToSourcePascalString; Original Pascal string which you want copied into pointerToDestinationCString. This string is NOT altered in any way by this routine. Pass nil to make an empty (0x00 terminated) pointerToDestinationCString. (no bombs!) unsigned long copyNoMoreThanThisManyCharacters; A maximum limit to the number of characters you’ll allow pointerToDestinationCString to contain. Always 1 less than allocated memory! Pass 0 to make an empty (0x00 terminated) pointerToDestinationCString. returns nothing. * This routine does NOT move memory * 96/01/11 v1.0.0 David Cook First version */ Byte numberOfCharactersRemainingToBeCopied; if ( pointerToDestinationCString != nil ) /* Is there a destination string to copy to? */ { if ( pointerToSourcePascalString != nil && copyNoMoreThanThisManyCharacters > 0 ) /* Is there a source string and do we want to copy any characters? */ { numberOfCharactersRemainingToBeCopied = (Byte) *pointerToSourcePascalString; pointerToSourcePascalString++; if ( numberOfCharactersRemainingToBeCopied > copyNoMoreThanThisManyCharacters ) { numberOfCharactersRemainingToBeCopied = copyNoMoreThanThisManyCharacters; } while ( *pointerToSourcePascalString != 0 && numberOfCharactersRemainingToBeCopied > 0 ) { *pointerToDestinationCString = *pointerToSourcePascalString; pointerToDestinationCString++; pointerToSourcePascalString++; numberOfCharactersRemainingToBeCopied--; } } *pointerToDestinationCString = 0; } } /******************************** NOTIFICATION MANAGER ***************************************/ OSErr NotificationManagerMessage ( char *messageInCFormat ) { /* A quick way to send a single c-format string message to the user. When you need to notify the user why the init failed to start up, don’t just show them a crossed out icon, send them message by the Notification Manager. Oh, by the way, a copy of the string message is PERMANENTLY allocated into the system heap so that the Notification Manager may deliver the message to the user after the Mac has finished starting up. After calling this routine, you may safely close your resource fork and exit your init without losing the message that has been sent by this routine (because the message is PERMANENTLY allocated elsewhere in the system heap.). WARNING: The memory for the message is only freed after the user restarts again. This should not pose a problem, because the message will be truncated to a maximum 255 bytes (+queueElement) and this routine is usually called only after a bad startup. char * messageInCFormat; A c-format string (usually a complete error message including filename of sender). This string is NOT altered in any way by this routine. Pass nil or an empty string to not send a message (but no bombs!). returns OSErr; noErr (0) No error dsOldSystem (102) Pre-System 6.0, so couldn’t call Notification manager memFullErr (-108) Couldn’t allocate memory for Notification manager call memPCErr (-114) Was told noErr by MemError, but the allocated pointer was nil *** This routine MOVES MEMORY *** 96/01/11 v1.0.0 David Cook First version */ unsigned long cStringSize; StringPtr pointerToDestinationPascalString; /* I’ve created a combined structure so that no matter what structure alignment method you compile with (68K or PPC), we know where the start of the string is. */ NMRecAndPascalStringCombinedRecord *notificationElementPtr; OSErr result; if ( GetSystemVersion ( ) < kNotificationManagerMinimumSystemRequired ) { result = dsOldSystem; } else if ( messageInCFormat == nil ) { result = memPCErr; } else { result = noErr; cStringSize = GetLengthOfCStringWithLimit ( messageInCFormat, 255 ); if ( cStringSize > 0 ) { notificationElementPtr = ( NMRecAndPascalStringCombinedRecord * ) NewPtrSysClear ( sizeof ( NMRecAndPascalStringCombinedRecord ) + cStringSize + 1 ); /* We’ve allocated the notificationElement and Pascal string together, */ /* because they are not going to be disposed of until the system is rebooted. */ /* It seems kinder to have a single Ptr hang around, than to have two. */ result = MemError ( ); if ( result == noErr ) { if ( notificationElementPtr == nil ) { result = memPCErr; /* MemError lied to us */ } else { notificationElementPtr->notificationRecord.qType = nmType; notificationElementPtr->notificationRecord.nmMark = 0; notificationElementPtr->notificationRecord.nmIcon = nil; notificationElementPtr->notificationRecord.nmSound = (Handle) -1; notificationElementPtr->notificationRecord.nmStr = (StringPtr) &(notificationElementPtr->notificationPascalString); notificationElementPtr->notificationRecord.nmResp = (NMUPP) -1; notificationElementPtr->notificationRecord.nmRefCon = kNotificationSentByShowInitAndName; pointerToDestinationPascalString = notificationElementPtr->notificationRecord.nmStr; *pointerToDestinationPascalString = cStringSize; pointerToDestinationPascalString++; while ( cStringSize > 0 ) { *pointerToDestinationPascalString = *messageInCFormat; pointerToDestinationPascalString++; messageInCFormat++; cStringSize--; } result = NMInstall ( &(notificationElementPtr->notificationRecord) ); if ( result != noErr ) { DisposePtr ( (Ptr) notificationElementPtr ); } } } } } return ( result ); } /******************************** SYSTEM CHECKS ***************************************/ Boolean IsColorQuickdrawAvailable ( void ) { /* IsColorQuickdrawAvailable returns true if Color Quickdraw exists on this Mac, false if it does not. */ /* 96/01/11 v1.0.0 David Cook First version */ Boolean doesColorQuickdrawExist; long quickdrawVersionAnswer; doesColorQuickdrawExist = false; if ( Gestalt ( gestaltQuickdrawVersion, &quickdrawVersionAnswer ) == noErr ) { if ( quickdrawVersionAnswer >= gestalt8BitQD ) { doesColorQuickdrawExist = true; } } return ( doesColorQuickdrawExist ); } long GetSystemVersion ( void ) { /* GetSystemVersion returns returns the System Software version present. i.e. 0x0700 for System 7, */ /* 0x0605 for system 6.0.5. You’re not supposed to rely on system versions to determine features - yah yah yah */ /* 96/01/11 v1.0.0 David Cook First version */ long currentSystemVersion; Gestalt ( gestaltSystemVersion, ¤tSystemVersion ); return ( currentSystemVersion ); } long GetMainScreenPixelDepth ( void ) { /* Don’t use this function for everyday application usage. This only tells the depth of the MAIN screen, which is fine for inits, but not for REAL programs. */ /* This returns the depth (i.e. 2 to the power of depth = number of colors) of the main screen. */ /* 1 = Black and white */ /* 2 = Four colors */ /* 4 = Sixteen colors */ /* 8 = 256 colors */ /* 16 = Thousands of colors */ /* 32 = Millions of colors */ /* Note: The actual monitor itself could be gray scale, not color. This routine doesn’t check for that information - and shouldn’t. */ /* 96/01/11 v1.0.0 David Cook First version */ long mainScreenPixelDepth; GDHandle mainDevicePtr; mainScreenPixelDepth = 1; /* B&W */ if ( IsColorQuickdrawAvailable ( ) != false ) { mainDevicePtr = GetMainDevice ( ); if ( mainDevicePtr != nil ) { mainScreenPixelDepth = (*(*mainDevicePtr)->gdPMap)->pixelSize; } } return ( mainScreenPixelDepth ); } void GetMainScreenRect ( Rect *mainScreenRectPtr ) { /* Don’t use this function for everyday application usage. This only tells the size of the MAIN screen, which is fine for inits, but not for REAL programs. */ /* This routine returns the main screen’s boundary rectangle. */ /* You can pass a nil handle for mainScreenRectPtr and not cause a crash. */ /* 96/01/11 v1.0.0 David Cook First version */ GDHandle mainDevicePtr; if ( mainScreenRectPtr != nil ) { mainScreenRectPtr->top = 0; mainScreenRectPtr->bottom = 342; mainScreenRectPtr->left = 0; mainScreenRectPtr->right = 512; /* Hard coded data is wrong on the Lisa. But, I couldn’t rely of qd.screenBits.bounds as initialized */ if ( IsColorQuickdrawAvailable ( ) != false ) { mainDevicePtr = GetMainDevice ( ); if ( mainDevicePtr != nil ) { *mainScreenRectPtr = ((**((*mainDevicePtr)->gdPMap)).bounds); } } } } Boolean IsPlotIconIDAvailable ( void ) { /* 96/01/11 v1.0.0 David Cook First version */ Boolean doesPlotIconIDExist; doesPlotIconIDExist = false; if ( GetSystemVersion ( ) >= 0x0700 ) /* Well, it seems true. */ { doesPlotIconIDExist = true; } return ( doesPlotIconIDExist ); } /******************************** ICON AND CROSS OUT ROUTINES ***************************************/ OSErr DrawIcon ( short iconResourceID, short pixelDepthOfScreenToDrawOn, Rect *pointerToDestinationRect ) { /* If the pixelDepthOfScreenToDrawOn > 2 (i.e. more than 4 colors), this routine will */ /* get and draw the icl8, icl4, cicn, OR ICN# (in that order) specified by iconResourceID */ /* in the native size (usually 32x32), at the position specified. */ /* If the pixelDepthOfScreenToDrawOn <= 2 (i.e. 4 colors or black&white), this */ /* routine will get and draw the ICN# specified by iconResourceID */ /* in the native size (usually 32x32), at the position specified. */ /* If System 7 or better is installed, and an icl8, icl4, OR icn# is available, */ /* PlotIconID will be called instead of using the rest of this function. */ /* Note: This routine tries to find a 'cicn' ONLY if the pixelDepthOfScreenToDrawOn > 2 */ /* and neither an 'icl8' or 'icl4' of iconResourceID can be found. */ /* If any of the icons are NOT purgable, they will be automatically purged when the */ /* init’s resource fork is closed. This routine does NOT release the resource handles. */ /* 96/01/11 v1.0.0 David Cook First version */ Rect iconSourceRect; CIconHandle colorIconHandle; /* 'cicn' color icon handle */ Handle iclHandle; /* 'icl8' or 'icl4' color icon handle */ Handle icnHandle; /* 'ICN#' black&white icon handle */ PixMap iclPixMap; /* Used for drawing 'icl8' or 'icl4' */ BitMap icnBitMap; /* Used for drawing 'ICN#'s */ GrafPtr localPortPtr; char originaliclHandleState; char originalICNHandleState; short iclPixelDepth; OSErr result; if ( pointerToDestinationRect == nil ) { result = memPCErr; } else { result = noErr; colorIconHandle = nil; iclHandle = nil; if ( pixelDepthOfScreenToDrawOn >= kMinimumPixelDepthToDrawColorIcons && IsColorQuickdrawAvailable ( ) != false ) /* Just checking to be safe! */ { if ( pixelDepthOfScreenToDrawOn >= 8 ) { iclHandle = GetResource ('icl8', iconResourceID ); } if ( iclHandle != nil ) { iclPixelDepth = 8; /* An 'icl8' was successfully found */ } else /* Either an 'icl8' wasn’t found or pixelDepthOfScreenToDrawOn < 8 */ { iclHandle = GetResource ('icl4', iconResourceID ); if ( iclHandle != nil ) /* An 'icl4' was successfully found */ { iclPixelDepth = 4; /* An 'icl8' was successfully found */ } else /* We haven’t found an 'icl8' or 'icl4' yet */ { if ( pixelDepthOfScreenToDrawOn < 8 ) /* We haven’t tried looking for an 'icl8' yet */ { iclHandle = GetResource ('icl8', iconResourceID ); } if ( iclHandle != nil ) { iclPixelDepth = 8; } else /* We’ve tried looking for an 'icl8' and an 'icl4' and the screen is color */ { colorIconHandle = GetCIcon ( iconResourceID ); } } } } if ( colorIconHandle != nil ) /* This can only be true if we couldn’t find an 'icl8' or an 'icl4' and the screen is color */ { PlotCIcon ( pointerToDestinationRect, colorIconHandle ); DisposCIcon ( colorIconHandle ); colorIconHandle = nil; /* Its a good habit to nil out local handles so you won’t mistakenly use them again and sometimes have it work. */ } else { if ( IsPlotIconIDAvailable ( ) != false ) { result = PlotIconID ( pointerToDestinationRect, atNone, ttNone, iconResourceID ); } else { if ( iclHandle != nil ) /* If we have an 'icl8' or 'icl4' handle, lets make sure it doesn’t get purged before we use it */ { originaliclHandleState = HGetState ( iclHandle ); HNoPurge ( iclHandle ); } GetPort ( &localPortPtr ); if ( localPortPtr == nil ) { result = nilHandleErr; } else { icnHandle = GetResource ('ICN#', iconResourceID ); result = ResError ( ); if ( result == noErr ) { if ( icnHandle == nil ) { result = resNotFound; /* We were lied to by ResError - this does happen! */ } else { originalICNHandleState = HGetState ( icnHandle ); HNoPurge ( icnHandle ); iconSourceRect.left = 0; iconSourceRect.right = kWidthOfIcon; iconSourceRect.top = 0; iconSourceRect.bottom = kHeightOfIcon; if ( iclHandle != nil ) { HLock ( iclHandle ); iclPixMap.baseAddr = *iclHandle; iclPixMap.rowBytes = (((iconSourceRect.right - iconSourceRect.left) * iclPixelDepth) / 8) | 0x8000; iclPixMap.bounds = iconSourceRect; iclPixMap.pmVersion = 0; iclPixMap.packType = 0; iclPixMap.packSize = 0; iclPixMap.hRes = 72; iclPixMap.vRes = 72; iclPixMap.pixelType = 0; iclPixMap.pixelSize = iclPixelDepth; iclPixMap.cmpCount = 1; iclPixMap.cmpSize = iclPixelDepth; iclPixMap.planeBytes = 0; iclPixMap.pmTable = GetCTable ( iclPixelDepth ); iclPixMap.pmReserved = 0; if ( iclPixMap.pmTable == nil ) /* Bummer! We were so close! */ { HSetState ( iclHandle, originaliclHandleState ); iclHandle = nil; } } HLock ( icnHandle ); icnBitMap.rowBytes = kBitmapRowBytesForBWIcon; icnBitMap.bounds = iconSourceRect; icnBitMap.baseAddr = *icnHandle + kByteOffsetToICNMask; /* punch hole with mask */ CopyBits ( &icnBitMap, &(localPortPtr->portBits), &iconSourceRect, pointerToDestinationRect, srcBic, nil ); if ( iclHandle != nil ) /* We have color */ { CopyBits ( (BitMap *) &iclPixMap, &(localPortPtr->portBits), &iconSourceRect, pointerToDestinationRect, srcOr, nil ); DisposeCTable ( iclPixMap.pmTable ); iclPixMap.pmTable = nil; } else /* We don’t have color */ { icnBitMap.baseAddr = *icnHandle; /* now draw the icon */ CopyBits ( &icnBitMap, &(localPortPtr->portBits), &iconSourceRect, pointerToDestinationRect, srcOr, nil ); } HSetState ( icnHandle, originalICNHandleState ); } } } if ( iclHandle != nil ) { HSetState ( iclHandle, originaliclHandleState ); } } } } return ( result ); } void DrawCrossOut ( Rect *pointerToDestinationRect, short pixelDepthOfScreenToDrawOn ) { /* If we have a pointerToDestinationRect and is it NOT infinitely thin, this routine draws an 'X' (cross out) over the destination rect */ /* If pixelDepthOfScreenToDrawOn is not B&W (even if it is just 4 colors), the cross out will be attempted in color. */ /* 96/01/11 v1.0.0 David Cook First version */ RGBColor workingRGBCrossOutColor; Pattern workingCrossOutPattern; short widthOfDestinationRect; short heightOfDestinationRect; if ( pointerToDestinationRect != nil ) /* Do we have a destinationRect? */ { widthOfDestinationRect = pointerToDestinationRect->right - pointerToDestinationRect->left; heightOfDestinationRect = pointerToDestinationRect->bottom - pointerToDestinationRect->top; if ( widthOfDestinationRect > 0 && heightOfDestinationRect > 0 ) /* Is the destinationRect NOT infinitely thin? */ { if ( pixelDepthOfScreenToDrawOn > 1 ) { workingRGBCrossOutColor.red = 0; workingRGBCrossOutColor.green = 0; workingRGBCrossOutColor.blue = 0; RGBForeColor ( &workingRGBCrossOutColor ); } MoveTo ( pointerToDestinationRect->left + 2, pointerToDestinationRect->top ); Line ( widthOfDestinationRect - 3, heightOfDestinationRect - 3 ); Line ( -2, 2 ); Line ( -widthOfDestinationRect + 3, -heightOfDestinationRect + 3 ); Line ( 2, -2 ); MoveTo ( pointerToDestinationRect->right - 3, pointerToDestinationRect->top ); Line ( 2, 2 ); Line ( -widthOfDestinationRect + 3, heightOfDestinationRect - 3 ); Line ( -2, -2 ); Line ( widthOfDestinationRect - 3, - heightOfDestinationRect + 3 ); if ( pixelDepthOfScreenToDrawOn > 1 ) { workingRGBCrossOutColor.red = 65535; workingRGBCrossOutColor.green = 30000; workingRGBCrossOutColor.blue = 30000; RGBForeColor ( &workingRGBCrossOutColor ); } MoveTo ( pointerToDestinationRect->left + 2, pointerToDestinationRect->top + 1 ); Line ( -1, 1 ); Line ( widthOfDestinationRect - 5, heightOfDestinationRect - 5 ); MoveTo ( pointerToDestinationRect->right - 3, pointerToDestinationRect->top + 1 ); Line ( -widthOfDestinationRect + 4, heightOfDestinationRect - 4 ); if ( pixelDepthOfScreenToDrawOn > 1 ) { workingRGBCrossOutColor.red = 40000; workingRGBCrossOutColor.green = 0; workingRGBCrossOutColor.blue = 0; RGBForeColor ( &workingRGBCrossOutColor ); } Move ( 1, 1 ); Line ( widthOfDestinationRect - 4, -heightOfDestinationRect + 4 ); MoveTo ( pointerToDestinationRect->left + 3, pointerToDestinationRect->top + 2 ); Line ( widthOfDestinationRect - 5, heightOfDestinationRect - 5 ); Line ( -1, 1 ); if ( pixelDepthOfScreenToDrawOn > 1 ) { workingRGBCrossOutColor.red = 60000; workingRGBCrossOutColor.green = 0; workingRGBCrossOutColor.blue = 0; RGBForeColor ( &workingRGBCrossOutColor ); } else { workingCrossOutPattern.pat [ 0 ] = 0; workingCrossOutPattern.pat [ 1 ] = 0; workingCrossOutPattern.pat [ 2 ] = 0; workingCrossOutPattern.pat [ 3 ] = 0; workingCrossOutPattern.pat [ 4 ] = 0; workingCrossOutPattern.pat [ 5 ] = 0; workingCrossOutPattern.pat [ 6 ] = 0; workingCrossOutPattern.pat [ 7 ] = 0; PenPat ( &workingCrossOutPattern ); } MoveTo ( pointerToDestinationRect->left + 2, pointerToDestinationRect->top + 2 ); Line ( widthOfDestinationRect - 5, heightOfDestinationRect - 5 ); MoveTo ( pointerToDestinationRect->right - 3, pointerToDestinationRect->top + 2 ); Line ( -widthOfDestinationRect + 5, heightOfDestinationRect - 5 ); /* Set colors and pens back to black */ if ( pixelDepthOfScreenToDrawOn > 1 ) { workingRGBCrossOutColor.red = 0; workingRGBCrossOutColor.green = 0; workingRGBCrossOutColor.blue = 0; RGBForeColor ( &workingRGBCrossOutColor ); } else { /* If this routine is to stand alone, we can’t rely on the value of Quickdraw globals! */ workingCrossOutPattern.pat [ 0 ] = 0xFF; workingCrossOutPattern.pat [ 1 ] = 0xFF; workingCrossOutPattern.pat [ 2 ] = 0xFF; workingCrossOutPattern.pat [ 3 ] = 0xFF; workingCrossOutPattern.pat [ 4 ] = 0xFF; workingCrossOutPattern.pat [ 5 ] = 0xFF; workingCrossOutPattern.pat [ 6 ] = 0xFF; workingCrossOutPattern.pat [ 7 ] = 0xFF; PenPat ( &workingCrossOutPattern ); } } } } /******************************** TEXT ROUTINES ***************************************/ void DrawTextCString ( char *pointerToTextCString, Rect *pointerToDestinationRect ) { /* WARNING: Do NOT use this routine unless you have a port initialized! */ /* This draws a c string (i.e. '0' terminated), in the specified style, in the specified destinationRect. */ /* Passing nil for pointerToTextCString or pointerToDestinationRect will NOT result in a crash. */ /* 96/01/11 v1.0.0 David Cook First version */ unsigned long numberOfCharactersInTextCString; if ( pointerToTextCString != nil && *pointerToTextCString != 0 /* Make sure the c string exists and is not empty. */ && pointerToDestinationRect != nil ) /* Make sure destination rect exists. */ { TextFont ( kTextCStringFont ); TextSize ( kTextCStringFontSize ); TextFace ( kTextCStringFace ); TextMode ( srcCopy ); numberOfCharactersInTextCString = GetLengthOfCStringWithLimit ( pointerToTextCString, kTextCStringMaximumNumberOfCharacters ); TextBox ( pointerToTextCString, numberOfCharactersInTextCString, pointerToDestinationRect, kTextCStringAlignment ); } } short GetPixelWidthOfTextCString ( char *pointerToTextCString ) { /* WARNING: Do NOT use this routine unless you have a port initialized! */ /* This sets the font and style appropriately, and then calculates the width of pointerToTextCString in pixels. */ /* It does NOT restore the font and style to what it was before. */ /* 96/01/11 v1.0.0 David Cook First version */ unsigned long numberOfCharactersInTextCString; short textCStringWidthInPixels; textCStringWidthInPixels = 0; if ( pointerToTextCString != nil && *pointerToTextCString != 0 ) /* Make sure the c string exists and is not empty. */ { TextFont ( kTextCStringFont ); TextSize ( kTextCStringFontSize ); TextFace ( kTextCStringFace ); numberOfCharactersInTextCString = GetLengthOfCStringWithLimit ( pointerToTextCString, kTextCStringMaximumNumberOfCharacters ); textCStringWidthInPixels = TextWidth ( pointerToTextCString, 0, numberOfCharactersInTextCString ); } return ( textCStringWidthInPixels ); } /******************************** INIT ICON POSITIONING ROUTINES ***************************************/ void AdvanceShowInit ( short byHorizontalAmount ) { /* This routine advances the horizontal drawing position by the amount specified. */ /* Note: This does not move to an absolute position, but instead moves relative to */ /* the current location. */ /* 96/01/11 v1.0.0 David Cook First version */ short horizontal, vertical; if ( byHorizontalAmount != 0 ) { LMGetShowInitHorizontalAndVertical ( &horizontal, &vertical ); horizontal += byHorizontalAmount + kHorizontalMarginBetweenShowInits; CorrectShowInitHorizontalAndVerticalIfNotOnMainScreen ( &horizontal, kWidthOfIcon, &vertical, kHeightOfIcon + kTextCStringHeight ); /* Be good to the next icon being drawn and make sure they are on the screen and in the proper vertical position. */ /* This is also useful to nudge older algorithms that may not go high enough to avoid chopping off the top of our drawing. */ LMSetShowInitHorizontalAndVertical ( horizontal, vertical ); } } Boolean CorrectShowInitHorizontalAndVerticalIfNotOnMainScreen ( short *pointerToHorizontal, short horizontalSizeOfThisShowInit, short *pointerToVertical, short verticalSizeOfThisShowInit ) { /* This routine makes sure that an init can display horizontalSizeOfThisShowInit pixels horizontally, */ /* and verticalSizeOfThisShowInit pixels vertically, and still be within the margins of the main screen. */ /* Don’t add the standard kHorizontalMarginBetweenShowInits or kVerticalMarginBetweenShowInits to the */ /* sizes, that will be done automatically if necessary. */ /* If the drawing won’t fit within the horizontal screen width - margins, the vertical is advanced up */ /* a row up on the screen and the horizontal is reset to the beginning of the row + margin. */ /* If the init is at the top of the main screen, but still can’t fit, it */ /* is moved completely off the top of the screen so that it won’t drag its image partially over. You */ /* can’t simply move it to the next monitor horizontally, because this algorithm always resets the */ /* horizontal back onto the main monitor and then moves the vertical up. */ /* Notice that you can pass nil for pointerToHorizontal and pointerToVertical without bombing. */ /* 96/01/11 v1.0.0 David Cook First version */ Rect mainScreenRect; Boolean aCorrectionWasMade; if ( verticalSizeOfThisShowInit < kMinimumVerticalSizeOfShowInit ) { verticalSizeOfThisShowInit = kMinimumVerticalSizeOfShowInit; } GetMainScreenRect ( &mainScreenRect ); mainScreenRect.left += kShowInitMarginForMainScreenLeft; mainScreenRect.right -= kShowInitMarginForMainScreenRight; /* mainScreenRect.top is a special case */ mainScreenRect.bottom -= kShowInitMarginForMainScreenBottom; aCorrectionWasMade = false; if ( pointerToHorizontal != nil ) { if ( *pointerToHorizontal + horizontalSizeOfThisShowInit > mainScreenRect.right ) { *pointerToHorizontal = mainScreenRect.left; if ( pointerToVertical != nil ) { *pointerToVertical -= (verticalSizeOfThisShowInit + kVerticalMarginBetweenShowInits); } aCorrectionWasMade = true; } else if ( *pointerToHorizontal < mainScreenRect.left ) { *pointerToHorizontal = mainScreenRect.left; aCorrectionWasMade = true; } } if ( pointerToVertical != nil ) { if ( *pointerToVertical + verticalSizeOfThisShowInit > mainScreenRect.bottom ) { *pointerToVertical = mainScreenRect.bottom - verticalSizeOfThisShowInit; aCorrectionWasMade = true; } else if ( *pointerToVertical < mainScreenRect.top + kShowInitMarginForMainScreenTop && *pointerToVertical + verticalSizeOfThisShowInit > mainScreenRect.top - kShowInitMarginForMainScreenBottom ) /* If our top is over the top margin of the screen, but our bottom is still partially on the main screen, take us all the way over the top of the screen */ { *pointerToVertical = mainScreenRect.top - verticalSizeOfThisShowInit - kShowInitMarginForMainScreenBottom; /* Note: LMGetMBarHeight isn’t of value to us at startup, because there isn’t a menu bar yet. */ aCorrectionWasMade = true; } } return ( aCorrectionWasMade ); } void LMGetShowInitHorizontalAndVertical ( short *pointerToHorizontal, short *pointerToVertical ) { /* This routine gets the horizontal and vertical position for drawing the current init. */ /* Note: You can pass a nil pointer in either of both parameters without causing a crash. */ /* 96/01/11 v1.0.0 David Cook First version */ StringPtr localCurApNamePtr; short storedHorizontal, storedVertical, storedChecksum; Rect mainScreenRect; GetMainScreenRect ( &mainScreenRect ); localCurApNamePtr = LMGetCurApName ( ); /* This won’t work correctly if LMGetCurApName returns a pointer to a copy of CurApName which has not been BlockMoved. */ /* (i.e. uses a Pascal string copy routine which fails to copy the normally “unused” bytes that follow a shorter filename.) */ storedHorizontal = *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredHorizontal) ); storedChecksum = *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredHorizontalChecksum) ); if ( storedChecksum != CalculateHorizontalOrVerticalStorageChecksum ( storedHorizontal ) ) { /* Since the checksum is NOT correct, we must be the very first init to be displayed. */ /* So, replace the incorrect horizontal position with the standard first horizontal position. */ storedHorizontal = mainScreenRect.left + kDefaultHorizontalOffsetFromLeftOfMainScreenIfHorizontalChecksumIsWrong; } storedVertical = *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredVertical) ); storedChecksum = *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredVerticalChecksum) ); if ( storedChecksum != CalculateHorizontalOrVerticalStorageChecksum ( storedVertical ) ) { /* Since the checksum is NOT correct, we must be the very first init to be displayed (or use this method). */ /* So, replace the incorrect vertical position with the standard first vertical position. */ storedVertical = mainScreenRect.bottom - kDefaultVerticalOffsetFromBottomOfMainScreenIfVerticalChecksumIsWrong; } if ( pointerToHorizontal != nil ) { *pointerToHorizontal = storedHorizontal; } if ( pointerToVertical != nil ) { *pointerToVertical = storedVertical; } } void LMSetShowInitHorizontalAndVertical ( short horizontal, short vertical ) { /* The routine stores the init’s horizontal and vertical position. */ /* (And updates the associated checksum.) */ /* This routine does not advance the position, so if you don’t */ /* adjust the horizontal and vertical after you’re completely finished, */ /* the next init will draw over yours. */ /* 96/01/11 v1.0.0 David Cook First version */ StringPtr localCurApNamePtr; localCurApNamePtr = LMGetCurApName ( ); /* This won’t work correctly if LMGetCurApName returns a pointer to a copy of CurApName which has not been BlockMoved. */ /* (i.e. uses a Pascal string copy routine which fails to copy the normally “unused” bytes that follow a shorter filename.) */ *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredHorizontal) ) = horizontal; *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredHorizontalChecksum) ) = CalculateHorizontalOrVerticalStorageChecksum ( horizontal ); *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredVertical) ) = vertical; *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredVerticalChecksum) ) = CalculateHorizontalOrVerticalStorageChecksum ( vertical ); LMSetCurApName ( localCurApNamePtr ); /* This might be necessary in future systems */ /* This won’t work correctly if LMGetCurApName returns a pointer to a copy of CurApName, and LMSetCurApName doesn’t BlockMove it back. */ /* (i.e. uses a Pascal string copy routine which fails to copy the normally “unused” bytes that follow a shorter filename.) */ } short CalculateHorizontalOrVerticalStorageChecksum ( short horizontalOrVerticalValue ) { /* Given the init’s horizontal or vertical position, this routine calculates and returns */ /* a checksum using the classic mathematical formula from the original ShowInit.a. */ /* 96/01/11 v1.0.0 David Cook First version */ short storageChecksum; if ( ( horizontalOrVerticalValue & 0x8000 ) != 0 ) { storageChecksum = ( ( horizontalOrVerticalValue << 1 ) | 1 ) ^ kChecksumStorageConstant; } else { storageChecksum = ( horizontalOrVerticalValue << 1 ) ^ kChecksumStorageConstant; } return ( storageChecksum ); }