TweetFollow Us on Twitter

MACINTOSH C

Demonstration Program

Go to Contents
// 
// Files1.h
// 
//
// This program demonstrates:
//
//   Application-defined file handling functions associated with:
//
//        The user invoking the File menu Open..., Close, Save..., Save As..., Revert to 
//        Saved, and Quit commands of a typical application.
//
//        Handling of the required Apple events Open Application, Open Documents and 
//        Quit Application.
//
//   A customised Open dialog which allows the user, via an added pop-up menu button, to
//    specify the types of file (TEXT or PICT) to be displayed in the dialog's list.
//
//   A directory selection dialog.
//
// To keep the code not specifically related to files and file-handling to a minimum, an 
// item is included in the Demonstration menu which allows the user to simulate making a
// window "touched" (that is, modifying the contents of the associated document).  
// Choosing the first menu item in this menu sets the windowTouched flag in the window's 
// document structure to true and draws the text "WINDOW TOUCHED" in the window in a 
// large font size, this latter so that the user can keep track of which windows are 
// "touched".
//
// The program utilises the following resources:
//
//   An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit and Demonstration
//    menus (preload, non-purgeable).  
//
//   A 'WIND' resource (purgeable) (initially not visible).  
//
//   Two 'ALRT' resources (purgeable) and associated 'alrx', 'DITL', and 'dftb'
//    resources (purgeable). The first is used to support  the Revert to Saved menu item.
//    The second is used when an attempt is made to close  a modified document before 
//    that document has been saved.
//
//   Two 'DLOG' resources ((purgeable) and associated 'dlgx' and 'DITL' resources 
//    (purgeable). The first is for the customised Open dialog.  The second is for the
//    directory selection dialog.
//
//   Two 'CNTL' resources (purgeable) for the items added to the customised Open dialog.
//    The first is for a pop-up menu button.  The second is for a separator line.
//
//   A 'MENU' resource for the popu-up menu button.
//
//   A 'STR ' resource (purgeable) containing the "missing application name" string 
//    resource, which is copied to all document files created by the program.  
//
//   A 'STR#' resource containing error strings.
//
//   A 'SIZE' resource with the acceptSuspendResumeEvents, isHighLevelEventAware, and
//    is32BitCompatible flags set (non-purgeable).    
//
//   The 'BNDL' resource (non-purgeable), 'FREF' resources (non-purgeable), signature
//    resource (non-purgeable), and icon family resources (purgeable), required to 
//    support the built application.
//
// 

// ............................................................................. includes

#include <Appearance.h>
#include <AERegistry.h>
#include <Devices.h>
#include <Folders.h>
#include <Navigation.h>
#include <Resources.h>
#include <Sound.h>
#include <ToolUtils.h>

// ............................................................................. typedefs

typedef struct
{
  TEHandle    editStrucHdl;
  PicHandle   pictureHdl;
  SInt16      fileRefNum;
  FSSpec      fileFSSpec;
  Boolean     windowTouched;
}  docStructure, *docStructurePointer, **docStructureHandle;

typedef StandardFileReply *standardFileReplyPtr;

// .............................................................................. defines

#define mApple                  128
#define  iAbout                 1
#define mFile                   129
#define  iNew                   1
#define  iOpen                  2
#define  iClose                 4
#define  iSave                  5
#define  iSaveAs                6
#define  iRevert                7
#define  iQuit                  12
#define mDemonstration          131
#define  iTouchWindow           1
#define  iSelectDirectoryDialog 3
#define rNewWindow              128
#define rMenubar                128
#define rRevertAlert            128
#define rCloseFileAlert         129
#define rCustomOpenDialog       130
#define  iPopupItem             10
#define rSelectDirectoryDialog  131
#define  iSelectButton          10
#define rErrorStrings           128  
#define  eInstallHandler        1000
#define  eMaxWindows            1001
#define  eFileIsOpen            opWrErr
#define kMaxWindows             10
#define kUserCancelled          1002
#define MAXLONG                 0x7FFFFFFF
#define MIN(a,b)                ((a) < (b) ? (a) : (b))

// .................................................................. function prototypes

void  main                  (void);
void  eventLoop             (void);
void  doInitManagers        (void);
void  doInstallAEHandlers   (void);
void  doEvents              (EventRecord *);
void  doMouseDown           (EventRecord *);
void  doUpdate              (EventRecord *);
void  doMenuChoice          (SInt32);
void  doFileMenu            (SInt16);
void  doAdjustMenus         (void);
void  doErrorAlert          (SInt16);
void  doCopyPString         (Str255,Str255);
void  doConcatPStrings      (Str255,Str255);
void  doTouchWindow         (void);
pascal OSErr  doOpenAppEvent          (AppleEvent *,AppleEvent *,SInt32);
pascal OSErr  doReopenAppEvent        (AppleEvent *,AppleEvent *,SInt32);
pascal OSErr  doOpenDocsEvent         (AppleEvent *,AppleEvent *,SInt32);
pascal OSErr  doQuitAppEvent          (AppleEvent *,AppleEvent *,SInt32);
OSErr         doHasGotRequiredParams  (AppleEvent *);

OSErr  doNewCommand          (void);
OSErr  doOpenCommand         (void);
OSErr  doCloseCommand        (void);
OSErr  doSaveCommand         (void);
OSErr  doSaveAsCommand       (void);
OSErr  doRevertCommand       (void);
OSErr  doQuitCommand         (void);
OSErr  doNewDocWindow        (Boolean,OSType);
OSErr  doOpenFile            (FSSpec,OSType);
OSErr  doReadTextFile        (WindowPtr);
OSErr  doReadPictFile        (WindowPtr);
OSErr  doCloseFile           (WindowPtr,docStructureHandle);
OSErr  doWriteFile           (WindowPtr);
OSErr  doWriteTextData       (WindowPtr,SInt16);
OSErr  doWritePictData       (WindowPtr,SInt16);
OSErr  doCopyAppNameResource (WindowPtr);
OSErr  doCopyResource        (ResType,SInt16,SInt16,SInt16);

pascal  Boolean     filterFunctionOpenDialog    (CInfoPBPtr,void *);
pascal  SInt16      hookFunctionOpenDialog      (SInt16,DialogPtr,void *);
StandardFileReply   doDirectorySelectionDialog  (void);
pascal  Boolean     filterFunctionDirSelect     (CInfoPBPtr,void *);
pascal  SInt16      hookFunctionDirSelect       (SInt16,DialogPtr,void *);

// 
// Files1.c
// 

// ............................................................................. includes

#include "Files1.h"

// ..................................................................... global variables

Boolean            gDone;
Boolean            gInBackground;
AEEventHandlerUPP  doOpenAppEventUPP;
AEEventHandlerUPP  doReopenAppEventUPP;
AEEventHandlerUPP  doOpenDocsEventUPP;
AEEventHandlerUPP  doQuitAppEventUPP;
SInt16             gAppResFileRefNum;

extern SInt16    gCurrentNumberOfWindows;
extern Rect      gDestRect,gViewRect;
extern Boolean   gDirectorySelectionFlag;

//  main

void  main(void)
{
  Handle      menubarHdl;
  MenuHandle  menuHdl;

  // .................................................................initialise managers

  doInitManagers();

  // ................................. create routine decriptors for Apple event handlers

  doOpenAppEventUPP    = NewAEEventHandlerProc((ProcPtr) doOpenAppEvent);
  doReopenAppEventUPP  = NewAEEventHandlerProc((ProcPtr) doReopenAppEvent);
  doOpenDocsEventUPP   = NewAEEventHandlerProc((ProcPtr) doOpenDocsEvent);
  doQuitAppEventUPP    = NewAEEventHandlerProc((ProcPtr) doQuitAppEvent);

  // ........................... set application's resource fork as current resource file

  gAppResFileRefNum = CurResFile();

  // .......................................................... set up menu bar and menus
  
  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    doErrorAlert(MemError());
  SetMenuBar(menubarHdl);
  DrawMenuBar();

  menuHdl = GetMenuHandle(mApple);
  if(menuHdl == NULL)
    doErrorAlert(MemError());
  else
    AppendResMenu(menuHdl,'DRVR');

  // .............................................. install required Apple event handlers

  doInstallAEHandlers();
  
  // ................................................................... enter event loop

  eventLoop();
}

//  eventLoop

void  eventLoop(void)
{
  EventRecord eventStructure;

  gDone = false;

  while(!gDone)
  {
    if(WaitNextEvent(everyEvent,&eventStructure,MAXLONG,NULL))
      doEvents(&eventStructure);
  }
}

//  doInitManagers

void  doInitManagers(void)
{
  MaxApplZone();
  MoreMasters();

  InitGraf(&qd.thePort);
  InitFonts();
  InitWindows();
  InitMenus();
  TEInit();
  InitDialogs(NULL);

  InitCursor();
  FlushEvents(everyEvent,0);

  RegisterAppearanceClient();
}

//  doInstallAEHandlers

void  doInstallAEHandlers(void)
{
  OSErr  osError;

  osError = AEInstallEventHandler(kCoreEventClass,kAEOpenApplication,doOpenAppEventUPP,
                              0L,false);
  if(osError != noErr)  doErrorAlert(eInstallHandler);

  osError = AEInstallEventHandler(kCoreEventClass,kAEReopenApplication,
                                  doReopenAppEventUPP,0L,false);
  if(osError != noErr)  doErrorAlert(eInstallHandler);

  osError = AEInstallEventHandler(kCoreEventClass,kAEOpenDocuments,doOpenDocsEventUPP,
                              0L,false);
  if(osError != noErr)  doErrorAlert(eInstallHandler);

  osError = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,doQuitAppEventUPP,
                              0L,false);
  if(osError != noErr)  doErrorAlert(eInstallHandler);
}

//  doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  SInt8  charCode;

  switch(eventStrucPtr->what)
  {
    case kHighLevelEvent:
      AEProcessAppleEvent(eventStrucPtr);
      break;

    case mouseDown:
      doMouseDown(eventStrucPtr);
      break;

    case keyDown:
    case autoKey:
      charCode = eventStrucPtr->message & charCodeMask;
      if((eventStrucPtr->modifiers & cmdKey) != 0)
      {
        doAdjustMenus();
        doMenuChoice(MenuEvent(eventStrucPtr));
      }
      break;

    case updateEvt:
      doUpdate(eventStrucPtr);
      break;

    case osEvt:
      switch((eventStrucPtr->message >> 24) & 0x000000FF)
      {
        case suspendResumeMessage:
          gInBackground = (eventStrucPtr->message & resumeFlag) == 0;
          break;
      }
      HiliteMenu(0);
      break;
  }
}

//  doMouseDown

void  doMouseDown(EventRecord *eventStrucPtr)
{
  WindowPtr windowPtr;
  SInt16    partCode;

  partCode = FindWindow(eventStrucPtr->where,&windowPtr);
  
  switch(partCode)
  {
    case inMenuBar:
      doAdjustMenus();
      doMenuChoice(MenuSelect(eventStrucPtr->where));
      break;

    case inContent:
      if(windowPtr != FrontWindow())
        SelectWindow(windowPtr);
      break;

    case inDrag:
      DragWindow(windowPtr,eventStrucPtr->where,&qd.screenBits.bounds);
      break;

    case inGoAway:
      if(TrackGoAway(windowPtr,eventStrucPtr->where) == true)
        doCloseCommand();
      break;
  }
}

//  doUpdate

void  doUpdate(EventRecord *eventStrucPtr)
{
  WindowPtr           windowPtr;
  docStructureHandle  docStrucHdl;
  GrafPtr             oldPort;
  Rect                destRect;

  windowPtr = (WindowPtr) eventStrucPtr->message;
  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);

  GetPort(&oldPort);
  SetPort(windowPtr);

  BeginUpdate(windowPtr);

  if((*docStrucHdl)->pictureHdl)
  {
    destRect = (*(*docStrucHdl)->pictureHdl)->picFrame;
    OffsetRect(&destRect,170,54);
    HLock((Handle) (*docStrucHdl)->pictureHdl);
    DrawPicture((*docStrucHdl)->pictureHdl,&destRect);
    HUnlock((Handle) (*docStrucHdl)->pictureHdl);
  }
  else if((*docStrucHdl)->editStrucHdl)
  {
    HLock((Handle) (*docStrucHdl)->editStrucHdl);
    TEUpdate(&gDestRect,(*docStrucHdl)->editStrucHdl);
    HUnlock((Handle) (*docStrucHdl)->editStrucHdl);
  }

  if((*docStrucHdl)->windowTouched)
  {
    TextSize(48);
    MoveTo(30,170);
    DrawString("\pWINDOW TOUCHED");
    TextSize(12);
  }

  EndUpdate((WindowPtr)eventStrucPtr->message);

  SetPort(oldPort);
}

//  doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  SInt16            menuID, menuItem;
  Str255            itemName;
  SInt16            daDriverRefNum;
  StandardFileReply stdFileReply;
  Rect              theRect;
  Str255            theString, numberString;  

  menuID = HiWord(menuChoice);
  menuItem = LoWord(menuChoice);

  if(menuID == 0)
    return;

  switch(menuID)
  {
    case mApple:
      GetMenuItemText(GetMenuHandle(mApple),menuItem,itemName);
      daDriverRefNum = OpenDeskAcc(itemName);
      break;

    case mFile:
      doFileMenu(menuItem);
      break;

    case mDemonstration:
      if(menuItem == iTouchWindow)
        doTouchWindow();
      else if(menuItem == iSelectDirectoryDialog)
      {
        stdFileReply = doDirectorySelectionDialog();
        if(FrontWindow())
        {
          SetPort(FrontWindow());
          TextSize(10);
          SetRect(&theRect,0,271,600,300);
          EraseRect(&theRect);
          if(gDirectorySelectionFlag)
          {
            doCopyPString(stdFileReply.sfFile.name,theString);
            doConcatPStrings(theString, "\p   Volume Reference Number: ");
            NumToString((SInt32) stdFileReply.sfFile.vRefNum,numberString);
            doConcatPStrings(theString,numberString);
            doConcatPStrings(theString, "\p   Parent Directory ID: ");
            NumToString((SInt32) stdFileReply.sfFile.parID,numberString);
            doConcatPStrings(theString,numberString);
            MoveTo(10,290);
            DrawString(theString);
          }
        }
      }
      break;
  }

  HiliteMenu(0);
}

//  doFileMenu

void  doFileMenu(SInt16 menuItem)
{
  OSErr  osError;

  switch(menuItem)
  {
    case iNew:
      if(osError = doNewCommand())
        doErrorAlert(osError);
      break;

    case iOpen:
      if(osError = doOpenCommand())
        doErrorAlert(osError);
      break;

    case iClose:
      if((osError = doCloseCommand()) && osError != kUserCancelled)
        doErrorAlert(osError);
      break;

    case iSave:
      if(osError = doSaveCommand())
        doErrorAlert(osError);
      break;

    case iSaveAs:
      if(osError = doSaveAsCommand())
        doErrorAlert(osError);
      break;

    case iRevert:
      if(osError = doRevertCommand())
        doErrorAlert(osError);
      break;

    case iQuit:
      if((osError = doQuitCommand()) && osError != kUserCancelled)
        doErrorAlert(osError);
      if(osError != kUserCancelled)
        gDone = true;
      break;
  }
}

//  doAdjustMenus

void  doAdjustMenus(void)
{
  MenuHandle          menuHdl;
  WindowPtr           windowPtr;
  docStructureHandle  docStrucHdl;

  windowPtr = FrontWindow();
  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);

  menuHdl = GetMenuHandle(mFile);

  if(gCurrentNumberOfWindows > 0)
  {
    menuHdl = GetMenuHandle(mFile);
    EnableItem(menuHdl,iClose);
    if((*docStrucHdl)->windowTouched)
    {
      EnableItem(menuHdl,iSave);
      EnableItem(menuHdl,iRevert);
    }
    else
    {
      DisableItem(menuHdl,iSave);
      DisableItem(menuHdl,iRevert);
    }
    EnableItem(menuHdl,iSaveAs);

    menuHdl = GetMenuHandle(mDemonstration);
    if((*docStrucHdl)->windowTouched == false)
      EnableItem(menuHdl,iTouchWindow);
    else
      DisableItem(menuHdl,iTouchWindow);
  }
  else
  {
    menuHdl = GetMenuHandle(mFile);
    DisableItem(menuHdl,iClose);
    DisableItem(menuHdl,iSave);
    DisableItem(menuHdl,iSaveAs);
    DisableItem(menuHdl,iRevert);
    menuHdl = GetMenuHandle(mDemonstration);
    DisableItem(menuHdl,iTouchWindow);
  }  

  DrawMenuBar();
}
  
//  doErrorAlert

void  doErrorAlert(SInt16 errorCode)
{
  AlertStdAlertParamRec paramRec;
  Str255                errorString, theString;
  SInt16                itemHit;

  paramRec.movable        = true;
  paramRec.helpButton      = false;
  paramRec.filterProc      = NULL;
  paramRec.defaultText    = (StringPtr) kAlertDefaultOKText;
  paramRec.cancelText      = NULL;
  paramRec.otherText      = NULL;
  paramRec.defaultButton  = kAlertStdAlertOKButton;
  paramRec.cancelButton    = 0;
  paramRec.position        = kWindowDefaultPosition;

  if(errorCode == eInstallHandler)
    GetIndString(errorString,rErrorStrings,1);
  else if(errorCode == eMaxWindows)
    GetIndString(errorString,rErrorStrings,2);
  else if(errorCode == eFileIsOpen)
    GetIndString(errorString,rErrorStrings,3);
  else
  {
    GetIndString(errorString,rErrorStrings,4);
    NumToString((SInt32) errorCode,theString);
    doConcatPStrings(errorString,theString);
  }

  if(errorCode != memFullErr)
    StandardAlert(kAlertCautionAlert,errorString,NULL,¶mRec,&itemHit);
  else
  {
    StandardAlert(kAlertStopAlert,errorString,NULL,¶mRec,&itemHit);
    ExitToShell();
  }
}

//  doCopyPString

void  doCopyPString(Str255 sourceString,Str255 destinationString)
{
  SInt16  stringLength;

  stringLength = sourceString[0];
  BlockMove(sourceString + 1,destinationString + 1,stringLength);
  destinationString[0] = stringLength;
}

//  doConcatPStrings

void  doConcatPStrings(Str255 targetString,Str255 appendString)
{
  SInt16  appendLength;

  appendLength = MIN(appendString[0],255 - targetString[0]);

  if(appendLength > 0)
  {
    BlockMoveData(appendString+1,targetString+targetString[0]+1,(SInt32) appendLength);
    targetString[0] += appendLength;
  }
}

//  doTouchWindow

void  doTouchWindow(void)
{
  WindowPtr           windowPtr;
  docStructureHandle  docStrucHdl;

  windowPtr = FrontWindow();
  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
  
  SetPort(windowPtr);
  
  TextSize(48);
  MoveTo(30,170);
  DrawString("\pWINDOW TOUCHED");
  TextSize(12);

  (*docStrucHdl)->windowTouched = true;
}

//  doOpenAppEvent

pascal OSErr  doOpenAppEvent(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefCon)
{
  OSErr  osError;

  osError = doHasGotRequiredParams(appEvent);
  if(osError == noErr)
    osError = doNewCommand();

  return(osError);
}

//  doReopenAppEvent

pascal OSErr  doReopenAppEvent(AppleEvent *appEvent,AppleEvent *reply,
                               SInt32 handlerRefCon)
{
  OSErr  osError;

  osError = doHasGotRequiredParams(appEvent);
  if(osError == noErr)
    if(!FrontWindow())
      osError = doNewCommand();

  return(osError);
}
//  doOpenDocsEvent

pascal OSErr  doOpenDocsEvent(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
  FSSpec      fileSpec;
  AEDescList  docList;
  OSErr       osError, ignoreErr;
  SInt32      index, numberOfItems;
  Size        actualSize;
  AEKeyword   keyWord;
  DescType    returnedType;
  FInfo       fileInfo;

  osError = AEGetParamDesc(appEvent,keyDirectObject,typeAEList,&docList);

  if(osError == noErr)
  {
    osError = doHasGotRequiredParams(appEvent);
    if(osError == noErr)
    {
      AECountItems(&docList,&numberOfItems);
      if(osError == noErr)
      {
        for(index=1;index<=numberOfItems;index++)
        {
          osError = AEGetNthPtr(&docList,index,typeFSS,&keyWord,&returnedType,
                              (Ptr) &fileSpec,sizeof(fileSpec),&actualSize);
          if(osError == noErr)
          {
            osError = FSpGetFInfo(&fileSpec,&fileInfo);
            if(osError == noErr)
            {
              if(osError = doOpenFile(fileSpec,fileInfo.fdType))
                doErrorAlert(osError);
            }
          }
          else
            doErrorAlert(osError);
        }
      }
    }
    else
      doErrorAlert(osError);

    ignoreErr = AEDisposeDesc(&docList);
  }
  else
    doErrorAlert(osError);

  return(osError);
}

//  doQuitAppEvent

pascal OSErr  doQuitAppEvent(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
  OSErr  osError;

  osError = doHasGotRequiredParams(appEvent);
  if(osError == noErr)
  {
    while(FrontWindow())
    {
      osError = doCloseCommand();
      if(osError != noErr && osError != kUserCancelled)
        doErrorAlert(osError);
      if(osError == kUserCancelled)
        return;
    }
  }

  gDone = true;

  return(osError);
}

//  doHasGotRequiredParams

OSErr  doHasGotRequiredParams(AppleEvent *appEvent)
{
  DescType  returnedType;
  Size      actualSize;
  OSErr      osError;

  osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,
                            NULL,0,&actualSize);
  if(osError == errAEDescNotFound)
    return(noErr);
  else if(osError == noErr)
    return(errAEParamMissed);
}

// 
// NewOpenCloseSave.c
// 

// ............................................................................. includes

#include "Files1.h"

// ..................................................................... global variables

WindowPtr   gWindowPtr;
SInt16      gCurrentNumberOfWindows = 0;
Rect        gDestRect,gViewRect;
SFTypeList  gFileTypes;

extern SInt16  gAppResFileRefNum;

//  doNewCommand

OSErr  doNewCommand(void)
{
  OSErr   osError;
  OSType  documentType = 'TEXT';

  osError = doNewDocWindow(true,documentType);
  return(osError);
}

//  doOpenCommand

OSErr  doOpenCommand(void)
{
  StandardFileReply fileReply;
  OSType            documentType;
  OSErr             osError = noErr;    
  FileFilterYDUPP   filterFunctionOpenDialogUPP;
  DlgHookYDUPP      hookFunctionOpenDialogUPP;
  Point             dialogLocation;

  filterFunctionOpenDialogUPP = NewFileFilterYDProc((ProcPtr) filterFunctionOpenDialog);
  hookFunctionOpenDialogUPP = NewDlgHookYDProc((ProcPtr) hookFunctionOpenDialog);

  gFileTypes[0] = 'TEXT';
  gFileTypes[1] = 'PICT';

  dialogLocation.v = -1;
  dialogLocation.h = -1;

  CustomGetFile(filterFunctionOpenDialogUPP,2,gFileTypes,&fileReply,rCustomOpenDialog,
                dialogLocation,hookFunctionOpenDialogUPP,NULL,NULL,NULL,NULL);

  DisposeRoutineDescriptor(filterFunctionOpenDialogUPP);
  DisposeRoutineDescriptor(hookFunctionOpenDialogUPP);

  documentType = fileReply.sfType;

  if(fileReply.sfGood)
    osError = doOpenFile(fileReply.sfFile,documentType);

  return(osError);
}

//  doCloseCommand

OSErr  doCloseCommand(void)
{
  WindowPtr           windowPtr;
  SInt16              windowKind;
  docStructureHandle  docStrucHdl;
  OSErr               osError = noErr;

  windowPtr = FrontWindow();
  windowKind = ((WindowPeek) windowPtr)->windowKind;

  switch(windowKind)
  {
    case kApplicationWindowKind:
      docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
      osError = doCloseFile(windowPtr,docStrucHdl);
      if(osError == kUserCancelled)
        return(kUserCancelled);
      else if(osError == noErr)
      {
        DisposeWindow(windowPtr);
        gCurrentNumberOfWindows --;
      }
      break;
      
    case kDialogWindowKind:
      // Hide or close modeless dialog, as required.
      break;
  }

  return(osError);
}

//  doSaveCommand

OSErr  doSaveCommand(void)
{
  WindowPtr           windowPtr;
  docStructureHandle  docStrucHdl;
  OSErr               osError = noErr;
  
  windowPtr = FrontWindow();
  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);

  if((*docStrucHdl)->fileRefNum)
  {
    osError = doWriteFile(windowPtr);
    
    SetPort(windowPtr);
    EraseRect(&windowPtr->portRect);
    InvalRect(&windowPtr->portRect);
  }
  else
    osError = doSaveAsCommand();
  
  return(osError);
}

//  doSaveAsCommand

OSErr  doSaveAsCommand(void)
{
  WindowPtr           windowPtr;
  docStructureHandle  docStrucHdl;
  StandardFileReply   fileReply;
  OSType              fileType;
  SInt16              fileRefNum;
  OSErr               osError = noErr;

  windowPtr = FrontWindow();
  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);

  StandardPutFile("\pSave as:","\pUntitled",&fileReply);

  if(fileReply.sfGood)
  {
    if(!(fileReply.sfReplacing))
    {
      if((*docStrucHdl)->editStrucHdl)
        fileType = 'TEXT';
      else if((*docStrucHdl)->pictureHdl)
        fileType = 'PICT';
      osError = FSpCreate(&fileReply.sfFile,'KKKB',fileType,smSystemScript);
      if(osError != noErr)
        return(osError);
    }

    (*docStrucHdl)->fileFSSpec = fileReply.sfFile;

    if((*docStrucHdl)->fileRefNum != 0)
    {
      osError = FSClose((*docStrucHdl)->fileRefNum);
      (*docStrucHdl)->fileRefNum = 0;
    }

    if(osError == noErr)
      osError = FSpOpenDF(&(*docStrucHdl)->fileFSSpec,fsRdWrPerm,&fileRefNum);

    if(osError == noErr)
    {
      (*docStrucHdl)->fileRefNum = fileRefNum;
      SetWTitle(windowPtr,fileReply.sfFile.name);
      osError = doWriteFile(windowPtr);
    }
  }

  SetPort(windowPtr);
  EraseRect(&windowPtr->portRect);
  InvalRect(&windowPtr->portRect);

  return(osError);
}

//  doRevertCommand

OSErr  doRevertCommand(void)
{
  WindowPtr           windowPtr;
  docStructureHandle  docStrucHdl;
  Str255              fileName;
  SInt16              itemHit;
  OSErr               osError = noErr;
  
  windowPtr = FrontWindow();
  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);

  SetPort(windowPtr);

  GetWTitle(windowPtr,fileName);
  ParamText(fileName,NULL,NULL,NULL);

  SetPort(windowPtr);

  itemHit = CautionAlert(rRevertAlert,NULL);

  if(itemHit == 1)
  {
    EraseRect(&windowPtr->portRect);
    if((*docStrucHdl)->editStrucHdl)
      osError = doReadTextFile(windowPtr);
    else if((*docStrucHdl)->pictureHdl)
    {
      KillPicture((*docStrucHdl)->pictureHdl);
      (*docStrucHdl)->pictureHdl = NULL;
      osError = doReadPictFile(windowPtr);
    }

    (*docStrucHdl)->windowTouched = false;

    InvalRect(&windowPtr->portRect);
  }

  return(osError);
}

//  doQuitCommand

OSErr  doQuitCommand(void)
{
  OSErr  osError = noErr;

  while(FrontWindow())
  {
    osError = doCloseCommand();
    if(osError != noErr)
      return(osError);
  }

  return(osError);
}

//  doNewDocWindow

OSErr  doNewDocWindow(Boolean showWindow,OSType documentType)
{
  docStructureHandle  docStrucHdl;

  if(gCurrentNumberOfWindows == kMaxWindows)
    return(eMaxWindows);

  if(!(gWindowPtr = GetNewCWindow(rNewWindow,NULL,(WindowPtr)-1)))
    return(MemError());

  SetPort(gWindowPtr);

  if(!(docStrucHdl = (docStructureHandle) NewHandle(sizeof(docStructure))))
  {
    DisposeWindow(gWindowPtr);
    gCurrentNumberOfWindows --;
    return(MemError());
  }

  SetWRefCon(gWindowPtr,(SInt32) docStrucHdl);

  (*docStrucHdl)->editStrucHdl = NULL;
  (*docStrucHdl)->pictureHdl = NULL;
  (*docStrucHdl)->fileRefNum = 0;
  (*docStrucHdl)->windowTouched = false;

  if(documentType == 'TEXT')
  {
    gDestRect = gWindowPtr->portRect;
    InsetRect(&gDestRect,6,6);
    gViewRect = gDestRect;

    MoveHHi((Handle) docStrucHdl);
    HLock((Handle) docStrucHdl);

    if(!((*docStrucHdl)->editStrucHdl = TENew(&gDestRect,&gViewRect)))
    {
      DisposeWindow(gWindowPtr);
      gCurrentNumberOfWindows --;
      DisposeHandle((Handle) docStrucHdl);
      return(MemError());
    }

    HUnlock((Handle) docStrucHdl);
  }

  if(showWindow)
    ShowWindow(gWindowPtr);

  gCurrentNumberOfWindows ++;

  return(noErr);
}

//  doOpenFile

OSErr  doOpenFile(FSSpec fileSpec,OSType documentType)
{
  OSErr               osError;
  SInt16              fileRefNum;
  docStructureHandle  docStrucHdl;

  if(osError = doNewDocWindow(false,documentType))
    return(osError);

  SetWTitle(gWindowPtr,fileSpec.name);  

  if(osError = FSpOpenDF(&fileSpec,fsRdWrPerm,&fileRefNum))
  {
    DisposeWindow(gWindowPtr);
    gCurrentNumberOfWindows --;
    return(osError);
  }
  
  docStrucHdl = (docStructureHandle) GetWRefCon(gWindowPtr);
  (*docStrucHdl)->fileRefNum = fileRefNum;
  (*docStrucHdl)->fileFSSpec = fileSpec;

  if(documentType == 'TEXT')
  {
    if(osError = doReadTextFile(gWindowPtr))
      return(osError);
  }
  else if(documentType == 'PICT')
  {
    if(osError = doReadPictFile(gWindowPtr))
      return(osError);
  }

  ShowWindow(gWindowPtr);

  return(noErr);
}

//  doCloseFile

OSErr  doCloseFile(WindowPtr windowPtr,docStructureHandle docStrucHdl)
{
  Str255  fileName;
  SInt16  itemHit;
  OSErr   osError;

  if((*docStrucHdl)->windowTouched)
  {
    GetWTitle(windowPtr,fileName);
    ParamText(fileName,NULL,NULL,NULL);

    itemHit = CautionAlert(rCloseFileAlert,NULL);
    if(itemHit == 2)
      return(kUserCancelled);
    else if(itemHit == 1)
    {
      if(osError = doSaveCommand())
        return(osError);
    }
  }

  if((*docStrucHdl)->fileRefNum != 0)
  {
    if(!(osError = FSClose((*docStrucHdl)->fileRefNum)))  
    {
      osError = FlushVol(NULL,(*docStrucHdl)->fileFSSpec.vRefNum);
      (*docStrucHdl)->fileRefNum = 0;
    }
  }

  if((*docStrucHdl)->editStrucHdl)
    TEDispose((*docStrucHdl)->editStrucHdl);
  if((*docStrucHdl)->pictureHdl)
    KillPicture((*docStrucHdl)->pictureHdl);

  DisposeHandle((Handle) docStrucHdl);
  
  return(osError);
}

//  doWriteFile

OSErr  doWriteFile(WindowPtr windowPtr)
{
  docStructureHandle  docStrucHdl;
  FSSpec              fileSpecActual, fileSpecTemp;
  UInt32              currentTime;
  Str255              tempFileName;
  SInt16              tempFileVolNum, tempFileRefNum;
  SInt32              tempFileDirID;
  OSErr               osError;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
  fileSpecActual = (*docStrucHdl)->fileFSSpec;

  GetDateTime(¤tTime);
  NumToString((SInt32) currentTime,tempFileName);
  
  osError = FindFolder(fileSpecActual.vRefNum,kTemporaryFolderType,kCreateFolder,
                        &tempFileVolNum,&tempFileDirID);
  if(osError == noErr)
    osError = FSMakeFSSpec(tempFileVolNum,tempFileDirID,tempFileName,&fileSpecTemp);
  if(osError == noErr || osError == fnfErr)
    osError = FSpCreate(&fileSpecTemp,'trsh','trsh',smSystemScript);
  if(osError == noErr)
    osError = FSpOpenDF(&fileSpecTemp,fsRdWrPerm,&tempFileRefNum);
  if(osError == noErr)
  {
    if((*docStrucHdl)->editStrucHdl)
      osError = doWriteTextData(windowPtr,tempFileRefNum);
    else if((*docStrucHdl)->pictureHdl)
      osError = doWritePictData(windowPtr,tempFileRefNum);
  }  
  if(osError == noErr)
    osError = FSClose(tempFileRefNum);
  if(osError == noErr)
    osError = FSClose((*docStrucHdl)->fileRefNum);
  if(osError == noErr)
    osError = FSpExchangeFiles(&fileSpecTemp,&fileSpecActual);
  if(osError == noErr)
    osError = FSpDelete(&fileSpecTemp);
  if(osError == noErr)
    osError = FSpOpenDF(&fileSpecActual,fsRdWrPerm,&(*docStrucHdl)->fileRefNum);

  if(osError == noErr)
    osError = doCopyAppNameResource(windowPtr);

  return(osError);
}

//  doReadTextFile

OSErr  doReadTextFile(WindowPtr windowPtr)
{
  docStructureHandle  docStrucHdl;
  SInt16              fileRefNum;
  TEHandle            textEditHdl;
  SInt32              numberOfBytes;
  Handle              textBuffer;
  OSErr               osError;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
  fileRefNum = (*docStrucHdl)->fileRefNum;

  textEditHdl = (*docStrucHdl)->editStrucHdl;
  (*textEditHdl)->txSize = 10;
  (*textEditHdl)->lineHeight = 15;

  SetFPos(fileRefNum,fsFromStart,0);
  GetEOF(fileRefNum,&numberOfBytes);

  if(numberOfBytes > 32767)
    numberOfBytes = 32767;

  if(!(textBuffer = NewHandle((Size) numberOfBytes)))
    return(MemError());

  osError = FSRead(fileRefNum,&numberOfBytes,*textBuffer);
  if(osError == noErr || osError == eofErr)
  {
    MoveHHi(textBuffer);
    HLockHi(textBuffer);
    TESetText(*textBuffer,numberOfBytes,(*docStrucHdl)->editStrucHdl);
    HUnlock(textBuffer);
    DisposeHandle(textBuffer);
  }
  else
    return(osError);

  return(noErr);
}

//  doReadPictFile

OSErr  doReadPictFile(WindowPtr windowPtr)
{
  docStructureHandle  docStrucHdl;
  SInt16              fileRefNum;
  SInt32              numberOfBytes;
  OSErr               osError;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
  fileRefNum = (*docStrucHdl)->fileRefNum;

  GetEOF(fileRefNum,&numberOfBytes);
  SetFPos(fileRefNum,fsFromStart,512);
  numberOfBytes -= 512;

  if(!((*docStrucHdl)->pictureHdl = (PicHandle) NewHandle(numberOfBytes)))
    return(MemError());

  osError = FSRead(fileRefNum,&numberOfBytes,*(*docStrucHdl)->pictureHdl);
  if(osError == noErr || osError == eofErr)
    return(noErr);
  else
    return(osError);
}

//  doWriteTextData

OSErr  doWriteTextData(WindowPtr windowPtr,SInt16 tempFileRefNum)
{
  docStructureHandle  docStrucHdl;
  TEHandle            textEditHdl;
  Handle              editText;
  SInt32              numberOfBytes;
  SInt16              volRefNum;
  OSErr               osError;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
  textEditHdl = (*docStrucHdl)->editStrucHdl;
  editText = (*textEditHdl)->hText;
  numberOfBytes = (*textEditHdl)->teLength;

  osError = SetFPos(tempFileRefNum,fsFromStart,0);
  if(osError == noErr)
    osError = FSWrite(tempFileRefNum,&numberOfBytes,*editText);
  if(osError == noErr)
    osError = SetEOF(tempFileRefNum,numberOfBytes);
  if(osError == noErr)
    osError = GetVRefNum(tempFileRefNum,&volRefNum);
  if(osError == noErr)
    osError = FlushVol(NULL,volRefNum);

  if(osError == noErr)
    (*docStrucHdl)->windowTouched = false;

  return(osError);
}

//  doWritePictData

OSErr  doWritePictData(WindowPtr windowPtr,SInt16 tempFileRefNum)
{
  docStructureHandle  docStrucHdl;
  PicHandle           pictureHdl;
  SInt32              numberOfBytes, dummyData;
  SInt16              volRefNum;
  OSErr               osError;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
  pictureHdl = (*docStrucHdl)->pictureHdl;

  numberOfBytes = 512;
  dummyData = 0;

  osError = SetFPos(tempFileRefNum,fsFromStart,0);

  if(osError == noErr)
    osError = FSWrite(tempFileRefNum,&numberOfBytes,&dummyData);

  numberOfBytes = GetHandleSize((Handle) (*docStrucHdl)->pictureHdl);

  if(osError == noErr)
  {
    HLock((Handle) (*docStrucHdl)->pictureHdl);
    osError = FSWrite(tempFileRefNum,&numberOfBytes,*(*docStrucHdl)->pictureHdl);
    HUnlock((Handle) (*docStrucHdl)->pictureHdl);
  }

  if(osError == noErr)
    osError = SetEOF(tempFileRefNum,512 + numberOfBytes);
  if(osError == noErr)
    osError = GetVRefNum(tempFileRefNum,&volRefNum);
  if(osError == noErr)
    osError = FlushVol(NULL,volRefNum);

  if(osError == noErr)
    (*docStrucHdl)->windowTouched = false;

  return(osError);
}

//  doCopyAppNameResource

OSErr  doCopyAppNameResource(WindowPtr windowPtr)
{
  docStructureHandle  docStrucHdl;
  OSType              fileType;
  OSErr               osError;
  SInt16              fileRefNum;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);

  if((*docStrucHdl)->editStrucHdl)
    fileType = 'TEXT';
  else if((*docStrucHdl)->pictureHdl)
    fileType = 'PICT';

  FSpCreateResFile(&(*docStrucHdl)->fileFSSpec,'KKKB',fileType,smSystemScript);

  osError = ResError();
  if(osError == noErr)
    fileRefNum = FSpOpenResFile(&(*docStrucHdl)->fileFSSpec,fsRdWrPerm);

  if(fileRefNum > 0)
    osError = doCopyResource('STR ',-16396,gAppResFileRefNum,fileRefNum);
  else
    osError = ResError();

  if(osError == noErr)
    CloseResFile(fileRefNum); 

  osError = ResError();
  return(osError);
}

//  doCopyResource

OSErr  doCopyResource(ResType resourceType,SInt16 resourceID,SInt16 sourceFileRefNum,
                      SInt16 destFileRefNum)
{
  Handle  sourceResourceHdl;
  Str255  sourceResourceName;
  ResType ignoredType;
  SInt16  ignoredID;

  UseResFile(sourceFileRefNum);

  sourceResourceHdl = GetResource(resourceType,resourceID);

  if(sourceResourceHdl != NULL)
  {
    GetResInfo(sourceResourceHdl,&ignoredID,&ignoredType,sourceResourceName);
    DetachResource(sourceResourceHdl);
    UseResFile(destFileRefNum);
    AddResource(sourceResourceHdl,resourceType,resourceID,sourceResourceName);
    if(ResError() == noErr)
      UpdateResFile(destFileRefNum);
  }

  ReleaseResource(sourceResourceHdl);

  return(ResError());
}

// 
// FiltersAndHooks.c
// 

// ............................................................................. includes

#include "Files1.h"

// ..................................................................... global variables

SInt16  gCurrentType = 1;
Str255  gPrevSelectedName;
Boolean gDirectorySelectionFlag;

extern SFTypeList  gFileTypes;

//  filterFunctionOpenDialog

pascal Boolean  filterFunctionOpenDialog(CInfoPBPtr pbPtr,void *dataPtr)
{
  if(pbPtr->hFileInfo.ioFlFndrInfo.fdType == gFileTypes[gCurrentType - 1])
    return false;
  else
    return true;
}

//  hookFunctionOpenDialog

pascal SInt16  hookFunctionOpenDialog(SInt16 item,DialogPtr theDialog,void *dataPtr)
{
  SInt16  theType;
  Handle  controlHdl;
  Rect    theRect;
  
  switch(item)
  {
    case sfHookFirstCall:
      GetDialogItem(theDialog,iPopupItem,&theType,&controlHdl,&theRect);
      SetControlValue((ControlHandle) controlHdl,gCurrentType);
      return sfHookNullEvent;
      break;
      
    case iPopupItem:
      GetDialogItem(theDialog,iPopupItem,&theType,&controlHdl,&theRect);
      theType = GetControlValue((ControlHandle) controlHdl);
      if(theType != gCurrentType)
      {
        gCurrentType = theType;
        return sfHookRebuildList;
      }
      break;
  }
  
  return item;
}

//  doDirectorySelectionDialog

StandardFileReply  doDirectorySelectionDialog(void)
{
  StandardFileReply stdFileReplyStruct;
  SFTypeList        fileTypes;
  Point              dialogLocation;
  FileFilterYDUPP   filterFunctionDirSelectUPP;
  DlgHookYDUPP      hookFunctionDirSelectUPP;
    
  filterFunctionDirSelectUPP = NewFileFilterYDProc((ProcPtr) filterFunctionDirSelect);
  hookFunctionDirSelectUPP = NewDlgHookYDProc((ProcPtr) hookFunctionDirSelect);

  gPrevSelectedName[0] = 0;
  gDirectorySelectionFlag = true;
  dialogLocation.v = -1;
  dialogLocation.h = -1;

  CustomGetFile(filterFunctionDirSelectUPP,-1,fileTypes,&stdFileReplyStruct,
                rSelectDirectoryDialog,dialogLocation,hookFunctionDirSelectUPP,NULL,NULL,
                NULL,&stdFileReplyStruct);

  DisposeRoutineDescriptor(filterFunctionDirSelectUPP);
  DisposeRoutineDescriptor(hookFunctionDirSelectUPP);

  return stdFileReplyStruct;
}    

//  filterFunctionDirSelect

pascal Boolean  filterFunctionDirSelect(CInfoPBPtr pbPtr,void *dataPtr)
{
  SInt32  attributes;
  Boolean result;

  attributes = (SInt32) pbPtr->hFileInfo.ioFlAttrib;
  result = !(BitTst(&attributes,31 - 4));
  return result;
}

//  hookFunctionDirSelect

pascal SInt16  hookFunctionDirSelect(SInt16 item,DialogPtr theDialog,void *dataPtr)
{
  SInt16                itemType, width;
  Handle                itemHdl;
  Rect                  itemRect;
  Str255                theName, theString =  "\pSelect  '";
  standardFileReplyPtr  stdFileReplyPtr;

  stdFileReplyPtr = (standardFileReplyPtr) dataPtr;

  if(stdFileReplyPtr->sfIsFolder || stdFileReplyPtr->sfIsVolume)
  {
    doCopyPString(stdFileReplyPtr->sfFile.name,theName);

    if(IdenticalString(theName,gPrevSelectedName,NULL) != 0)
    {
      doCopyPString(theName,gPrevSelectedName);
  
      GetDialogItem(theDialog,iSelectButton,&itemType,&itemHdl,&itemRect);
      width = (itemRect.right - itemRect.left) - StringWidth("\pSelect  '    ");
      TruncString(width,theName,smTruncMiddle);
      doConcatPStrings(theString,theName);
      doConcatPStrings(theString,"\p'");

      SetControlTitle((ControlHandle) itemHdl,theString);
    }
  }

  if(item == iSelectButton)
    return sfItemCancelButton;
  else if(item == sfItemCancelButton)
    gDirectorySelectionFlag = false;

  return item;
}

// 

Demonstration Program Comments

When the program is run, the user should:

*   Exercise the File menu by opening the supplied TEXT and PICT files, saving those
    files, saving those files under new names, closing files, opening the new files,
    attempting to open files which are already open, attempting to save files to new
    files with existing names, making open windows "touched" by choosing the first item
    in the Demonstration menu item, reverting to the saved versions of files associated
    with "touched" windows, choosing Quit when "touched" and non-"touched" windows are
    open, and so on.

*   Choose, via the File types pop-up menu button, the file types required to be
    displayed in the customised Open dialog.

*   Choose the Directory Selection Dialog item from the Demonstration menu to display
    the directory selection dialog (a customised Open dialog), and select a directory
    using the Select button at the bottom of the dialog.  (The name of the selected
    directory will be drawn in the bottom-left corner of the front window.)

The program may be run from within CodeWarrior to demonstrate responses to the File menu
commands and the directory selection dialog.  

The built application, together with the supplied TEXT and PICT files, may be used to
demonstrate the additional aspect of integrating the receipt of required Apple events
with the overall file handling mechanism.  To prove the correct handling of the required
Apple events, the user should:

*   Open the application by double-clicking the application icon, noting that a new
    document window is opened after the application is launched and the Open Application
    event is received.

*   Double click on a document icon, or select one or more document icons and either
    drag those icons to the application icon or choose Open from the Finder's File menu,
    noting that the application is launched and the selected files are opened when the
    Open Documents event is received.

*   Double click on a document icon, or select one or more document icons and either
    drag those icons to the application icon or choose Open from the Finder's File menu,
    noting that the application is launched and the selected files are opened when the
    Open Documents event is received.

*   With several documents open, some with "touched" windows, choose Restart or Shut
    Down from the Finder's Special menu (thus invoking a Quit Application event), noting
    that, for "touched" windows, an alert box is presented asking the user whether the
    file should be saved before the shutdown process proceeds.

Files1.h

#typedef

Each window created by the program will have an associated document structure, accessed
via the window structure's refCon field.  The docStructure structure will be used for
document structures.

The editRec field will be assigned a handle to a TextEdit edit structure ('TEXT' files). 
The pictureHdl field will be assigned a handle to a Picture structure ('PICT' files). 
The fileRefNum and fileFSSpec fields will be assigned the file reference number and the
file system specification structure of the file associated with the window.  The
windowTouched field will be set to true when a window has been made "touched", that is,
when the associated document in memory has been modified by the user.

The second type defined will be used in the dialog hook function for the directory
selection dialog.

#define

After the usual constants relating to menus, windows, and alert boxes are established,
additional constants are established for the customised Open dialog's 'DLOG' resource and
its additional item, the directory selection dialog's 'DLOG' resource and its additional
item, a 'STR#' resource containing error strings, and three specific error conditions. 
kMaxWindows is used to limit the number of windows the user can open.  kUserCancelled is
used when the user clicks the Cancel button of a particular alert box

Files1.c

Files1.c is simply the basic "engine" which supports the demonstration.  There is little
in this file which has not featured in previous demonstration programs.

Global Variables

gDone controls termination of the main loop and thus of the program.  gInBackground
relates to foreground/background switching.  The next three globals will be assigned
universal procedure pointers to the required Apple event handlers.  gAppResFileRefNum
will be assigned the file reference number of the application's resource fork.

main

Within the main function, routine descriptors for the required Apple events (less the
Print Documents event) are created and a call is made to the application-defined function
which installs the handlers.  Also, the file reference number of the application's
resource fork (which is opened automatically at application launch) is assigned to the
global variable gAppResFileRefNum.

doInstallAEHandlers

doInstallAEHandlers installs handlers for the Open Application, Open Documents, and Quit
Application events.  (Note that, so as to avoid the necessity to include
application-defined printing functions in this program, a handler for the Print Documents
event is not included in this demonstration.)

doUpdate

doUpdate performs such window updating as is necessary for the satisfactory execution of
the demonstration aspects of the program.

doMenuChoice

If the second item in the Demonstration menu is chosen, the application-defined function
which presents the directory selection dialog is called.  This function returns a
standard file reply structure.  If a window is open, a rectangle in the bottom corner of
the front window is erased.  If the dialog was dismissed using the Select button (not the
Cancel button), the selected directory name, volume reference number, and parent
directory ID are extracted from the standard file reply structure and drawn in the bottom
of the window.

doFileMenu

doFileMenu handles File menu choices.  In each case, the relevant application-defined
function is called and, if that function returns an error, the application-defined
function doErrorAlert is called.  Note that, in the case of the Quit command, gDone is
set to true after doQuitCommand returns, thus causing the program to terminate.

doErrorAlert

doErrorAlert handles errors, invoking an appropriate alert box (caution or stop) advising
of the nature of the problem by error code number or straight text.  Note that he program
will only be terminated in the case of the memFullErr error (no more space in the
application heap).

doTouchWindow

doTouchWindow is called when the user chooses the Touch Window item in the Demonstration
menu.  Changing the content of the in-memory version of a file is only simulated in this
program.  The text "WINDOW TOUCHED" is drawn in window and the windowTouched field of the
document structure is set to true.

doOpenAppEvent, doOpenDocsEvent, and doQuitAppEvent

The handlers for the required Apple events are essentially identical to those in the
demonstration program at Chapter 10 - Required Apple Events.

Most programs should simply open a new untitled window on receipt of an Open Application
event.  Accordingly, doOpenAppEvent simply calls the same function (doNewCommand) as is
called when the user chooses New from the File menu. 

On receipt of a Re-Open Application event, if no windows are currently open, doNewCommand
is called to open a window.

The demonstration program supports both 'TEXT' and 'PICT' files.  On receipt of an Open
Application event, it is thus necessary to determine the type of each file specified in
the event.  Accordingly, within doOpenDocsEvent, the call to FSpGetFInfo returns the
Finder information from the volume catalog entry for the file relating to the specified
FSSpec structure.  The fdType field of the FInfo structure "filled-in" by FSpGetFInfo
contains the file type.  This, together with the FSSpec structure, is then passed in the
call to doOpenFile.  (doOpenFile is also called when the user chooses Open from the File
menu.)

Within the function doQuitAppEvent, the while loop entered at repeats for each open
window.  Within the loop, doCloseCommand is called.  doCloseCommand, in turn, calls
doCloseFile.  doCloseFile presents a Yes/No/Cancel caution alert.  If an error is
returned by this sequence, and if the user did not click the Cancel button in the alert,
the error handler is called.  If the user clicked the Cancel button, it is necessary to
interrupt the sequence of closing all open windows and re-enter the main event loop.  

When the while loop eventually exits, gDone is set to true, causing the program to
terminate.

NewOpenCloseSave.c

Global Variables

gWindowPtr is assigned the pointer to the graphics port of each new window as it is
opened. gCurrentNumberofWindows keeps a count of the number of windows opened.  gDestRect
and gViewRect are used to set the destination and view rectangles for the edit structures
associated with 'TEXT' files.  gFileTypes will control the file types to be displayed in
the Open dialog box.

doNewCommand

doNewCommand is the first of the file-handling functions.  It is called when the user
chooses New from the File menu and when an Open Application event is received.

Since this demonstration does not support the actual entry of text or the drawing of
graphics, the document type passed to doNewDocWindow immaterial.  The document type
'TEXT' is passed in this instance simply to keep doNewDocWindow happy.

doOpenCommand

doOpenCommand is called when the user chooses Open from the File menu.

The first two lines create routine descriptors for the file filter and dialog hook
functions utilised by the customised Open dialog box.  At the next two lines, the first
two elements of the gFileTypes are assigned the file types to be displayed.  -1 is then
assigned to both fields of dialogLocation.

The call to CustomGetFile presents the customised Open dialog box.  The first parameter
is a UPP to an application-defined file filter function.  As will be seen, this filter
function will either allow or block the display of one or other of the two file types
specified in the first two elements of gFileTypes.  The sixth parameter ordinarily
specifies the location of the top-left of the dialog box on the screen; however, the
assignment of -1 to both fields of dialogLocation will cause CustomGetFile to centre the
dialog box on the screen.  Note also that the next parameter is a UPP to the
application-defined dialog hook function which handles user interaction with the dialog.

When the dialog box is dismissed, the routine descriptors are disposed of.

The sfType field of the StandardFileReply structure "filled-in" by CustomGetFile
(fileReply) contains the file type of the file selected by the user and the sfFile field
contains the file system specification.  If the user clicks the OK button (the third last
line), these are passed to the application-defined function doOpenFile.

To use the standard Open dialog, delete the UPP and Point variables and their
associated code, make gFilesType a local variable, and replace the call to 
CustomGetFile with:

     StandardGetFile(NULL,2,gFileTypes,&fileReply);

doCloseCommand

doCloseCommand is called when the user chooses Close from the File menu or clicks in the
window's go-away box.  It is also called successively for each open window when a Quit
Application event is received.

The first two lines get the WindowPtr for the front window and establish whether the
front window is a document window or a modeless dialog box.

If the front window is a document window, the handle to the window's document structure
is retrieved from the window structure's refCon field.  The WindowPtr and this handle are
then passed to the application-defined function doCloseFile.  If the window is "touched",
doCloseFile presents an alert box asking the user whether the document should be saved
before it is closed.  If the user clicks the Cancel button of that alert box, doCloseFile
returns kUserCancelled, in which case doCloseCommand returns kUserCancelled.  If the user
clicks either the Yes or No buttons of the alert box, and if doCloseFile returns no
error, the window is closed as the final act in closing the file, and the global variable
which keeps track of the number of open windows is decremented.

No modeless dialog boxes are used by this program.  However, if the front window was a
modeless dialog box, the appropriate action would be taken at the second case.

doSaveCommand

doSaveCommand is called when the user chooses Save from the File menu.  It may also be
called by doCloseFile if the user is attempting to close a "touched" window.

Th  first two lines get the WindowPtr for the front window and retrieve the handle to
that window's document structure.  If a file currently exists for the document in this
window, the application-defined function doWriteFile is called, otherwise the
application-defined function doSaveAsCommand is called.

doSaveAsCommand

doSaveAsCommand is called when the user chooses Save As... from the File menu.  It is
also called by doSaveCommand if the user chooses Save when the front window contains a
document for which no file currently exists.

The first two lines get the WindowPtr for the front window and retrieve the handle to
that window's document structure.

The call to StandardPutFile presents the Save dialog box.  The remaining code executes
only if the user clicks on the Save button.

If the sfReplacing field of the StandardFileReply structure "filled-in" by
StandardPutFile indicates that an existing file is not being replaced, the file type is
retrieved from the document structure for the front window and FSpCreate is called to
create a new file of that type, specifying the application's signature as the creator.

The file system specification structure returned in the sfFile field of the
StandardFileReply structure tis then assigned to the fileFSSpec field of the document
structure.

If a file currently exists for the document, that file is closed by the call to FSClose.

The data fork of the newly created file is then opened by a call to FSpOpenDF, the
fileRefNum field of the document structure is assigned the file reference number returned
by FSpOpenDF, the window's title is set to the new file's name, and the
application-defined function doWriteFile is called to write the document to the new file.

doRevertCommand

doRevertCommand is called when the user chooses Revert to Saved from the File menu.

The first three lines get the WindowPtr for the front window, retrieve the handle to that
window's document structure, and retrieve the file reference number from the document
structure.

The call to GetWTitle gets the window's title (that is, the filename) for insertion by
ParamText into the text of the alert box invoked by the call to CautionAlert.  (The alert
box asks the user to confirm, or otherwise, the reversion to the last saved version.)

If the user clicks the OK button, the window's content area is erased and the appropriate
application-defined function (doReadTextFile or doReadPictFile) is called depending on
whether the file type is 'TEXT' or 'PICT'.  In addition, the window's "touched" field in
the document structure is set to false and InvalRect is called to force a redraw of the
window's content region.

doQuitCommand

doQuitCommand is called when the user chooses Quit from the File menu and when a Quit
Application event is received.

The while loop continues to execute until no more windows remain open.  On each pass
through the loop, doCloseCommand is called to manage the process of closing (and, where
necessary, saving) all documents and disposing of the associated windows.

doNewDocWindow

doNewDocWindow is called by doNewCommand, doOpenFile and the Open Application event
handler.  It creates a new window and associated document structure.

If the current number of open windows is the maximum allowable by this program, the
function immediately exits, passing an error code which will cause an advisory error
alert box to be displayed.

The call to GetNewCWindow opens a new window.  SetPort sets that window's graphics port
as the current port for drawing.

The call to NewHandle allocates memory for the window's document structure.  If this call
is not successful, the window is disposed of and the function returns with the error code
returned by MemError.

The call to SetWRefCon assigns the handle to the document structure to the window
structure's refCon field.  The next four lines initialise fields of the document
structure.

If the document type is 'TEXT', the if block executes, creating a TextEdit edit structure
and assigning a handle to that structure to the editRec field of the document structure. 
(Note that the processes here are not explained in detail because TextEdit and edit
structures are not central to the demonstration.  For the purposes of the demonstration,
it is sufficient to understand that the text data retrieved from, and saved to, disk is
stored in a TextEdit edit structure.  TextEdit is addressed in detail at Chapter 19 -
Text and TextEdit.)

If the Boolean value passed to doNewDocWindow was set to true, the call to ShowWindow
makes the window visible, otherwise the window is left invisible.  The penultimate line
increments the global variable which keeps track of the number of open windows.

doOpenFile

doOpenFile is called by doOpenCommand and the Open Documents event handler, which pass to
it the file system specification structure and document type.  doOpenFile opens a new
document window and calls the application-defined functions which read in the file.

The call to doNewDocWindow opens a new window and creates an associated document
structure.  SetWTitle sets the window's title.  FSpOpenDF opens the file's data fork.  If
this call is not successful, the window is disposed of and the function returns.  The
next three lines assign the file reference number and file system specification structure
to the relevant fields of the document structure.

The next block calls the appropriate function for reading in the file, depending on
whether the file type is of type 'TEXT' or 'PICT'.  If the file is read in successfully,
ShowWindow makes the window visible.

doCloseFile

doCloseFile is called by doCloseCommand.  doCloseFile does not allow a "touched" window
to be closed without offering the user the option of first saving the associated document
to file.

If the window is touched, a caution alert is presented asking the user whether the
document should be saved.  (GetWTitle and ParamText insert the window title into the text
in the alert box.)  The alert box contains Yes, No and Cancel buttons.  If the user
clicks Cancel, the function returns kUserCancelled.  If the user clicks Yes, the
application-defined function doSaveCommand() is called to save the file.

If the user clicks Yes or No, the next block executes:

*   If the document has a file, FSClose closes the file, and FlushVol stores to disk all
    unwritten data currently in the volume buffer.

*   If the document is a text document, the text edit structure is disposed of.  If it
    is a picture document, the Picture structure is disposed of.  Finally, the document
    structure is disposed of.

doWriteFile

doWriteFile is called by doSaveCommand and doSaveAsCommand.  In conjunction with two
supporting application-defined functions, it writes the document to disk using the
"safe-save" procedure.

The first two lines retrieve a handle to the document structure and the file system
specification from the document structure.  

The next two lines create a temporary file name which is bound to be unique.  FindFolder
finds the temporary folder on the file's volume, or creates a temporary folder if
necessary.  FSMakeFSSpec makes a file system specification structure for the temporary
file, using the volume reference number and parent directory ID returned by the
FindFolder call.  FSpCreate creates the temporary file in that directory on that volume,
and FSpOpenDF opens the file's data fork.

Within the next if block, the appropriate application-defined function is called to write
the document's data to the temporary file.

The two calls to FSClose close both the temporary and existing files prior to the call to
FSpExchangeFiles, which swaps the files' data by changing the information in the volume's
catalog.  The temporary file is then deleted  and the data fork of the existing file is
re-opened.

The application-defined function doCopyAppNameResource is called to copy the missing
application name string resource from the resource fork of the application file to the
resource fork of the new document file.

doReadTextFile

doReadTextFile is called by doOpenFile and doRevertCommand to read in data from an open
file of type 'TEXT'.

The first two lines retrieve the file reference number from the document structure.  

The next three lines retrieve the handle to the TextEdit edit structure from the document
structure and modify the text size and line height fields of the edit structure.

SetFPos sets the file mark to the beginning of the file.  GetEOF gets the number of bytes
in the file.  If the number of bytes exceeds that which can be stored in a TextEdit edit
structure (32,767), the number of bytes which will be read from the file is restricted to
32,767.

NewHandle allocates a buffer equal to the size of the file (or 32,767 bytes if the
preceding if statement executed).  FSRead reads the data from the file into the buffer. 
MoveHHi and HLockHi move the buffer high in the heap and lock it preparatory to the call
to TESetText.  TESetText copies the text in the buffer into the existing hText handle of
the TextEdit edit structure.  The buffer is then unlocked and disposed of.

(Note:  TextEdit is addressed in detail at Chapter 19 - Text and TextEdit.)

doReadPictFile

doReadPictFile is called by doOpenFile and doRevertCommand to read in data from an open
file of type 'PICT'.

The first two lines retrieve the file reference number from the document structure. 
GetEOF gets the number of bytes in the file.  SetFPos sets the file mark 512 bytes (the
size of a 'PICT' file's header) past the beginning of the file, and the next line
subtracts the header size from the total size of the file.  NewHandle allocates memory
for the Picture structure and FSRead reads in the file's data.

doWriteTextData

doWriteTextData is called by doWriteFile to write text data to the specified file.

The first two lines retrieve the handle to the TextEdit edit structure from the document
structure.  The number of bytes of text is then retrieved from the teLength field of the
text edit structure.

SetFPos sets the file mark to the beginning of the file.  FSWrite writes the specified
number of bytes to the file.  SetEOF adjusts the file's size.  FlushVol stores to disk
all unwritten data currently in the volume buffer.

The penultimate line sets the windowTouched field of the document structure to indicate
that the document data on disk equates to the document data in memory.

doWritePictData

doWritePictData is called by doWriteFile to write picture data to the specified file.

The first two lines retrieve the handle to the relevant Picture structure from the
document structure.  SetFPos sets the file mark to the start of the file.  FSWrite writes
zeros in the first 512 bytes (the size of a 'PICT' file's header).  GetHandleSize gets
the size of the Picture structure and FSWrite writes the bytes in the Picture structure
to the file.  SetEOF adjusts the file's size and FlushVol stores to disk all unwritten
data currently in the volume buffer.

The penultimate line sets the windowTouched field of the document structure to indicate
that the document data on disk equates to the document data in memory.

doCopyAppNameResource

doCopyAppNameResource is called by doWriteFile when a newly created file has been written
to for the first time.  It copies the missing application name string resource from the
resource fork of the application file to the resource fork of the new file.

The first line retrieves a handle to the file's document structure.  The next four lines
establish the file type involved.  FSpCreateResFile creates the resource fork in the new
file and FSpOpenResFile opens the resource fork.  The application-defined function for
copying specified resources between specified files (doCopyResource) is then called.  In
this case, the specified resource is the missing application name string resource, the
source resource file is the resource fork of the application file, and the destination
resource file is the resource fork of the new file.

CloseResFile closes the resource fork of the new file.

doCopyResource

doCopyResource copies specified resources between specified files.  In this program, it
is called only by doCopyAppNameResource.

UseResFile sets the application's resource fork as the current resource file. 
GetResource reads the specified resource into memory.

GetResInfo, given a handle, gets the resource type, ID and name.  (Note that this line is
included only because of the generic nature of doCopyResource.  The calling function has
passed doCopyResource the type and ID in this instance.)

DetachResource removes the resource's handle from the resource map without removing the
resource from memory, and converts the resource handle into a generic handle.  UseResFile
makes the new file's resource fork the current resource file.  AddResource makes the now
arbitrary data in memory into a resource, assigns a resource ID, type and name to that
resource, and inserts an entry in the resource map for the current resource file. 
UpdateResFile then writes the resource map and data to disk.

FiltersAndHooks.c

FiltersAndHooks.c contains the file filter and dialog hook functions for the customised
Open dialog and the directory selection dialog, together with the function which calls up
the directory selection dialog.

filterFunctionOpenDialog

filterFunctionOpenDialog is the file filter function for the customised Open dialog.

The global variable gCurrentType contains an index into the gFileTypes array (see the
function doOpenCommand, above).  As will be seen, the index changes according to the item
chosen by the user using the two-item pop-up menu button added to the dialog box.

If the file type passed in for evaluation matches the current file type indexed by
gFileTypes, the filter returns false, indicating that CustomGetFile should put it in the
list.  All other file types are blocked as a result of the filter function returning
true.

hookFunctionOpenDialog

hookFunctionOpenDialog is the hook function for the customised Open dialog.  It handles
item selection within the dialog.  CustomGetFile calls this function immediately after
calling ModalDialog, passing the function the item number returned by ModalDialog.

A dialog hook function is required to return the item number passed to it or some other
item number.  This includes "psuedo item numbers", that is, constants which do not
represent actual items in the item list.  There are two types of psuedo items: psuedo
items passed to a dialog hook function by the Standard File Package; psuedo items passed
to the Standard File Package by a dialog hook function.

The sfHookFirstCall constant (see the first case in the switch statement) is an example
of the first kind of psuedo item.  This psuedo item is sent to the dialog hook function
immediately before the dialog is displayed.  The dialog hook function typically reacts to
this item by performing any necessary initialisation.  In hookFunctionOpenDialog, this
initialisation involves setting the value of the pop-up menu button control (that is, the
current menu item) to equate to the initial value in the global variable gCurrentType
(1).

A dialog hook function can pass back psuedo items to request some action by the Standard
File Package, or to indicate that it needs no further action by the Standard File
Package.  The last action following the receipt of the sfHookFirstCall psuedo item is to
return the sfHookNullEvent, which indicates that no further action is required.

At the second case, if the pop-up menu button was hit, the control's value is retrieved
and compared with the value currently in the global gCurrentType.  If the two do not
match, gCurrentType is assigned the current value of the control (that is, the menu item
currently chosen) and the psuedo item sfHookRebuildList is returned.  This psuedo item
instructs the Standard File Package to rebuild the list of files and folders.

If the item passed to hookFunctionOpenDialog was not sfHookFirstCall or the pop-up menu
button item, the last line returns that item number to the Standard File Package for
processing.

DIALOG HOOK FUNCTIONS FOR CUSTOMISED SAVE DIALOGS
A dialog hook function for a customised Save dialog must account for the
fact that user action can result in another dialog opening on top of the Save
dialog.  For example, if the user hits the New Folder button, the New Folder
dialog box will open.  CustomPutFile will call your dialog hook function for
item selections in subsidiary dialogs as well as the main dialog.  Your dialog
hook function must therefore return immediately if the dialog pointer received
does not pertain to the customised Save dialog itself.  The content of the dialog
window's refCon field may be used to discriminate between the various dialogs.

The refCon field of a standard Save dialog's window contains sfMainDialogRefCon
(1937007718).  Accordingly, you can assign that value to your customised dialog
window's refCon field and include the following as the beginning of your dialog
hook function:

     if((GetWRefCon((WindowPtr) theDialog)) != (SInt32) sfMainDialogRefCon)
       return;

doDirectorySelectionDialog

doDirectorySelectionDialog is called when the user chooses the Directory Selection Dialog
item in the Demonstration menu.

The directory selection dialog box is a customised version of the standard Open dialog. 
It adds a static text item at the top and a "Select" push button immediately below the
list.

The first two lines create routine descriptors for the filter and hook functions.  Two
global variables are then initialised.  The first is used to store the selected directory
name and the second (gDirectorySelectionFlag) is a flag which the hook function will set
to false if the user hits the Cancel button in the dialog box.  The values assigned to
the fields of the dialogLocation variable ensure that the dialog will be displayed in the
centre of the screen.

CustomGetFile displays the dialog.  When the user dismisses the dialog, the routine
descriptors are disposed of and the standard file reply structure "filled in" by
CustomGetFile is returned to the calling function (doMenuChoice), where, if the dialog
was dismissed using the Select button (not the Cancel button), the selected directory
name, volume reference number, and parent directory ID are extracted and drawn in the
bottom of the window.

filterFunctionDirSelect

filterFunctionDirSelect is the filter function for the directory selection dialog.  It
inspects the appropriate bit in the file attributes field of the catalog information
parameter block passed to it.  If the directory bit (bit 4) is set, false is returned,
indicating that the item should appear in the list; otherwise, true is returned to
exclude the item from the list.

hookFunctionDirSelect

hookFunctionDirSelect is the hook function for the directory selection dialog.

No initialisation is required by this hook function.  Accordingly, although an
sfHookFirstCall psuedo item will be received just before the dialog is displayed, it is
disregarded.

If the sfIsFolder or sfIsVolume fields of the standard file reply structure pointed to by
dataPtr indicate that the selected item is a folder or a volume, and if the name
extracted from the standard file reply structure is not the same as that stored in the
global variable gPrevSelectedName, the new name is copied to gPrevSelectedName.  Also,
the new string for the Select push button's title is built up prior to a call to
SetControlTitle.  If the string is too wide for the push putton, it is centre-truncated.

If the item hit was the Select push button, a return is forced by faking a cancel (that
is, sfItemCancelButton is returned even though the Cancel button was not hit.  This will
cause the dialog to be dismissed.  If, on the other hand, the Cancel button was hit, the
global variable gDirectorySelectionFlag is set to false and the last line returns the
item number received (sfItemCancelButton), causing the dialog to be dismissed.  (In this
demonstration, gDirectorySelectionFlag simply allows or defeats the drawing of the
selected directory name, volume reference number, and parent directory ID in the bottom
of the window.)

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »
Explore some of BBCs' most iconic s...
Despite your personal opinion on the BBC at a managerial level, it is undeniable that it has overseen some fantastic British shows in the past, and now thanks to a partnership with Roblox, players will be able to interact with some of these... | Read more »
Play Together teams up with Sanrio to br...
I was quite surprised to learn that the massive social network game Play Together had never collaborated with the globally popular Sanrio IP, it seems like the perfect team. Well, this glaring omission has now been rectified, as that instantly... | Read more »
Dark and Darker Mobile gets a new teaser...
Bluehole Studio and KRAFTON have released a new teaser trailer for their upcoming loot extravaganza Dark and Darker Mobile. Alongside this look into the underside of treasure hunting, we have received a few pieces of information about gameplay... | Read more »

Price Scanner via MacPrices.net

Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more
Roundup of Verizon’s April Apple iPhone Promo...
Verizon is offering a number of iPhone deals for the month of April. Switch, and open a new of service, and you can qualify for a free iPhone 15 or heavy monthly discounts on other models: – 128GB... Read more
B&H has 16-inch MacBook Pros on sale for...
Apple 16″ MacBook Pros with M3 Pro and M3 Max CPUs are in stock and on sale today for $200-$300 off MSRP at B&H Photo. Their prices are among the lowest currently available for these models. B... Read more
Updated Mac Desktop Price Trackers
Our Apple award-winning Mac desktop price trackers are the best place to look for the lowest prices and latest sales on all the latest computers. Scan our price trackers for the latest information on... Read more
9th-generation iPads on sale for $80 off MSRP...
Best Buy has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80 off MSRP on their online store for a limited time. Prices start at only $249. Sale prices for online orders only, in-store prices... Read more
15-inch M3 MacBook Airs on sale for $100 off...
Best Buy has Apple 15″ MacBook Airs with M3 CPUs on sale for $100 off MSRP on their online store. Prices valid for online orders only, in-store prices may vary. Order online and choose free shipping... Read more
24-inch M3 iMacs now on sale for $150 off MSR...
Amazon is now offering a $150 discount on Apple’s new M3-powered 24″ iMacs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 24″ M3 iMac/8-core GPU/8GB/256GB: $1149.99, $150... Read more
15-inch M3 MacBook Airs now on sale for $150...
Amazon is now offering a $150 discount on Apple’s new M3-powered 15″ MacBook Airs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 15″ M3 MacBook Air/8GB/256GB: $1149.99, $... Read more

Jobs Board

Early Preschool Teacher - Glenda Drive/ *Appl...
Early Preschool Teacher - Glenda Drive/ Apple ValleyTeacher Share by Email Share on LinkedIn Share on Twitter Read more
Retail Assistant Manager- *Apple* Blossom Ma...
Retail Assistant Manager- APPLE BLOSSOM MALL Brand: Bath & Body Works Location: Winchester, VA, US Location Type: On-site Job ID: 04225 Job Area: Store: Management Read more
Housekeeper, *Apple* Valley Village - Cassi...
Apple Valley Village Health Care Center, a senior care campus, is hiring a Part-Time Housekeeper to join our team! We will train you for this position! In this role, Read more
Sonographer - *Apple* Hill Imaging Center -...
Sonographer - Apple Hill Imaging Center - Evenings Location: York Hospital, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now See Read more
Senior Software Engineer - *Apple* Fundamen...
…center of Microsoft's efforts to empower our users to do more. The Apple Fundamentals team focused on defining and improving the end-to-end developer experience in Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.