CUtils.cp

/*
    Random and sundry error, file and path name utilities
    
    Created 29 Jan 1996 by EGH
    
    Copyright © 1996, Apple Computer, Inc. All rights reserved.
*/
 
#include <FixMath.h>
#include <fp.h>
 
#include <stdio.h>
#include <String_Utils.h>
 
#include "CApp.h"
 
#include "CUtils.h"
 
/* GetFullPathName
 
    Get the full path name of the passed file, but force its length to fit into maxSize.
    It is obviously more desirable to not restrict its length, but in this case we desire
    this and "elipsize" the string by removing characters from the center.
*/
void GetFullPathName(
    const FSSpec *fileSpec,
    Str255 outPathName,
    short maxSize)
{
        // force an upper limit to what will fit in a Str255
    if (maxSize > sizeof (Str255) - 1)
        maxSize = sizeof (Str255) - 1;
    
    Int16 pathLen;
    Handle pathH;
    OSErr err = FSpGetFullPath(fileSpec, &pathLen, &pathH);
    if (err == noErr)
    {
        ElipsedPathNameH(pathH, maxSize, outPathName, 'É');
        ::DisposeHandle(pathH);
    }
    else
    {
            // no memory (?!) so just copy the file's name
        CopyPStr(fileSpec->name, outPathName);
    }
}
 
/* FSpGetFullPath
 
    Grok the full the path name of the passed file.
    Nabbed from More Files 1.4.1 from my colleagues at Apple Developer Technical Support.
*/
OSErr FSpGetFullPath(
    const FSSpec *spec,
    short *fullPathLength,
    Handle *fullPath)
{
    OSErr       result;
    FSSpec      tempSpec;
    CInfoPBRec  pb;
    
    /* Make a copy of the input FSSpec that can be modified */
    BlockMoveData(spec, &tempSpec, sizeof(FSSpec));
    
    if ( tempSpec.parID == fsRtParID )
    {
        /* The object is a volume */
        
        /* Add a colon to make it a full pathname */
        ++tempSpec.name[0];
        tempSpec.name[tempSpec.name[0]] = ':';
        
        /* We're done */
        result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
    }
    else
    {
        /* The object isn't a volume */
        
        /* Put the object name in first */
        result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
        if ( result == noErr )
        {
            /* Get the ancestor directory names */
            pb.dirInfo.ioNamePtr = tempSpec.name;
            pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
            pb.dirInfo.ioDrParID = tempSpec.parID;
            do  /* loop until we have an error or find the root directory */
            {
                pb.dirInfo.ioFDirIndex = -1;
                pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
                result = PBGetCatInfoSync(&pb);
                if ( result == noErr )
                {
                    /* Append colon to directory name */
                    ++tempSpec.name[0];
                    tempSpec.name[tempSpec.name[0]] = ':';
                    
                    /* Add directory name to beginning of fullPath */
                    (void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], tempSpec.name[0]);
                    result = MemError();
                }
            } while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) );
        }
    }
    if ( result == noErr )
    {
        /* Return the length */
        *fullPathLength = GetHandleSize(*fullPath);
    }
    else
    {
        /* Dispose of the handle and return NULL and zero length */
        DisposeHandle(*fullPath);
        *fullPath = NULL;
        *fullPathLength = 0;
    }
    
    return result;
}
 
 
/* ElipsedPathNameH
    
    Gracefully degrade the passed string by removing characters from the center
    until it fits into the passed maximum width. Inserts an elipsis character to indicate this.
*/
void ElipsedPathNameH(
    Handle pathNameH,
    short maxWidth,
    Str255 elipsedName,
    char elipsis)
{
    long pNameLen = GetHandleSize(pathNameH);
    char saveState = HGetState(pathNameH);
    Ptr pathNameP = *pathNameH;
 
    HLock(pathNameH);
 
    if ((pNameLen <= 255) && (TextWidth(pathNameP, 0, (short)pNameLen) <= maxWidth))
    {
        BlockMove(pathNameP, (Ptr)(elipsedName + 1), pNameLen);
        *elipsedName = (char)pNameLen;
    }
    else
    {
        long i, j, width = CharWidth(elipsis);
        char rightChars[128];
 
        maxWidth /= 2;
 
        for (i = 1; (i < pNameLen) && (i < 128); i++)
        {
            char nextChar = pathNameP[i - 1];
 
            width += CharWidth(nextChar);
            if (width >= maxWidth) break;
            elipsedName[i] = nextChar;
        }
 
        width = 0;
        for (j = pNameLen - 1, *rightChars = 0; (j > i) && (*rightChars < 128); j--)
        {
            char nextChar = pathNameP[j];
 
            width += CharWidth(nextChar);
            if (width >= maxWidth) break;
            rightChars[++*rightChars] = nextChar;
        }
 
        elipsedName[(*elipsedName = (char)i)] = elipsis;
 
        while (*rightChars)
        {
            elipsedName[++*elipsedName] = rightChars[(*rightChars)--];
        }
    }
 
    HSetState(pathNameH, saveState);
}
 
 
/* ElipsedPathName
 
    Same as ElipsedPathNameH except uses Str255's rather than a handle.
*/
void ElipsedPathName(
    Str255 pathName,
    short maxWidth,
    Str255 elipsedName,
    char elipsis)
{
    Int16 pNameLen = pathName[0];
    UInt8 *pathNameP = &pathName[1];
    Int16 textWidth;
    
    textWidth = ::StringWidth(pathName);
    if ((pNameLen <= 255) && (textWidth <= maxWidth))
    {
        BlockMove(pathNameP, (Ptr)(elipsedName + 1), pNameLen);
        *elipsedName = (char)pNameLen;
    }
    else
    {
        long i, j, width = CharWidth(elipsis);
        char rightChars[128];
 
        maxWidth /= 2;
 
        for (i = 1; (i < pNameLen) && (i < 128); i++)
        {
            char nextChar = pathNameP[i - 1];
 
            width += CharWidth(nextChar);
            if (width >= maxWidth) break;
            elipsedName[i] = nextChar;
        }
 
        width = 0;
        for (j = pNameLen - 1, *rightChars = 0; (j > i) && (*rightChars < 128); j--)
        {
            char nextChar = pathNameP[j];
 
            width += CharWidth(nextChar);
            if (width >= maxWidth) break;
            rightChars[++*rightChars] = nextChar;
        }
 
        elipsedName[(*elipsedName = (char)i)] = elipsis;
 
        while (*rightChars)
        {
            elipsedName[++*elipsedName] = rightChars[(*rightChars)--];
        }
    }
}
 
 
/* SetSizedDescriptor
    
    Set the descriptor of the passed pane, but gracefully degrade the
    length of the string to physically fit into the pane's frame.
*/
void SetSizedDescriptor(
    LWindow *parent,
    PaneIDT inPaneID,
    StringPtr inDescStr)
{
    LCaption *pane = (LCaption *)parent->FindPaneByID(inPaneID);
    pane->FocusDraw(); // just to be in a valid port
    ResIDT texttraitsID = pane->GetTextTraitsID();
    UTextTraits::SetPortTextTraits(texttraitsID);
    SDimension16 panesize;
    pane->GetFrameSize(panesize);
    Str255 elipStr;
    ::ElipsedPathName(inDescStr, panesize.width, elipStr, 'É');
    pane->SetDescriptor(elipStr);
}
 
 
/* ReportError
 
    Report an error to the user.
*/
void ReportError(
    ExceptionCode inErr,
    Int16 inStrIndex)
{
    Str255
        errStr = "\pAn error occurred.",
        errNumStr,
        errDescStr;
    Int16 descIndex;
    
    gApp->BugUserTilSwitchedIn();
    
        // make some attempt to provide a description of the error
    switch (inErr)
    {
        case iMemFullErr:
            descIndex = err_NotEnufMemory;
            break;
            
        case notEnoughMemoryErr:
            descIndex = err_NotEnufPhysicalMemory;
            break;
        
        case dirFulErr:
        case dskFulErr:
            descIndex = err_DiskFull;
            break;
        
        case ioErr:
        case fnOpnErr:
        case eofErr:
        case fnfErr:
        case nsvErr:
            descIndex = err_DiskError;
            break;
        
        case resNotFound:
            descIndex = err_NoResource;
        
        default:
            descIndex = err_NoDescription;
    }
    ::GetIndString(errDescStr, STRx_ErrorDescs, descIndex);
    
    ::SetCursor(&qd.arrow);
    
    ::GetIndString(errStr, STRx_Errors, inStrIndex);
    ::NumToString(inErr, errNumStr);
    ::ParamText(errStr, errNumStr, errDescStr, "\p");
    ::StopAlert(ALRT_Error, nil);
}
 
 
OSErr GetFileParent(
    FSSpec *fileSpec,
    FSSpec *parentSpec)
{
    CInfoPBRec pBlock;
    OSErr result;
    
    pBlock.dirInfo.ioVRefNum = fileSpec-> vRefNum;
    pBlock.dirInfo.ioDrDirID = fileSpec-> parID;
    pBlock.dirInfo.ioNamePtr = parentSpec-> name;
    parentSpec-> name[0] = 0;
    pBlock.dirInfo.ioFDirIndex = -1;
     
    result = PBGetCatInfoSync(&pBlock);
    
    if (result == noErr)
    {
        parentSpec-> vRefNum = fileSpec-> vRefNum;
        parentSpec-> parID = pBlock.dirInfo.ioDrParID;
    }
    
    return result;
}
 
 
const Int32 kPictFileHeaderSize = 512;
 
#pragma options align=mac68k
 
typedef struct PICT2Xheader {
    short   version;
    short   reserved;
    Fixed   hRes;
    Fixed   vRes;
    Rect    srcRect;
    long    reserved2;
} PICT2Xheader;
 
#pragma options align=reset
 
/* ReadPictFrame
 
    Read a pict's rectangle.
*/
void ReadPictFrame(
    const FSSpec &inPictSpec,
    Rect &outPictRect)
{
    LFile pictFile(inPictSpec);
    
    pictFile.OpenDataFork(fsRdPerm); // LFile will close the file upon failure
    
        // picture begins past the header
    OSErr result = ::SetFPos(pictFile.GetDataForkRefNum(), fsFromStart, kPictFileHeaderSize);
    ThrowIfOSErr_(result);
    
        // read a PICT2Xheader structure at 16 bytes into the pict
    char pict[sizeof (PICT2Xheader) + 16];
    Int32 count = sizeof (PICT2Xheader) + 16;
    result = ::FSRead(pictFile.GetDataForkRefNum(), &count, &pict);
    ThrowIfOSErr_(result);
    
    PICT2Xheader *pic2XHeader;
    pic2XHeader = (PICT2Xheader *)&(((Ptr)(pict))[16]);
    if (pic2XHeader->version == -2)
    { // extended type 2
        outPictRect = pic2XHeader->srcRect;
    }
    else
        outPictRect = ((Picture *)pict)->picFrame;
    
    pictFile.CloseDataFork();
}
 
 
void FixedToStr(
    Fixed inFixValue,
    Str255 outStr)
{
        // convert to double_t, then use the dec routines
    double_t value = Fix2X(inFixValue);
    decform f;
    f.style = FIXEDDECIMAL;
    f.digits = 1;
    decimal dec;
    num2dec(&f, value, &dec);
    dec2str(&f, &dec, (char *)outStr);
    c2pstr((char *)outStr);
    
        // alternate method using the std C lib
    //sprintf((char *)outStr, "%.1lf", value);
    //c2pstr((char *)outStr);
}
 
 
Fixed StrToFixed(
    StringPtr str)
{
    Fixed theNum;
    long lower,upper;
    short len,i,j, decPoint;
    float fraction;
    Boolean neg;
    
    len = str[0];
    
    for (i=1; i<= len; i++)
        if (str[i] == '.')
            break;
        // No decimal point
    if (i >= len)
    {
        StringToNum(str, &theNum);
        return (theNum << 16);
    }
    
        // Make into two strings
    decPoint = i;
    str[0] = i-1;
    str[decPoint] = len-i;
    
    StringToNum((StringPtr) str, &upper);
    if ((neg = (int)(upper < 0)) != 0)
        upper = - upper;
    StringToNum((StringPtr) str+decPoint, &lower);
    
    fraction = lower;
    for (j = 1; j <= (len-i) ; j++)
        fraction = fraction / 10;
    
    lower = fraction * 0x10000;
    
        // Restore orig string
    str[0] = len;
    str[decPoint] = '.';
 
    return ((neg?-1:1)*((upper << 16) + lower));
}
 
 
/* FindCodecName
 
    Search the list of codecs for a name describing the passed codec.
*/
Boolean FindCodecName(
    StringPtr outName,
    CodecType inCodec)
{
    Boolean found = false;
    
    CodecNameSpecListPtr cnsp;
    OSErr result = ::GetCodecNameList(&cnsp, 1);
    if (result == noErr)
    {
        for (Int32 i = 0; i < cnsp->count; i++)
        {
            if (cnsp->list[i].cType == inCodec)
            {
                found = true; // yay
                CopyPStr(cnsp->list[i].typeName, outName);
                break;
            }
        }
        
        ::DisposeCodecNameList(cnsp);
    }
    
    return found;
}
 
 
/* SetCompressionText
 
    Set the passed pane's descriptor to text describing the passed image compression
    settings.
*/
void SetCompressionText(
    LPane *inPane,
    CodecType inCodec,
    CodecQ inSpatialQuality)
{
    if (inPane != nil)
    {
            // attempt to find a name for the current codec
        Str255 str;
        if (!FindCodecName(str, inCodec))
        {
                // put something in the string, however cryptic
            str[0] = 7;
            str[1] = '\'';
            *(OSType *)&str[2] = inCodec;
            str[6] = '\'';
            str[7] = ' ';
        }
        else
            str[++str[0]] = ' ';
        
            // create a description of the compression quality
        Str255 qstr;
        if (inSpatialQuality >= codecLosslessQuality)
            CopyPStr("\pLossless", qstr);
        else if (inSpatialQuality >= codecMaxQuality)
            CopyPStr("\pMaximum", qstr);
        else if (inSpatialQuality >= codecHighQuality)
            CopyPStr("\pHigh", qstr);
        else if (inSpatialQuality >= codecNormalQuality)
            CopyPStr("\pNormal", qstr);
        else if (inSpatialQuality > codecMinQuality)
            CopyPStr("\pLow", qstr);
        else
            CopyPStr("\pMinimum", qstr);
        ConcatPStr(str, qstr);
 
        ConcatPStr(str, "\p (");
        ::NumToString((((((float)inSpatialQuality)+.5) / 1024.0) * 100), qstr);
        ConcatPStr(str, qstr);
        ConcatPStr(str, "\p)");
        
        inPane->SetDescriptor(str);
    }
}