Sources/MSUtils.c

// MSUtils.c
//
// Original version by Jon Lansdell and Nigel Humphreys.
// 4.0 and 3.1 updates by Greg Sutton.
// ©Apple Computer Inc 1996, all rights reserved.
 
/*
    Changes for 3.1:
    
        12-Oct-95   : CW : Simplified FeatureIsImplemented routine.
                           Added init of gHasDragManager flag in CheckEnvironment.
                           
    Changes for 4.0:
        17-Apr-96   : GS : Changed pascal calling conventions to C where possible.
                           Changed behavior of outlining Up, Down and Other in Size menu.
 
*/
 
 
#ifdef THINK_C
    #include "PLStrs.h"
#else
    #include <PLStringFuncs.h>
#endif
#include <Events.h>
#include <Traps.h>
#include <Dialogs.h>
#include <Fonts.h>
#include <Packages.h>
#include <ToolUtils.h>
#include <AppleEvents.h>
#include <CodeFragments.h>
#include "MSUtils.h"
 
//      Name:           LesserOf
//      Purpose:        Returns the Lesser of two longints.
 
#pragma segment Utils
        
long    LesserOf( long A, long B )
{
    if ( A < B )
        return( A );
    else
        return( B );
} // LesserOf
            
 
//      Name:           GreaterOf
//      Purpose:        Returns the Greater of two longints.
    
#pragma segment Utils
        
long    GreaterOf( long A, long B )
{
    if ( A > B )
        return(A);
    else
        return(B);
} // GreaterOf
            
 
//      Name:       ShowError
//      Purpose:    Reports an error to the user as both string and number.
 
#pragma segment Utils
        
void    ShowError( Str255 theError, long theErrorCode )
{
    short     alertResult;
    Str255    theString;
    OSErr     myErr;
     
    myErr = AEInteractWithUser( kAEDefaultTimeout, nil, nil );
     
    if (myErr == noErr)
    {
        SetCursor(&qd.arrow);
        NumToString(theErrorCode, theString);
        ParamText( theError, theString, (unsigned char *)"", (unsigned char *)"" );
        alertResult = Alert(300, nil);
    }
} // ShowError
 
 
//      Name:           Ours
//      Purpose:        Checks the frontmost window belongs to the app.
 
#pragma segment Utils       
 
Boolean Ours( WindowPtr aWindow )
{
    if (aWindow)
        if (((WindowPeek)aWindow)->windowKind == zoomDocProc)
            return(true);
 
    return(false);
} // Ours
 
 
//      Name:           SetShortMenus
//      Purpose:        Cuts the menus down to a minimum - Apple File Edit.
//                      Greys out the unavailable options - used when no docs open
 
#pragma segment Utils       
 
void    SetShortMenus( void )
{ 
    DeleteMenu(mfontID);
    DeleteMenu(sizeID);
    DeleteMenu(styleID);
    DeleteMenu(mscriptID);
    DeleteMenu(subroutineID);
 
    SetMenuItemState ( false, myMenus[fileM], fmClose);
    SetMenuItemState ( false, myMenus[fileM], fmSave);
    SetMenuItemState ( false, myMenus[fileM], fmSaveAs);
    SetMenuItemState ( false, myMenus[fileM], fmRevert);
    SetMenuItemState ( false, myMenus[fileM], fmPageSetUp);
 
    if (gGXIsPresent)
    {
        SetMenuItemState ( false, myMenus[fileM], fmPrint);
        SetMenuItemState ( false, myMenus[fileM], fmPrintOne );
    }
    else
        SetMenuItemState ( false, myMenus[fileM], fmNoGXPrint);
 
    // now the unnecessary items on the edit menu
                
    SetMenuItemState ( false, myMenus[editM], undoCommand);
    SetMenuItemState ( false, myMenus[editM], cutCommand);
    SetMenuItemState ( false, myMenus[editM], copyCommand);
    SetMenuItemState ( false, myMenus[editM], clearCommand);
    SetMenuItemState ( false, myMenus[editM], pasteCommand);
    SetMenuItemState ( false, myMenus[editM], selectAllCommand);
 
    DrawMenuBar();
}  // SetShortMenus
 
 
//      Name:       SetLongMenus
//      Purpose:    Reinstates the full menu bar - called when first document
//                   opened.
 
#pragma segment Utils       
 
void    SetLongMenus( void )
{
    InsertMenu(myMenus[fontM], 0);
    InsertMenu(myMenus[sizeM], 0);
    InsertMenu(myMenus[styleM], 0);
    InsertMenu(myMenus[scriptM], 0);
    InsertMenu(myMenus[subroutineM], 0);
 
    SetMenuItemState ( true, myMenus[fileM], fmClose);
    SetMenuItemState ( true, myMenus[fileM], fmSave);
    SetMenuItemState ( true, myMenus[fileM], fmSaveAs);
    SetMenuItemState ( true, myMenus[fileM], fmRevert);
    SetMenuItemState ( true, myMenus[fileM], fmPageSetUp);
 
    if (gGXIsPresent)
    {
        SetMenuItemState ( true, myMenus[fileM],  fmPrint );
        SetMenuItemState ( true, myMenus[fileM],  fmPrintOne );
    }
    else
        SetMenuItemState ( true, myMenus[fileM], fmNoGXPrint );
 
    // now the necessary items on the edit menu -
    //  many other items fixed on each pass thru the main event
    //  loop or before the window pulled down
    
    SetMenuItemState ( true, myMenus[editM], selectAllCommand);
 
    DrawMenuBar();
}  // SetLongMenus
 
 
//      Name:       SetStyleMenu
//      Purpose:    Sets the style menu checking to reflect the style of the
//                   first character of the current selection in the given
//                   document.
 
#pragma segment Utils       
        
void    SetStyleMenu( DPtr theDoc )
{
    TextStyle   theTStyle;
    short       contMode;
    short       i;
    
    contMode = doFace;
    
    TEContinuousStyle(&contMode,&theTStyle,theDoc->theText);
    
    if ((contMode & doFace) != 0)
    {
        CheckItem(myMenus[styleM], cPlain,     (theTStyle.tsFace == 0));
        CheckItem(myMenus[styleM], cBold,      (bold      & theTStyle.tsFace));
        CheckItem(myMenus[styleM], cItalic,    (italic    & theTStyle.tsFace));
        CheckItem(myMenus[styleM], cUnderline, (underline & theTStyle.tsFace));
        CheckItem(myMenus[styleM], cOutline,   (outline   & theTStyle.tsFace));
        CheckItem(myMenus[styleM], cShadow,    (shadow    & theTStyle.tsFace));
        CheckItem(myMenus[styleM], cCondense,  (condense  & theTStyle.tsFace));
        CheckItem(myMenus[styleM], cExtend,    (extend    & theTStyle.tsFace));
    }
    else
        for (i=cPlain; i<= cExtend; i++)
            CheckItem(myMenus[styleM], i, false);
}
 
 
//    Name:       SetSizeMenu
//    Purpose:    Outline all the items if the current font is an
//                outline font. Check the size of the current selection
 
#pragma segment Utils       
 
void    SetSizeMenu( DPtr theDoc )
{
    short       i;
    short       aSize;
    short       max;
    long        theSize;
    Str255      name;
    Boolean     sizeinMenu;
    Point       numer;
    TextStyle   theStyle;
    TEHandle    myText;
    short       contMode;
    
    numer.h = 1;
    numer.v = 1;
    
    myText = theDoc->theText;
    
    contMode = doSize+doFont;
    
    TEContinuousStyle( &contMode, &theStyle, theDoc->theText );
    
    sizeinMenu = false;
    max = CountMItems(myMenus[sizeM]);
    for (i = 1; i <= max - 5; i++)
    {
        GetItem(myMenus[sizeM], i, name);
        StringToNum(name, &theSize);
        aSize = (short)theSize;
 
        if (RealFont(theStyle.tsFont, aSize) && (contMode & doFont) != 0) // there is only one font and this size exists
            SetItemStyle(myMenus[sizeM], i, outline);
        else
            SetItemStyle(myMenus[sizeM], i, 0);
 
        if ((aSize == theStyle.tsSize) && (contMode & doSize) != 0)
        {
            sizeinMenu = true;
            CheckItem(myMenus[sizeM], i, true);
        }
        else
            CheckItem(myMenus[sizeM], i, false);
    }
    
    // If it's not a size in the menu,and there is only one size in the
    //  selection range check the other item
    
    if (! sizeinMenu && (contMode & doSize) != 0)
    {
        CheckItem(myMenus[sizeM], max, true);
 
        if ( RealFont(theStyle.tsFont, theStyle.tsSize ) && (contMode & doFont) != 0 )
            SetItemStyle(myMenus[sizeM], max, outline);
        else
            SetItemStyle(myMenus[sizeM], max, 0);
    }
    else
    {
        CheckItem(myMenus[sizeM], max, false);
        SetItemStyle(myMenus[sizeM], max, 0);
    }
 
        // Outline Up if next size up is available
    if ( RealFont(theStyle.tsFont, theStyle.tsSize + 1 ) && (contMode & doFont) != 0 )
        SetItemStyle(myMenus[sizeM], max - 3, outline);
    else
        SetItemStyle(myMenus[sizeM], max - 3, 0);
 
        // Outline Down if next size up is available
    if ( RealFont(theStyle.tsFont, theStyle.tsSize - 1 ) && (contMode & doFont) != 0 )
        SetItemStyle(myMenus[sizeM], max - 2, outline);
    else
        SetItemStyle(myMenus[sizeM], max - 2, 0);
}
 
 
//    Name:       SetFontMenu
//    Purpose:    Set the font menu according to the state of
//                              current selection of the supplied document.
 
#pragma segment Utils
        
void    SetFontMenu( DPtr theDoc )
{
    MenuHandle      theMHandle;
    short           theNumber;
    short           i;
    short           max;
    Str255          name;
    TextStyle       theStyle;
    short           contMode;
    
    theMHandle = GetMHandle(mfontID);
    
    if (gFontMItem)
    CheckItem(theMHandle, gFontMItem, false);
        
    max = CountMItems(theMHandle);
    
    contMode = doFont;
    TEContinuousStyle(&contMode,&theStyle,theDoc->theText);
    
    gFontMItem = 0;
    
    if (contMode & doFont)
        for (i=1; i<=max; i++)
        {
            GetItem(theMHandle, i, name);
            GetFNum(name, &theNumber);
            if (theNumber == theStyle.tsFont)
                gFontMItem = i;
        }
    
    if (gFontMItem)
        CheckItem(theMHandle, gFontMItem, true);
        
    SetSizeMenu(theDoc);
    SetStyleMenu(theDoc);
}
 
 
//    Name:       GetTempFileName
//    Purpose:    Fills newstring with a temporary file name.
 
#pragma segment Utils
 
void    GetTempFileName( DPtr aDoc, Str255 newString )
{
    Str255        s;
    Str255        fileName;
    
    if (aDoc->everSaved == false)
        PLstrcpy(fileName, (unsigned char *)"\pTEXTra");
    else
        PLstrcpy(fileName, aDoc->theFileName);
    
    // generate a unique(ish) temporary filename
    
    if (fileName[0] > 21)
      fileName[0] = 21;
            
    NumToString(TickCount(), s);
    
    PLstrcat(fileName, s);
    
    PLstrcpy(newString,fileName);
}
 
 
//    Name:       SetText
//    Purpose:    Sets the text of the supplied itemNo in aDialog to 
//                              theString and select it.
 
#pragma segment Utils
 
void    SetText( DialogPtr aDialog, short itemNo, Str255 theString )
{
    Handle      itemHandle;
    Rect        box;
    short       kind;
    TEHandle    theTEHandle;
    
    GetDItem(aDialog, itemNo, &kind, &itemHandle, &box);
    SetIText(itemHandle, theString);
    
    theTEHandle = ((DialogPeek)aDialog)->textH;
    
    // Set all the text to be selected
    if (theTEHandle)
        TESetSelect(0, 255, theTEHandle);
}
            
 
//    Name:       RetrieveText
//    Purpose:    Returns the text of anItem in aDialog in aString.
 
#pragma segment Utils
 
void    RetrieveText( DialogPtr aDialog, short anItem, Str255 aString )
{
    short      kind;
    Rect       box;
    Handle     itemHandle;
    
    GetDItem(aDialog, anItem, &kind, &itemHandle, &box);
    GetIText(itemHandle, aString);
}
 
 
//    Name:       DrawDefaultOutline
//    Purpose:    Draws an outline around theItem.
//                  Called as a useritem Proc by the dialog manager.
//                  To use place a useritem over the default item in the
//                  dialog and install the address of this proc as the item
//                  handle.
 
#pragma segment Utils
 
pascal void DrawDefaultOutline( DialogPtr theDialog, short theItem )
{
    short       kind;
    Handle      itemHandle;
    Rect        box;
            
    GetDItem(theDialog, theItem, &kind, &itemHandle, &box);
    PenSize(3, 3);
    InsetRect(&box, - 4, - 4);
    FrameRoundRect(&box, 16, 16);
    PenNormal();
} // DrawDefaultOutline
            
 
//    Name:       AdornDefaultButton
//    Purpose:    Installs DrawDefaultOutline as the useritem proc
//                  for the given item.
 
#pragma segment Utils
        
void    AdornDefaultButton(DialogPtr theDialog,short theItem)
{
    short       kind;
    Handle      itemHandle;
    Rect        box;
    
    GetDItem(theDialog, theItem, &kind, &itemHandle, &box);
    SetDItem(theDialog, theItem, kind, (Handle)gDefaultButtonUPP, &box);
}
 
void    GetRectOfDialogItem( DialogPtr theDialog, short theItem, Rect *theRect )
{
    short       kind;
    Handle      itemHandle;
    
    GetDItem(theDialog, theItem, &kind, &itemHandle, theRect);
}
 
// -------  Determining of Gestalt is available -------
// The following routines come from the Inside Mac VI recommendations
//  about how to find if a trap is available
 
//  The glue for Gestalt will be in MPW 3.2, so if it is available we will also
//   need to check the system version
 
#pragma segment Utils
 
short   NumToolboxTraps(void)
{
    if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
        return(0x200);
    else
        return(0x400);
}
 
#pragma segment Utils
 
#define TrapMask  0x0800
 
TrapType    GetTrapType(short theTrap)
{
    if ((theTrap & TrapMask) > 0)
        return(ToolTrap);
    else
        return(OSTrap);
}
 
#pragma segment Utils
 
Boolean TrapAvailable(short theTrap)
{
    TrapType  tType;
    
    tType = GetTrapType(theTrap);
    if (tType == ToolTrap)
    {
        theTrap = theTrap & 0x07FF;
        if (theTrap >= NumToolboxTraps())
            theTrap = _Unimplemented;
    }
 
    return(NGetTrapAddress(theTrap, tType) != NGetTrapAddress(_Unimplemented,ToolTrap));
}
 
#pragma segment Utils
 
#define _Gestalt 0xA1AD
 
Boolean GestaltAvailable( void )
{
    return(TrapAvailable(_Gestalt));
}
 
 
// ------ FeatureIsImplemented ------
// This is called to use Gestalt to determine if a feature is implemented.
// This applies to only those referenced by OSType.
 
#pragma segment Utils
 
Boolean FeatureIsImplemented( OSType theFeature, short theTestBit )
{
    OSErr     err;
    long      result;
 
    err = Gestalt ( theFeature, &result );
    return (err == noErr && (result & (1L << theTestBit)));
}
 
 
#pragma segment Utils
 
Boolean CheckEnvironment( void )
{
    long        response;
 
        // first determine of Gestalt is available- if it isn't exit
        // as we only run under 7.0.  It could it present in 6.04 - so we need
        // to do some further checks for important features
    gGestaltAvailable = GestaltAvailable();
    
    if (!gGestaltAvailable)
        return(false);
 
        // check if the Edition Manager is present
    gEditionManagerImplemented = FeatureIsImplemented(gestaltEditionMgrAttr, gestaltEditionMgrPresent);
 
        // and for good measure - the Alias manager
    gAliasManagerImplemented  = FeatureIsImplemented(gestaltAliasMgrAttr, gestaltAliasMgrPresent);
 
        // check for the AppleEvents manager - we certainly can't work without it
    gAppleEventsImplemented   = FeatureIsImplemented(gestaltAppleEventsAttr, gestaltAppleEventsPresent);
 
        // check if recording is implemented
    gRecordingImplemented   = FeatureIsImplemented(gestaltAppleEventsAttr,1);
        
        // check for the Outline fonts
    gOutlineFontsImplemented  = FeatureIsImplemented(gestaltFontMgrAttr, gestaltOutlineFonts);
    
        // We would also like the Drag Manager
    gHasDragManager = FeatureIsImplemented ( gestaltDragMgrAttr, gestaltDragMgrPresent );
 
// It isn't enough to use Gestalt because we may not have sucessfully linked
// to the DragLib shared library. So, we also need to test one of the symbols
// against kUnresolvedSymbol to make sure we have a valid connection to it.
 
#if GENERATINGCFM
    if ( gHasDragManager )
        gHasDragManager = (InstallTrackingHandler != (void*) kUnresolvedSymbolAddress);
#endif
    
        // Want the process manager
    gHasProcessManager = ( noErr == Gestalt( gestaltOSAttr, &response ) );
 
 
    return (gEditionManagerImplemented
                && gAliasManagerImplemented 
                    && gAppleEventsImplemented
                        && gOutlineFontsImplemented
                            && gHasProcessManager );
                            
}  // CheckEnvironment
            
 
//  DoPageSetup returns true if the page setup of the document is altered
    
Boolean DoPageSetup(DPtr theDoc)
{
    Boolean result = false;
    
    if ( ! gGXIsPresent )
        if ( theDoc )
        {
            PrOpen();
            result =  PrStlDialog(theDoc->thePrintSetup);
            PrClose();
        }
        
    return(result);
}  // DoPageSetup
 
 
//  Name:    CtrlKeyPressed
//  Purpose: Returns true if control key pressed during event
 
Boolean CtrlKeyPressed( const EventRecord *theEvent )
{
    return((theEvent->modifiers & controlKey) != 0);
}
    
 
//  Name:    OptionKeyPressed
//  Purpose: Returns true if option key pressed during event
 
Boolean OptionKeyPressed( const EventRecord *theEvent )
{
    return((theEvent->modifiers & optionKey) != 0);
}
 
 
Boolean SetMenuItemState (  Boolean    theState,
                            MenuHandle theMenu,
                            short      item )
{
    Boolean reDrawMenuBar = false;
    Boolean isSet;
    
    if (item == kMenuTitle)
    {
        isSet = ((*theMenu)->enableFlags & 0x00000001);
        reDrawMenuBar = (isSet != theState);
    }
 
    if (theState)
        EnableItem ( theMenu, item );
    else
        DisableItem ( theMenu, item );
    
    return ( reDrawMenuBar );
}