TweetFollow Us on Twitter

MACINTOSH C

Demonstration Program

Go to Contents
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// Windows.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// 
// This program:
//
// „  Allows the user to open any number of kWindowFullZoomGrowDocumentProc windows, up 
//    to the maximum specified by the constant assigned to the symbolic name kMaxWindows,
//    using the File menu Open Command or  its keyboard equivalent.
//
// „  Allows the user to close opened windows using the close box, the File menu Close
//     command or the Close command's keyboard equivalent.
//
// „  Adds menu items representing each window to a Windows  menu as each window is
//    opened  (A keyboard equivalent is included in each menu item for windows 1 to 9.)
//
// „  Deletes menu items from the Windows menu as each window is closed.
//
// „  Fills each window with a plain colour pattern as a means of proving, for 
//    demonstration purposes, the window update process.
//
// „  Facilitates activation of a window by mouse selection.
//
// „  Facilitates activation of a window by Windows menu selection.
//
// „  Correctly performs all dragging, zooming and sizing operations.
//
// „  Demonstrates the provision of balloon help for static windows.
//
// The program utilises the following resources:
//
// „  An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit and Windows menus 
//    (preload, non-purgeable).  
//
// „  A 'WIND' resource (purgeable) (initially not visible).  
//
// „  An 'ALRT' resource and 'DITL' resource for use by Stop Alerts (purgeable).  
//
// „  A 'STR#' resource containing strings for the window title and error Alert box 
//    (purgeable).
//
// „  An 'hrct' resource and an 'hwin' resource for balloon help (both purgeable).
//
// „  Ten 'ppat' (pixel pattern) resources (purgeable), which are used to draw a plain
//    colour pattern in the windows  
//
// „  A 'SIZE' resource with the acceptSuspendResumeEvents, doesActivateOnFGSwitch,
//    and is32BitCompatible flags set.    
//
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

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

#include <Appearance.h>
#include <Devices.h>
#include <Sound.h>
#include <ToolUtils.h>

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

#define mApple         128
#define  iAbout        1
#define mFile          129
#define mEdit          130
#define  iNew          1
#define  iClose        4
#define  iQuit         11
#define mWindows       131
#define rNewWindow     128
#define rMenubar       128
#define rStringList    128
#define  sUntitled     1
#define  eMaxWindows   2
#define  eFailWindow   4
#define  eFailMenus    5
#define  eFailMemory   6
#define rPixelPattern  128
#define kMaxWindows    10
#define MAXLONG        0x7FFFFFFF

#define MIN(a,b)       ((a) < (b) ? (a) : (b))

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

Boolean    gDone;
Boolean    gInBackground;
Ptr        gPreAllocatedBlockPtr;
SInt32     gUntitledWindowNumber    = 0;
SInt32     gCurrentNumberOfWindows  = 0;
WindowPtr  gWindowPtrArray[kMaxWindows + 2];

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

void  main                       (void);
void  doInitManagers             (void);
void  eventLoop                  (void);
void  doEvents                   (EventRecord *);
void  doMouseDown                (EventRecord *);
void  doUpdate                   (EventRecord *);
void  doUpdateWindow             (EventRecord *);
void  doActivate                 (EventRecord *);
void  doActivateWindow           (WindowPtr,Boolean);
void  doOSEvent                  (EventRecord *);
void  doMenuChoice               (SInt32);
void  doFileMenu                 (SInt16);
void  doWindowsMenu              (SInt16);
void  doNewWindow                (void);
void  doCloseWindow              (void);
void  doInvalidateScrollBarArea  (WindowPtr);
void  doSetStandardState         (WindowPtr);
void  doConcatPStrings           (Str255,Str255);
void  doErrorAlert               (SInt16);

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× main

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

  // .................... get nonrelocatable block low in heap for first window structure

  if(!(gPreAllocatedBlockPtr = NewPtr(sizeof(WindowRecord))))
    doErrorAlert(eFailMemory);

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

  doInitManagers();

  // .. cause the Appearance-compliant menu bar definition function to be called directly
  
  RegisterAppearanceClient();
  
  // .......................................................... set up menu bar and menus
  
  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    doErrorAlert(eFailMenus);
  SetMenuBar(menubarHdl);
  DrawMenuBar();

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

  // .................................................... initialize window pointer array

  for(a=0;a<kMaxWindows+2;a++)
    gWindowPtrArray[a] = NULL;

  // .................................................................... enter eventLoop

  eventLoop();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doInitManagers

void  doInitManagers(void)
{
  MaxApplZone();
  MoreMasters();
  MoreMasters();
  MoreMasters();
  MoreMasters();
  
  InitGraf(&qd.thePort);
  InitFonts();
  InitWindows();
  InitMenus();
  TEInit();
  InitDialogs(NULL);

  InitCursor();  
  FlushEvents(everyEvent,0);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× eventLoop

void  eventLoop(void)
{
  EventRecord  eventStructure;

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

    if(gPreAllocatedBlockPtr == NULL)
      if(!(gPreAllocatedBlockPtr = NewPtr(sizeof(WindowRecord))))
        doErrorAlert(eFailMemory);
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  SInt8  charCode;

  switch(eventStrucPtr->what)
  {
    case mouseDown:
      doMouseDown(eventStrucPtr);
      break;

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

    case updateEvt:
      doUpdate(eventStrucPtr);
      break;

    case activateEvt:
      doActivate(eventStrucPtr);
      break;

    case osEvt:
      doOSEvent(eventStrucPtr);
      HiliteMenu(0);
      break;
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMouseDown

void  doMouseDown(EventRecord *eventStrucPtr)
{
  WindowPtr windowPtr;
  SInt16    partCode;
  Rect      growRect;
  SInt32    newSize;

  partCode = FindWindow(eventStrucPtr->where,&windowPtr);
  
  switch(partCode)
  {
    case inMenuBar:
      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)
        doCloseWindow();
      break;

    case inGrow:
      growRect = qd.screenBits.bounds;
      growRect.top   = 80; 
      growRect.left = 160;
      newSize = GrowWindow(windowPtr,eventStrucPtr->where,&growRect);
      if(newSize != 0)
      {
        doInvalidateScrollBarArea(windowPtr);
        SizeWindow(windowPtr,LoWord(newSize),HiWord(newSize),true);
        doInvalidateScrollBarArea(windowPtr);
      }
      break;

    case inZoomIn:
    case inZoomOut:
      if(TrackBox(windowPtr,eventStrucPtr->where,partCode))
      {
        SetPort(windowPtr);
        EraseRect(&windowPtr->portRect);
        ZoomWindow(windowPtr,partCode,false);
      }
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doUpdate

void  doUpdate(EventRecord *eventStrucPtr)
{
  WindowPtr  windowPtr;

  windowPtr = (WindowPtr) eventStrucPtr->message;

  BeginUpdate(windowPtr);

  SetPort(windowPtr);
  doUpdateWindow(eventStrucPtr);

  EndUpdate(windowPtr);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doUpdateWindow

void  doUpdateWindow(EventRecord *eventStrucPtr)
{
  WindowPtr     windowPtr;
  Rect          theRect;
  SInt32        windowRefCon;
  PixPatHandle  pixpatHdl;
  RGBColor      whiteColour = { 0xFFFF,0xFFFF,0xFFFF };
  SInt16        a;
  
  windowPtr = (WindowPtr) eventStrucPtr->message;

  EraseRgn(windowPtr->visRgn);

  theRect = windowPtr->portRect;
  theRect.right -= 15;
  theRect.bottom -= 15;

  windowRefCon = GetWRefCon(windowPtr);
  pixpatHdl = GetPixPat(rPixelPattern + windowRefCon);
  FillCRect(&theRect,pixpatHdl);
  DisposePixPat(pixpatHdl);

  DrawGrowIcon(windowPtr);    

  RGBForeColor(&whiteColour);
  TextSize(10);

  for(a=0;a<2;a++)
  {
    SetRect(&theRect,a*90+10,10,a*90+90,33);
    FrameRect(&theRect);
    MoveTo(a*90+18,25);
    
    DrawString("\pHot Rectangle");
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doActivate

void  doActivate(EventRecord *eventStrucPtr)
{
  WindowPtr  windowPtr;
  Boolean    becomingActive;

  windowPtr = (WindowPtr) eventStrucPtr->message;

  becomingActive = ((eventStrucPtr->modifiers & activeFlag) == activeFlag);

  doActivateWindow(windowPtr,becomingActive);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doActivateWindow

void  doActivateWindow(WindowPtr windowPtr,Boolean becomingActive)
{
  MenuHandle  windowsMenu;
  SInt16      menuItem, a = 1;

  windowsMenu = GetMenuHandle(mWindows);

  while(gWindowPtrArray[a] != windowPtr)
    a++;    
  menuItem = a;
  
  if(becomingActive)
    CheckMenuItem(windowsMenu,menuItem,true);
  else
    CheckMenuItem(windowsMenu,menuItem,false);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doOSEvent

void  doOSEvent(EventRecord *eventStrucPtr)
{
  switch((eventStrucPtr->message >> 24) & 0x000000FF)
  {
    case suspendResumeMessage:
      if(gCurrentNumberOfWindows > 0)
      {
        gInBackground = (eventStrucPtr->message & resumeFlag) == 0;
        doActivateWindow(FrontWindow(),!gInBackground);
      }
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  SInt16  menuID, menuItem;
  Str255  itemName;
  SInt16  daDriverRefNum;
       
  menuID = HiWord(menuChoice);
  menuItem = LoWord(menuChoice);

  if(menuID == 0)
    return;

  switch(menuID)
  {
    case mApple:
      if(menuItem == iAbout)
        SysBeep(10);
      else
      {
        GetMenuItemText(GetMenuHandle(mApple),menuItem,itemName);
        daDriverRefNum = OpenDeskAcc(itemName);
      }
      break;
      
    case mFile:
      doFileMenu(menuItem);
      break;
      
    case mWindows:
      doWindowsMenu(menuItem);
      break;    
  }

  HiliteMenu(0);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doFileMenu

void  doFileMenu(SInt16 menuItem)
{
  switch(menuItem)
  {
    case iNew:
      doNewWindow();
      break;
    
    case iClose:
      doCloseWindow();
      break;      
      
    case iQuit:
      gDone = true;
      break;
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doWindowsMenu

void  doWindowsMenu(SInt16 menuItem)
{
  WindowPtr  windowPtr;

  windowPtr = gWindowPtrArray[menuItem];
  SelectWindow(windowPtr);  
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doNewWindow

void  doNewWindow(void)
{
  WindowPtr    windowPtr;
  Str255      untitledString;
  Str255      numberAsString;
  MenuHandle  windowsMenu;

  if(gCurrentNumberOfWindows == kMaxWindows)
  {
    doErrorAlert(eMaxWindows);
    return;
  }

  if(!(windowPtr = GetNewCWindow(rNewWindow,gPreAllocatedBlockPtr,(WindowPtr)-1)))
    doErrorAlert(eFailWindow);
  
  gPreAllocatedBlockPtr = NULL;

  GetIndString(untitledString,rStringList,sUntitled);
  NumToString(++gUntitledWindowNumber,numberAsString);
  doConcatPStrings(untitledString,numberAsString);
  SetWTitle(windowPtr,untitledString);

  doSetStandardState(windowPtr);

  ShowWindow(windowPtr);

  if(gUntitledWindowNumber < 10)
  {
    doConcatPStrings(untitledString,"\p/");
    NumToString(gUntitledWindowNumber,numberAsString);
    doConcatPStrings(untitledString,numberAsString);
  }
  windowsMenu = GetMenu(mWindows);
  InsertMenuItem(windowsMenu,untitledString,CountMenuItems(windowsMenu));    

  SetWRefCon(windowPtr,gCurrentNumberOfWindows);

  gCurrentNumberOfWindows ++;
  gWindowPtrArray[gCurrentNumberOfWindows] = windowPtr;  

  if(gCurrentNumberOfWindows == 1)
  {
    EnableItem(GetMenu(mFile),iClose);
    EnableItem(GetMenu(mWindows),0);
    DrawMenuBar();
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doCloseWindow

void  doCloseWindow(void)
{
  WindowPtr   windowPtr;
  MenuHandle  windowsMenu;
  SInt16      a = 1;

  windowPtr = FrontWindow();
  CloseWindow(windowPtr);
  DisposePtr((Ptr) (WindowPeek) windowPtr);
  gCurrentNumberOfWindows --;

  windowsMenu = GetMenu(mWindows);
  while(gWindowPtrArray[a] != windowPtr)
    a++;
  gWindowPtrArray[a] = NULL;
  DeleteMenuItem(windowsMenu,a);

  for(a=1;a<kMaxWindows+1;a++)
  {
    if(gWindowPtrArray[a] == NULL)
    {
      gWindowPtrArray[a] = gWindowPtrArray[a+1];
      gWindowPtrArray[a+1] = NULL;
    }
  }  

  if(gCurrentNumberOfWindows == 0)
  {
    DisableItem(GetMenu(mFile),iClose);
    DisableItem(GetMenu(mWindows),0);
    DrawMenuBar();
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doInvalidateScrollBarArea

void  doInvalidateScrollBarArea(WindowPtr windowPtr)
{
  Rect  tempRect;
  
  SetPort(windowPtr);

  tempRect = windowPtr->portRect;
  tempRect.left = tempRect.right - 15;
  InvalRect(&tempRect);

  tempRect = windowPtr->portRect;
  tempRect.top = tempRect.bottom - 15;
  InvalRect(&tempRect);
}  

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSetStandardState

void  doSetStandardState(WindowPtr windowPtr)
{
  WindowPeek  windowRecPtr;
  WStateData  *winStateDataPtr;
  Rect        tempRect;

  tempRect = qd.screenBits.bounds;  
  windowRecPtr = (WindowPeek) windowPtr;
  winStateDataPtr = (WStateData *) *(windowRecPtr->dataHandle);

  SetRect(&(winStateDataPtr->stdState),tempRect.left+40,tempRect.top+60,
          tempRect.right-40,tempRect.bottom-40);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× 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;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doErrorAlert

void  doErrorAlert(SInt16 errorType)
{
  AlertStdAlertParamRec paramRec;
  Str255                labelText;
  Str255                narrativeText;
  SInt16                itemHit;
    
  paramRec.movable        = false;
  paramRec.helpButton     = false;
  paramRec.filterProc     = NULL;
  paramRec.defaultText    = (StringPtr) kAlertDefaultOKText;
  paramRec.cancelText     = NULL;
  paramRec.otherText      = NULL;
  paramRec.defaultButton  = kAlertStdAlertOKButton;
  paramRec.cancelButton   = 0;
  paramRec.position       = kWindowAlertPositionMainScreen;

  GetIndString(labelText,rStringList,errorType);

  if(errorType == eMaxWindows)
  {
    GetIndString(narrativeText,rStringList,errorType + 1);
    StandardAlert(kAlertCautionAlert,labelText,narrativeText,¶mRec,&itemHit);
  }
  else
  {
    StandardAlert(kAlertStopAlert,labelText,0,¶mRec,&itemHit);
    ExitToShell();
  }  
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

Demonstration Program Comments

When this program is run, the user should:

*   Open and close windows using both the Open and Close commands from the File menu 
    and their keyboard equivalents, noting that, whenever a window is opened or closed,
    a  menu item representing that window is added to, or deleted from, the Windows 
    menu.

*   Note that keyboard equivalents are added to the menu items in the Windows menu for
    the first nine windows opened.

*   Activate individual windows by both clicking the content region and pressing the
    keyboard equivalent for the window.

*   Send the application to the background and bring it to the foreground, noting window
    activation/deactivation.

*   Zoom, close, and resize windows using the zoom, close and size boxes, noting window
    updating and activation.

*   Choose Show Balloons from the Help menu and move the cursor over the hot rectangles in
    any window.

If an attempt is made to open more than 10 windows, a modal alert box appears.  If Show
Balloons has been chosen, the user will note that the windows are not updated when
overlaying balloons are closed.  The reason for this, and the cure, is addressed at
Chapter 8 - Dialogs and Alerts.

#define

The first ten #defines establish constants representing menu IDs and resources, and window
and menu bar resources.  The next six establish constants representing the resource ID of
a 'STR#' resource and the various strings in that resource.  rPixelPattern represents the
resource ID of the first of ten 'ppat' (pixel pattern) resources.

kMaxWindows controls the maximum number of windows allowed to be open at one time. 
MAXLONG is defined as the maximum possible long value.  (This will be assigned to
WaitNextEvent's sleep parameter.)  The (fairly common) macro which follows is required by
the application-defined string concatenation function doConcatPStrings.

Global Variables

The global variable gDone, when set to true, causes the main event loop to be exited and
the program to terminate.  gInBackground relates to foreground/background switching.

gPreAllocatedBlockPtr will be assigned a pointer to a pre-allocated block of memory for a
window structure. gUntitledWindowNumber keeps track of the window numbers to be inserted
into the window's title bar.  This number is incremented each time a new window is opened. 
gCurrentNumberOfWindows keeps track of how many windows are open at any one time.

gWindowPtrArray[] is central to the matter of maintaining an association between item
numbers in the Windows menu and the windows to which they refer, regardless of how many
windows are opened and closed, and in what sequence.  When, for example, a Windows menu
item is chosen, the program must be able to locate the window structure for the window
represented by that menu item number so as to activate the correct window.

The strategy adopted by this program is to assign the pointers for each opened window to
the elements of gWindowPtrArray[], starting with gWindowPtrArray[1] and leaving
gWindowPtrArray[0] unused.  If, for example, six windows are opened in sequence,
gWindowPtrArray[1] to gWindowPtrArray[6] are assigned the window pointers for each of
those six windows.  (At the same time, menu items representing each of those windows are
progressively added to the Windows menu.)  

If, say, the third window opened is then closed, gWindowPtrArray[3] is set to NULL and the
window pointers in gWindowPtrArray[4] to gWindowPtrArray[6] are moved down in the array to
occupy gWindowPtrArray[3] to gWindowPtrArray[5]. Since the Windows menu item for the third
window is deleted from the menu when the window is closed, there remains five windows and
their associated menu items, the "compaction" of the array having maintained a direct
relationship between the number of the array element to which each window pointer is
assigned and the number of the menu item for that window. 

main

The first line in the main function requires some explanation.  When a window is created,
its window structure is contained in a nonrelocatable block of memory.  Any program that
allows the user to open many windows at any time during program execution must have a
strategy for allocating all window structures as low in the heap as possible, since
nonrelocatable blocks scattered within the heap contribute to memory fragmentation and
impede effective heap compaction by the Memory Manager.

The best times to allocate nonrelocatable blocks so as to ensure that they are located as
low in the heap as possible are:

*   At the beginning of the program (just before the system software managers are
    initialised).

*   At the bottom of the event loop just after all events have been handled to completion. 
    At this time, the heap is as empty as it will ever be.

This program adopts that strategy.  The first line in main() pre-allocates a
nonrelocatable block which will later be used by the window structure of the first window
to be created.  The pointer returned by the first call to GetNewCWindow, which will be
copied to gWindowPtrArray[1], will point to this block.  gPreAllocatedBlockPtr will then
be set to NULL.  At the bottom of the event loop, gPreAllocatedBlockPtr will be checked. 
If it contains NULL, the pre-allocated block must now be occupied by a window structure,
in which circumstance a new block will be allocated in preparation for the next window to
be opened.

If the call to NewPtr fails, the following line invokes an Alert box.

The system software managers are then initialised. 

The call to RegisterAppearanceClient means that the new Appearance-compliant menu bar
definition function (resource ID 63) will be used regardless of whether system-wide
Appearance is selected on or off in the Appearance control panel.

The next block sets up the menus.  Note that error handling involving the invocation of
alert boxes is introduced in this program.  If an error occurs, the application-defined
function doErrorAlert will display an alert box advising of the nature of the error before
terminating the program.

In the final three lines, gWindowPtrArray is initialised  and the main event loop is
entered.

doInitManagers

Note that MoreMasters must be called four times to provide sufficient master pointers for
this particular program.  (The requirement for two calls to MoreMasters was determined by
watching master pointer usage during program execution using ZoneRanger.)
eventLoop

eventLoop will exit when gDone is set to true, which occurs when the user selects Quit
from the File menu.  (As an aside, note that the sleep parameter in the WaitNextEvent call
is set to MAXLONG, which is defined as the maximum possible long value.)

At the bottom of the event loop, a new nonrelocatable block is allocated in preparation
for the next window to be opened if the global variable gPreAllocatedBlockPtr contains
NULL.

doEvents

doEvents switches according to the event type received.

mouseDown, upDate, activateEvt and osEvt events are of significance to the windows aspects
of this demonstration.  To that extent, keyDown events are significant only with regard to
Windows menu keyboard equivalents.

Note that the call to HiliteMenu at the second last line is required to unhighlight the
Apple menu title when the application is brought to the foreground again following a
period of dalliance with an item in the Apple menu (other than the About... item).

doMouseDown

doMouseDown continues the processing of mouseDown events, switching according to the part
code.

The inContent case results in a call to SelectWindow if the window in which the mouse-down
occurred is not the front window.  SelectWindow:

*   Unhighlights the currently active window, brings the specified window to the front and
    highlights it.

*   Generates activate events for the two windows.

*   Moves the previously active window to a position immediately behind the specified
    window.

The inDrag case results in a call to DragWindow, which retains control until the user
releases the mouse button.  The third parameter in the DragWindow call establishes the
limits, in global screen coordinates, within which the user is allowed to drag the window.
screenBits is a QuickDraw global variable of type BitMap.  The bounds field of screenBits
is a Rect containing the coordinates of a rectangle which encloses the main screen.

The inGoAway case results in a call to TrackGoAway, which retains control until the user
releases the mouse button.  If the pointer was still within the go away box when the
button was released, the application-defined function doCloseWindow is called.

The inGrow case first sets up the Rect used in the third parameter of the GrowWindow call
which, in turn, will limit the maximum size to which the window can be resized.  The top,
left, bottom and right fields must contain, respectively, the minimum vertical, the
minimum horizontal, the maximum vertical, and the maximum horizontal measurements.  At the
first line, this Rect is set to the boundaries of the screen, which is a reasonable way to
get reasonable values into the bottom and right fields.  The top and left fields, however,
need to be manually set to some reasonable values (the two lines before the GrowWindow
call).

GrowWindow retains control until the user releases the mouse button, at which time the
Rect variable newSize will contain the new window size coordinates.  (Note that GrowWindow
does not redraw the window in this size.)  The SizeWindow call then redraws the window
frame and title and, where window height and/or width has been increased, adds the
newly-exposed areas to the update region.

The call SizeWindow is bracketed by two calls to the application-defined function
doInvalidateScrollBarArea.  In this program, scroll bars are not used but it has been
decided to, firstly, limit update drawing to the window's content area less the areas
normally occupied by scroll bars and, secondly, to use DrawGrowIcon to draw the draw
scroll bar delimiting lines.  (This is the usual practice for windows with a size box but
no scroll bars.)

The first call to doInvalidateScrollBarArea is necessary to cater for the case where the
window is resized larger.  If this call is not made, the scroll bar areas prior to the
resize will not be redrawn by the window updating function unless these areas are
programmatically added to the new update region created by the Window Manager as a result
of the resizing action.

The second call to doInvalidateScrollBarArea is necessary to cater for the case where the
window is resized smaller.  This call works in conjunction with the EraseRgn call in the
application-defined function doUpdateWindow.  The call to doInvalidateScrollBarArea
results in an update event being generated, and the call to EraseRgn in the doUpdateWindow
function causes the update region (that is, in this case, the scroll bar areas) to be
erased.  (Remember that, between the calls to BeginUpdate and EndUpdate, the visible
region equates to the update region and that QuickDraw limits its drawing to the update
region.)

The inZoomIn and inZoomOut cases result in a call to TrackBox, which takes control until
the user releases the mouse button.  If the mouse button is released while the pointer is
still within the zoom box, the current colour graphics port is set to that associated with
the active window. the content region is erased, and ZoomWindow is called to redraw the
window frame and title in the new zoomed state, which will be either the user state or the
standard state.  (ZoomWindow knows which way to go because the Window Manager keeps track
of the current state, which is contained in the partCode variable returned by FindWindow
and passed to ZoomWindow as its second parameter.)

doUpdate

doUpdate attends to basic window updating.  The call to BeginUpdate clips the visible
region to the intersection of the visible region and the update region.  The visible
region is now a sort of proxy for the update region.  The colour graphics port is then set
before the application-defined function doUpdateWindow is called to redraw the content
region.  The EndUpdate call restores the window's true visible region.

doUpdateWindow

doUpdateWindow is concerned with redrawing the window's contents less the scroll bar
areas.  Following the extraction of the window pointer from the message field of the event
structure, EraseRgn is called for reasons explained at doMouseDown, above.

A Rect is then assigned the coordinates of that window's colour graphics port portRect
field, following which the right and bottom fields are adjusted to exclude the scroll bar
areas.  The next four lines fill this rectangle with a plain colour pattern provided by a
'ppat' resource, simply as a means of proving the correctness of the window updating
process.

Note the call to GetWRefCon, which retrieves the value in the window structure's refCon
field.  As will be seen, whenever a new window is opened, a value between 1 and
kMaxWindows is assigned to this field.  In this function, this is just a convenient number
to be added to the base resource ID (128) in the single parameter of the GetPixPat call,
ensuring that FillCRect has a different pixel pattern to draw in each window.

The call to DrawGrowIcon draws the scroll bar delimiting lines.  Note that this call, the
preceding EraseRgn call, and the calls to doInvalidateScrollbarArea are made for
"cosmetic" purposes only and would not be required if the window contained scroll bars.

The remaining lines draw two rectangles and some text in the windows to visually represent
to the user the otherwise invisible "hot rectangles" defined in the 'hrct' resource and
associated with the window by the 'hwin' resource. When Show Balloons is chosen from the
Help menu, the help balloons will be invoked when the cursor moves over these rectangles.

doActivate

doActivate attends to those aspects of window activation not handled by the Window
Manager.  

The modifiers field of the event structure is tested to determine whether the window in
question is being activated or deactivated.  The result of this test is passed as a
parameter in the call to the application-defined function doActivateWindow.

doActivateWindow

In this demonstration, the remaining actions carried out in response to an activateEvt are
limited to placing and removing checkmarks from items in the Windows menu.

The first step in the function doActivateWindow is to associate the received WindowPtr
with its item number in the Windows menu.  At the while loop, the array maintained for
that purpose is searched until a match is found.  The array element number at which the
match is found correlates directly with the menu item number; accordingly this is assigned
to the variable menuItem, which is used in the following CheckMenuItem calls.  Whether the
checkmark is added or removed depends on whether the window in question is being activated
or deactivated, a condition passed to the call to doActivateWindow as its second
parameter.

Note that, if you required scroll bar delimiting lines to be drawn in the window, you
would call DrawGrowIcon within this function.  (In Appearance-compliant windows,
DrawGrowIcon does not draw the grow icon itself.)

doOSEvent

doOSEvent handles operating system events.  In this demonstration, action is taken only in
the case of suspend and resume events (first line) and then only if at least one window is
open (second line).  

The action taken is to set the global variable gInBackground to true or false depending on
whether the event is a suspend event or a resume event.  In the case of a suspend event,
window deactivation tasks needs to be performed.  In the case of a resume event,
activation tasks need to be attended to.  Accordingly, doActivateWindow is called with the
second parameter set to true for a resume and to false for a suspend.

doMenuChoice

doMenuChoice switches according to the menu choices of the user.

doFileMenu

doFileMenu switches according to the File menu item choice of the user.

doWindowsMenu

doWindowsMenu takes the item number of the selected Windows menu item and, since this
equates to the number of the array element in which the associated window pointer is
stored, extracts the window pointer associated with the menu item.  This is used in the
call to SelectWindow, which generates the activateEvts required to activate and deactivate
the appropriate windows.

doNewWindow

doNewWindow opens a new window and attends to associated tasks.

In the first block, if the current number of open windows equals the maximum allowable
specified by kMaxWindows, a Caution Alert is called up via the application-defined
doErrorAlert function (with the string represented by eMaxWindows displayed) and an
immediate return is executed when the user clicks the Alert's OK button.

At the next block, the new window is created.  The second parameter of the GetNewCWindow
call is a pointer to the pre-allocated block of memory allocated earlier in the program,
and the third parameter specifies that the window is to be opened in front of all other
windows.  If the call is not successful for any reason, a Stop Alert is called up via the
function doErrorAlert (with the string represented by eFailWindow displayed) and the
program terminates when the user clicks the Alert's OK button.

If the window was successfully opened, gPreAllocatedBlockPtr is set to NULL so that a new
pre-allocated block will be created at the bottom of the event loop in preparation for the
next window to be opened.

The next four lines insert the number of the window into the title bar (for example,
"Untitled 1" for the first window opened).  GetIndString retrieves the string "Untitled "
from the 'STR#' resource.  Within the first parameter of the NumToString call, the global
variable which keeps track of the numbers for the title bar is incremented before
NumToString converts that number to a Pascal string.  The next line concatenates this
string to the "Untitled " string.  The SetWTitle call then changes the window's title and
redraws the title bar.

The window's standard state is set by the call to the application-defined function
doSetStandardState.  (If the standard state is not set programmatically like this, the
system will automatically set it 3 pixels inside the screen's gray region boundary.)

The ShowWindow call makes the window visible.

The next block adds the metacharacter \ and the window number to the string used to set
the window title (thus setting up the Command key equivalent) before InsertMenuItem is
called to create a new menu item to the Windows menu.  Note that the Command-key
equivalent is only added for the first nine opened windows.)

The SetWRefCon call assigns a value to the window structure's reference constant (refCon)
field.  As previously stated, in this demonstration this is used to select a pixel pattern
to draw in each window's content region.

At the next two lines, the variable which keeps track of the current number of opened
windows is incremented and the appropriate element of the window pointer array is assigned
the window pointer of the newly opened window.

The last block enables the Windows menu and the Close item in the File menu when the first
window is opened.

doCloseWindow

The function doCloseWindow closes an open window and attends to associated tasks.

At the first two lines, a pointer to the frontmost window is retrieved and that window is
closed by a call to CloseWindow.  CloseWindow, rather than DisposeWindow, must be used
where storage for the window structure was allocated manually, that is, where the second
parameter in the GetNewCWindow call was not NULL.  Because CloseWindow is used, the
following call to DisposePtr is necessary to dispose of the non-relocatable block occupied
by the window structure.  With the window closed, the global variable which keeps track of
the number of windows currently open is decremented.

The next block deletes the associated menu item from the windows menu.  At the first four
lines, the array element in which the WindowPtr in question is located is searched out,
the element number (which correlates directly with the menu item number) is noted and the
element is set to NULL.  The call to DeleteMenuItem then deletes the menu item.

The for loop "compacts" the array, that is, it moves the contents of all elements above
the NULLed element down by one, maintaining the correlation with the Windows menu.

The last block disables the Windows menu and the Close item in the File menu if no windows
remain open as a result of this closure.

doInvalidateScrollBarArea

doInvalidateScrollBarArea invalidates that part of the window's content are which would be
occupied by scroll bars.  The function simply retrieves the coordinates of the content
area into a Rect and reduces this Rect to the relevant scroll bar area before invalidating
that area, that is, adding it to the window's update region.

doSetStandardState

The function doSetStandardState sets the window's standard state. First the coordinates of
the screen boundary are placed in a Rect.  Then, in the next two lines, the handle in the
window structure's dataHandle field is dereferenced to a pointer and cast to a pointer to
a WStateData structure.  At the last two lines, this pointer is then used in the call to
SetRect, which sets the required top, left, bottom, and right values in the stdState field
of the window's WStateData structure.

doConcatPStrings

The function doConcatPStrings concatenates two Pascal strings.

doErrorAlert

doErrorAlert displays either a Caution Alert or a Stop Alert with a specified string (two
strings in the case of the eMaxWindows error) extracted from the 'STR#' resource
identified by rStringList.

The creation of Alerts using the StandardAlert function is addressed at Chapter 8 -
Dialogs and Alerts.

Handling Horizontal and Vertical Zoom Boxes

The following details those changes that would be required to handle a vertical zoom box
in the windows of the Windows demonstration program.  It assumes that, when the vertical
zoom box is clicked, the window position is to remain unchanged and the window size is to
change only in the vertical direction.

Firstly, using Resorcerer, change the window definition ID in the 'WIND' resource editing
window to kWindowVertZoomDocumentProc (1026).  (This will simply change the appearance of
the zoom box.)  Then make the following changes to the source code.

Delete the function prototype for doSetStandardState and insert this new function
prototype:

   void  doSetUserAndStandardState  (WindowPtr);

In the function doNewWindow, change the line doSetStandardState(windowPtr); to:

   doSetUserAndStandardState(WindowPtr);

In the function doMouseDown, change the inDrag case as follows:

   case inDrag:
     DragWindow(windowPtr,eventStrucPtr->where,&qd.screenBits.bounds);
     doSetUserAndStandardState(windowPtr); // New call to set user and standard state to
                                      // reflect the new window location after the drag.
     break;

Replace the function doSetStandardState with this new function

   // ××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSetUserAndStandardState

    void  doSetUserAndStandardState(WindowPtr windowPtr)
    {
      WindowPeek windowRecPtr;
      WStateData *winStateDataPtr;
      Rect       tempRect;
      Point      topLeft;

     // Convert the top left corner of the window's content region to global coordinates.
     // (This gets the current location of the window.)

     SetPort(windowPtr);
     topLeft.v = 0;
     topLeft.h = 0;
     LocalToGlobal(&topLeft);

     // Get a pointer to the window's WStateData structure

     windowRecPtr = (WindowPeek) windowPtr;
     winStateDataPtr = (WStateData *) *(windowRecPtr->dataHandle);

     // Set the standard state to equate with the current position of the window and the 
     // required size of the window in the zoomed-out state (100 pixels higher than in the
     // zoomed-in state)..

     SetRect(&(winStateDataPtr->stdState),topLeft.h,topLeft.v,topLeft.h + 460,
             topLeft.v + 300 + 100);

     // Set the user state to equate with the current position of the window and the
     // required size of the window in the zoomed-in state.

     SetRect(&(winStateDataPtr->userState),topLeft.h,topLeft.v,topLeft.h + 460,
             topLeft.v + 300);
   }
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasnā€™t sued them for copyright infringement, but... | Read more »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, donā€™t remind me I could currently buy him this month Iā€™m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
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 »

Price Scanner via MacPrices.net

13-inch M2 MacBook Airs in stock today at App...
Apple has 13ā€³ M2 MacBook Airs available for only $849 today in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Appleā€™s one-year warranty is included,... Read more
New today at Apple: Series 9 Watches availabl...
Apple is now offering Certified Refurbished Apple Watch Series 9 models on their online store for up to $80 off MSRP, starting at $339. Each Watch includes Appleā€™s standard one-year warranty, a new... Read more
The latest Apple iPhone deals from wireless c...
Weā€™ve updated our iPhone Price Tracker with the latest carrier deals on Appleā€™s iPhone 15 family of smartphones as well as previous models including the iPhone 14, 13, 12, 11, and SE. Use our price... Read more
Boost Mobile will sell you an iPhone 11 for $...
Boost Mobile, an MVNO using AT&T and T-Mobileā€™s networks, is offering an iPhone 11 for $149.99 when purchased with their $40 Unlimited service plan (12GB of premium data). No trade-in is required... Read more
Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobileā€™s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13ā€³ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Appleā€™s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16ā€³ and 14ā€³ MacBook Pros along with 13ā€³ and 15ā€³ MacBook... Read more
Every model of Appleā€™s 13-inch M3 MacBook Air...
Best Buy has Apple 13ā€³ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13ā€³ M3 MacBook Airs among... Read more
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9ā€³ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... Read more

Jobs Board

Solutions Engineer - *Apple* - SHI (United...
**Job Summary** An Apple Solution Engineer's primary role is tosupport SHI customers in their efforts to select, deploy, and manage Apple operating systems and Read more
DMR Technician - *Apple* /iOS Systems - Haml...
ā€¦relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.