TweetFollow Us on Twitter

MACINTOSH C CARBON

Demonstration Program MonoTextEdit

Goto Contents

// *******************************************************************************************
// MonoTextEdit.c                                                           CARBON EVENT MODEL
// *******************************************************************************************
//
// This program demonstrates:
//
// o  A "bare-bones" monostyled text editor.
//
// o  A Help dialog which features the integrated scrolling of multistyled text and pictures.
//
// In the monostyled text editor demonstration, a panel is displayed at the bottom of all
// opened windows.  This panel displays the edit record length, number of lines, line height,
// destination rectangle (top), scroll bar/scroller value, and scroll bar/scroller maximum 
// value. 
//
// The bulk of the source code for the Help dialog is contained in the file HelpDialog.c.
// The dialog itself displays information intended to assist the user in adapting the Help
// dialog source code and resources to the requirements of his/her own application.
//
// The program utilises the following resources:
//
// o  A 'plst' resource.
//
// o  An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit, and Help dialog pop-up
//    menus (preload, non-purgeable).  
//
// o  A 'CNTL' resources (purgeable) for the vertical scroll bar in the text editor window.
//
// o  'TEXT' and associated 'styl' resources (all purgeable) for the Help dialog.
//
// o  'PICT' resources (purgeable) for the Help dialog.
//
// o  A 'STR#' resource  (purgeable) containing error text strings.  
//
// o  A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground, 
//    doesActivateOnFGSwitch, and isHighLevelEventAware flags set.
//
// *******************************************************************************************

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

#include <Carbon.h>

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

#define rMenubar          128
#define mAppleApplication 128
#define  iAbout           1
#define  iHelp            2
#define mFile             129
#define  iNew             1
#define  iOpen            2
#define  iClose           4
#define  iSaveAs          6
#define  iQuit            12
#define mEdit             130
#define  iUndo            1
#define  iCut             3
#define  iCopy            4
#define  iPaste           5
#define  iClear           6
#define  iSelectAll       7
#define rVScrollbar       128
#define rErrorStrings     128
#define  eMenuBar         1
#define  eWindow          2
#define  eDocStructure    3
#define  eEditRecord      4
#define  eExceedChara     5
#define  eNoSpaceCut      6
#define  eNoSpacePaste    7
#define kMaxTELength      32767
#define kTab              0x09
#define kBackSpace        0x08
#define kForwardDelete    0x7F
#define kReturn           0x0D
#define kEscape           0x1B
#define topLeft(r)        (((Point *) &(r))[0])
#define botRight(r)       (((Point *) &(r))[1])

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

typedef struct
{
  TEHandle   textEditStrucHdl;
  ControlRef vScrollbarRef;
} docStructure, **docStructureHandle;

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

Boolean          gRunningOnX = false;
MenuID           gHelpMenu;
ControlActionUPP gScrollActionFunctionUPP;
TEClickLoopUPP   gCustomClickLoopUPP;
SInt16           gNumberOfWindows = 0;
SInt16           gOldControlValue;

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

void            main                 (void);
void            doPreliminaries      (void);
OSStatus        appEventHandler      (EventHandlerCallRef,EventRef,void *);
OSStatus        windowEventHandler   (EventHandlerCallRef,EventRef,void *);
void            doIdle               (void);
void            doKeyEvent           (SInt8);
void            scrollActionFunction (ControlRef,SInt16);
void            doInContent          (Point,Boolean);
void            doDrawContent        (WindowPtr);
void            doActivateDeactivate (WindowRef,Boolean);
WindowRef       doNewDocWindow       (void);
EventHandlerUPP doGetHandlerUPP      (void);
Boolean         customClickLoop      (void);
void            doSetScrollBarValue  (ControlRef,SInt16 *);
void            doAdjustMenus        (void);
void            doMenuChoice         (MenuID,MenuItemIndex);
void            doFileMenu           (MenuItemIndex);
void            doEditMenu           (MenuItemIndex);
SInt16          doGetSelectLength    (TEHandle);
void            doAdjustScrollbar    (WindowRef);
void            doAdjustCursor       (WindowRef);
void            doCloseWindow        (WindowRef);
void            doSaveAsFile         (TEHandle);
void            doOpenCommand        (void);
void            doOpenFile           (FSSpec);
void            doDrawDataPanel      (WindowRef);
void            doErrorAlert         (SInt16);
void            navEventFunction     (NavEventCallbackMessage,NavCBRecPtr,
                                      NavCallBackUserData);

extern void     doHelp               (void);

// ************************************************************************************** main

void  main(void)
{
  MenuBarHandle menubarHdl;
  SInt32        response;
  MenuRef       menuRef;
  EventTypeSpec applicationEvents[] = { { kEventClassApplication, kEventAppActivated    },
                                        { kEventClassCommand,     kEventProcessCommand  },
                                        { kEventClassMenu,        kEventMenuEnableItems },
                                        { kEventClassMouse,       kEventMouseMoved      } };

  // ........................................................................ do preliminaries

  doPreliminaries();

  // ............................................................... set up menu bar and menus

  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    doErrorAlert(eMenuBar);
  SetMenuBar(menubarHdl);
  DrawMenuBar();

  Gestalt(gestaltMenuMgrAttr,&response);
  if(response & gestaltMenuMgrAquaLayoutMask)
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
    {
      DeleteMenuItem(menuRef,iQuit);
      DeleteMenuItem(menuRef,iQuit - 1);
    }

    menuRef = GetMenuRef(mAppleApplication);
    DeleteMenuItem(menuRef,iHelp);

    HMGetHelpMenu(&menuRef,NULL);
    InsertMenuItem(menuRef,"\pMonoTextEdit Help",0);
    gHelpMenu = GetMenuID(menuRef);

    gRunningOnX = true;
  }
  else
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
      SetMenuItemCommandID(menuRef,iQuit,kHICommandQuit);
  }

  // ..................................................... create universal procedure pointers

  gScrollActionFunctionUPP = NewControlActionUPP((ControlActionProcPtr) scrollActionFunction);
  gCustomClickLoopUPP      = NewTEClickLoopUPP((TEClickLoopProcPtr) customClickLoop);
 
  // ....................................................... install application event handler
  
  InstallApplicationEventHandler(NewEventHandlerUPP((EventHandlerProcPtr) appEventHandler),
                                 GetEventTypeCount(applicationEvents),applicationEvents,
                                 0,NULL);

  // ......................................................................... install a timer

  InstallEventLoopTimer(GetCurrentEventLoop(),0,TicksToEventTime(GetCaretTime()),
                        NewEventLoopTimerUPP((EventLoopTimerProcPtr) doIdle),NULL,
                        NULL);

  // ................................................................. open an untitled window

  doNewDocWindow();

  // .............................................................. run application event loop

  RunApplicationEventLoop();
}

// *************************************************************************** doPreliminaries

void  doPreliminaries(void)
{
  MoreMasterPointers(192);
  InitCursor();
}

// *************************************************************************** appEventHandler

OSStatus  appEventHandler(EventHandlerCallRef eventHandlerCallRef,EventRef eventRef,
                          void * userData)
{
  OSStatus      result = eventNotHandledErr;
  UInt32        eventClass;
  UInt32        eventKind;
  HICommand     hiCommand;
  MenuID        menuID;
  MenuItemIndex menuItem;

  eventClass = GetEventClass(eventRef);
  eventKind  = GetEventKind(eventRef);

  switch(eventClass)
  {
    case kEventClassApplication:
      if(eventKind == kEventAppActivated)
        SetThemeCursor(kThemeArrowCursor);
      break;

    case kEventClassCommand:
      if(eventKind == kEventProcessCommand)
      {
        GetEventParameter(eventRef,kEventParamDirectObject,typeHICommand,NULL,
                          sizeof(HICommand),NULL,&hiCommand);
        menuID = GetMenuID(hiCommand.menu.menuRef);
        menuItem = hiCommand.menu.menuItemIndex;
        if((hiCommand.commandID != kHICommandQuit) && 
           ((menuID >= mAppleApplication && menuID <= mEdit)) || menuID == gHelpMenu)
        {
          doMenuChoice(menuID,menuItem);
          result = noErr;
        }
      }
      break;

    case kEventClassMenu:
      if(eventKind == kEventMenuEnableItems)
      {
        GetWindowClass(FrontWindow(),&windowClass);
        if(windowClass == kDocumentWindowClass)
          doAdjustMenus();
        result = noErr;
      }
      break;

    case kEventClassMouse:
      if(eventKind == kEventMouseMoved)
      {
        GetWindowClass(FrontWindow(),&windowClass);
        if(windowClass == kDocumentWindowClass)
          doAdjustCursor(FrontWindow());
       result = noErr;
      }
      break;

  return result;
}

// ************************************************************************ windowEventHandler

OSStatus  windowEventHandler(EventHandlerCallRef eventHandlerCallRef,EventRef eventRef,
                             void* userData)
{
  OSStatus        result = eventNotHandledErr;
  UInt32          eventClass;
  UInt32          eventKind;
  WindowRef       windowRef;
  UInt32          modifiers; 
  Point           mouseLocation;
  Boolean         shiftKeyDown = false;
  ControlRef      controlRef;
  ControlPartCode controlPartCode;
  SInt8           charCode;

  eventClass = GetEventClass(eventRef);
  eventKind  = GetEventKind(eventRef);

  switch(eventClass)
  {
    case kEventClassWindow:                                              // event class window
      GetEventParameter(eventRef,kEventParamDirectObject,typeWindowRef,NULL,sizeof(windowRef),
                        NULL,&windowRef);
      switch(eventKind)
      {
        case kEventWindowDrawContent:
          doDrawContent(windowRef);
          result = noErr;
          break;

        case kEventWindowActivated:
          doActivateDeactivate(windowRef,true);
          result = noErr;
          break;

        case kEventWindowDeactivated:
          doActivateDeactivate(windowRef,false);
          result = noErr;
          break;

        case kEventWindowClickContentRgn:
          GetEventParameter(eventRef,kEventParamMouseLocation,typeQDPoint,NULL,
                            sizeof(mouseLocation),NULL,&mouseLocation);
          SetPortWindowPort(FrontWindow());
          GlobalToLocal(&mouseLocation);
          GetEventParameter(eventRef,kEventParamKeyModifiers,typeUInt32,NULL,
                            sizeof(modifiers),NULL,&modifiers);
          if(modifiers & shiftKey)
            shiftKeyDown = true;
          doInContent(mouseLocation,shiftKeyDown);
          result = noErr;
          break;

        case kEventWindowClose:
          doCloseWindow(windowRef);
          result = noErr;
          break;
      }
      break;

    case kEventClassMouse:                                                // event class mouse
      switch(eventKind)
      {
        case kEventMouseDown:
          GetEventParameter(eventRef,kEventParamMouseLocation,typeQDPoint,NULL,
                            sizeof(mouseLocation),NULL,&mouseLocation);
          SetPortWindowPort(FrontWindow());
          GlobalToLocal(&mouseLocation);
          controlRef = FindControlUnderMouse(mouseLocation,FrontWindow(),&controlPartCode);
          if(controlRef)
          {
            gOldControlValue = GetControlValue(controlRef);
            TrackControl(controlRef,mouseLocation,gScrollActionFunctionUPP);
            result = noErr;
          }
        break;
      }
      break;

    case kEventClassKeyboard:                                          // event class keyboard
      switch(eventKind)
      {
        case kEventRawKeyDown:
        case kEventRawKeyRepeat: 
          GetEventParameter(eventRef,kEventParamKeyMacCharCodes,typeChar,NULL,
                            sizeof(charCode),NULL,&charCode);
          GetEventParameter(eventRef,kEventParamKeyModifiers,typeUInt32,NULL,
                            sizeof(modifiers),NULL,&modifiers);
          if((modifiers & cmdKey) == 0)
            doKeyEvent(charCode);
          result = noErr;
          break;
      }
      break;
  }

  return result;
}

// ************************************************************************************ doIdle

void  doIdle(void)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;

  windowRef = FrontWindow();
  if(GetWindowKind(windowRef) == kApplicationWindowKind)
  {
    docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
    if(docStrucHdl != NULL)
      TEIdle((*docStrucHdl)->textEditStrucHdl);
  }
}

// ******************************************************************************** doKeyEvent

void  doKeyEvent(SInt8 charCode)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             selectionLength;

  if(charCode <= kEscape && charCode != kBackSpace && charCode != kReturn)
    return;

  windowRef = FrontWindow();
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  if(charCode == kTab)
  {
    // Do tab key handling here if required.
  }
  else if(charCode == kForwardDelete)
  {
    selectionLength = doGetSelectLength(textEditStrucHdl);
    if(selectionLength == 0)
      (*textEditStrucHdl)->selEnd += 1;
    TEDelete(textEditStrucHdl);
    doAdjustScrollbar(windowRef);
  }
  else
  {
    selectionLength = doGetSelectLength(textEditStrucHdl);
    if(((*textEditStrucHdl)->teLength - selectionLength + 1) < kMaxTELength)
    {
      TEKey(charCode,textEditStrucHdl);
      doAdjustScrollbar(windowRef);
    }
    else
      doErrorAlert(eExceedChara);
  }

  doDrawDataPanel(windowRef);
}

// ********************************************************************** scrollActionFunction

void  scrollActionFunction(ControlRef controlRef,SInt16 partCode)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             linesToScroll;
  SInt16             controlValue, controlMax;

  windowRef = GetControlOwner(controlRef);
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));;
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  controlValue = GetControlValue(controlRef);
  controlMax = GetControlMaximum(controlRef);

  if(partCode)
  {
    if(partCode != kControlIndicatorPart)
    {
      switch(partCode)
      {
        case kControlUpButtonPart:
        case kControlDownButtonPart:
          linesToScroll = 1;
          break;
        
        case kControlPageUpPart:
        case kControlPageDownPart:
          linesToScroll = (((*textEditStrucHdl)->viewRect.bottom - 
                            (*textEditStrucHdl)->viewRect.top) /
                            (*textEditStrucHdl)->lineHeight) - 1;
          break;
      }

      if((partCode == kControlDownButtonPart) || (partCode == kControlPageDownPart))
        linesToScroll = -linesToScroll;

      linesToScroll = controlValue - linesToScroll;
      if(linesToScroll < 0)
        linesToScroll = 0;
      else if(linesToScroll > controlMax)
        linesToScroll = controlMax;

      SetControlValue(controlRef,linesToScroll);

      linesToScroll = controlValue - linesToScroll;
    }
    else
    {
      linesToScroll = gOldControlValue - controlValue;
      gOldControlValue = controlValue;
    }

    if(linesToScroll != 0)
    {
      TEScroll(0,linesToScroll * (*textEditStrucHdl)->lineHeight,textEditStrucHdl);
      doDrawDataPanel(windowRef);
    }
  }
}

// ******************************************************************************* doInContent

void  doInContent(Point mouseLocation,Boolean shiftKeyDown)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;

  windowRef = FrontWindow();
  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  if(PtInRect(mouseLocation,&(*textEditStrucHdl)->viewRect))
    TEClick(mouseLocation,shiftKeyDown,textEditStrucHdl);
}

// ***************************************************************************** doDrawContent

void  doDrawContent(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  GrafPtr            oldPort;
  RgnHandle          visibleRegionHdl = NewRgn();
  Rect               portRect;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  GetPort(&oldPort);
  SetPortWindowPort(windowRef);

  GetPortVisibleRegion(GetWindowPort(windowRef),visibleRegionHdl);
  EraseRgn(visibleRegionHdl);

  UpdateControls(windowRef,visibleRegionHdl);

  GetWindowPortBounds(windowRef,&portRect);
  TEUpdate(&portRect,textEditStrucHdl);

  doDrawDataPanel(windowRef);

  DisposeRgn(visibleRegionHdl);
  SetPort(oldPort);
}

// *********************************************************************** doActivateDocWindow

void  doActivateDeactivate(WindowRef windowRef,Boolean becomingActive)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  if(becomingActive)
  {
    SetPortWindowPort(windowRef);

    (*textEditStrucHdl)->viewRect.bottom = ((((*textEditStrucHdl)->viewRect.bottom -
                                              (*textEditStrucHdl)->viewRect.top) / 
                                              (*textEditStrucHdl)->lineHeight) * 
                                              (*textEditStrucHdl)->lineHeight) + 
                                              (*textEditStrucHdl)->viewRect.top;
    (*textEditStrucHdl)->destRect.bottom = (*textEditStrucHdl)->viewRect.bottom;

    TEActivate(textEditStrucHdl);
    ActivateControl((*docStrucHdl)->vScrollbarRef);
    doAdjustScrollbar(windowRef);
    doAdjustCursor(windowRef);
  }
  else
  {
    TEDeactivate(textEditStrucHdl);
    DeactivateControl((*docStrucHdl)->vScrollbarRef);
  }
}

// **************************************************************************** doNewDocWindow

WindowRef  doNewDocWindow(void)
{
  WindowRef          windowRef;
  OSStatus           osError;
  Rect               contentRect = { 100,100,400,595 };
  WindowAttributes   attributes  = kWindowStandardHandlerAttribute |
                                    kWindowStandardDocumentAttributes;
  docStructureHandle docStrucHdl;
  Rect               portRect, destAndViewRect;
  EventTypeSpec    windowEvents[] = { { kEventClassWindow,   kEventWindowDrawContent     },
                                      { kEventClassWindow,   kEventWindowActivated       },
                                      { kEventClassWindow,   kEventWindowDeactivated     },
                                      { kEventClassWindow,   kEventWindowClickContentRgn },
                                      { kEventClassWindow,   kEventWindowClose           },
                                      { kEventClassMouse,    kEventMouseDown             },
                                      { kEventClassKeyboard, kEventRawKeyDown            },
                                      { kEventClassKeyboard, kEventRawKeyRepeat          } };

  osError = CreateNewWindow(kDocumentWindowClass,attributes,&contentRect,&windowRef);
  if(osError != noErr)
  {
    doErrorAlert(eWindow);
    return NULL;
  }

  ChangeWindowAttributes(windowRef,0,kWindowResizableAttribute);
  RepositionWindow(windowRef,NULL,kWindowCascadeOnMainScreen);
  SetWTitle(windowRef,"\puntitled");
  SetPortWindowPort(windowRef);
  TextSize(10);

  InstallWindowEventHandler(windowRef,doGetHandlerUPP(),GetEventTypeCount(windowEvents),
                            windowEvents,0,NULL);

  if(!(docStrucHdl = (docStructureHandle) NewHandle(sizeof(docStructure))))
  {
    doErrorAlert(eDocStructure);
    return NULL;
  }
  SetWRefCon(windowRef,(SInt32) docStrucHdl);

  gNumberOfWindows ++;

  (*docStrucHdl)->vScrollbarRef = GetNewControl(rVScrollbar,windowRef);

  GetWindowPortBounds(windowRef,&portRect);
  destAndViewRect = portRect;
  destAndViewRect.right -= 15;
  destAndViewRect.bottom -= 15;
  InsetRect(&destAndViewRect,2,2);

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

  if(!((*docStrucHdl)->textEditStrucHdl = TENew(&destAndViewRect,&destAndViewRect)))
  {
    DisposeWindow(windowRef);
    gNumberOfWindows --;
    DisposeHandle((Handle) docStrucHdl);
    doErrorAlert(eEditRecord);
    return NULL;
  }

  HUnlock((Handle) docStrucHdl);

  TESetClickLoop(gCustomClickLoopUPP,(*docStrucHdl)->textEditStrucHdl);
  TEAutoView(true,(*docStrucHdl)->textEditStrucHdl);
  TEFeatureFlag(teFOutlineHilite,1,(*docStrucHdl)->textEditStrucHdl);

  ShowWindow(windowRef);

  return windowRef;
}

// *************************************************************************** doGetHandlerUPP

EventHandlerUPP  doGetHandlerUPP(void)
{
  static EventHandlerUPP windowEventHandlerUPP;

  if(windowEventHandlerUPP == NULL)
    windowEventHandlerUPP = NewEventHandlerUPP((EventHandlerProcPtr) windowEventHandler);

  return windowEventHandlerUPP;
}

// *************************************************************************** customClickLoop

Boolean  customClickLoop(void)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  GrafPtr            oldPort;
  RgnHandle          oldClip;
  Rect               tempRect, portRect;
  Point              mouseXY;
  SInt16             linesToScroll = 0;

  windowRef = FrontWindow();
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  GetPort(&oldPort);
  SetPortWindowPort(windowRef);
  oldClip = NewRgn();
  GetClip(oldClip);
  SetRect(&tempRect,-32767,-32767,32767,32767);
  ClipRect(&tempRect);

  GetMouse(&mouseXY);
  GetWindowPortBounds(windowRef,&portRect);
  
  if(mouseXY.v < portRect.top)
  {
    linesToScroll = 1;
    doSetScrollBarValue((*docStrucHdl)->vScrollbarRef,&linesToScroll);
    if(linesToScroll != 0)
      TEScroll(0,linesToScroll * ((*textEditStrucHdl)->lineHeight),textEditStrucHdl);
  }
  else if(mouseXY.v > portRect.bottom)
  {
    linesToScroll = -1;
    doSetScrollBarValue((*docStrucHdl)->vScrollbarRef,&linesToScroll);
    if(linesToScroll != 0)
      TEScroll(0,linesToScroll * ((*textEditStrucHdl)->lineHeight),textEditStrucHdl);
  }

  if(linesToScroll != 0)
    doDrawDataPanel(windowRef);

  SetClip(oldClip);
  DisposeRgn(oldClip);
  SetPort(oldPort);

  return true;
}

// *********************************************************************** doSetScrollBarValue

void  doSetScrollBarValue(ControlRef controlRef,SInt16 *linesToScroll)
{
  SInt16 controlValue, controlMax;

  controlValue = GetControlValue(controlRef);
  controlMax = GetControlMaximum(controlRef);

  *linesToScroll = controlValue - *linesToScroll;
  if(*linesToScroll < 0)
    *linesToScroll = 0;
  else if(*linesToScroll > controlMax)
    *linesToScroll = controlMax;

  SetControlValue(controlRef,*linesToScroll);
  *linesToScroll = controlValue - *linesToScroll;
}

// ***************************************************************************** doAdjustMenus

void  doAdjustMenus(void)
{
  MenuRef            fileMenuHdl, editMenuHdl;
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  ScrapRef           scrapRef;
  OSStatus           osError;
  ScrapFlavorFlags   scrapFlavorFlags;

  fileMenuHdl = GetMenuRef(mFile);
  editMenuHdl = GetMenuRef(mEdit);

  if(gNumberOfWindows > 0)
  {
    windowRef = FrontWindow();
    docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
    textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

    EnableMenuItem(fileMenuHdl,iClose);

    if((*textEditStrucHdl)->selStart < (*textEditStrucHdl)->selEnd)
    {
      EnableMenuItem(editMenuHdl,iCut);
      EnableMenuItem(editMenuHdl,iCopy);
      EnableMenuItem(editMenuHdl,iClear);
    }
    else
    {
      DisableMenuItem(editMenuHdl,iCut);
      DisableMenuItem(editMenuHdl,iCopy);
      DisableMenuItem(editMenuHdl,iClear);
    }

    GetCurrentScrap(&scrapRef);

    osError = GetScrapFlavorFlags(scrapRef,kScrapFlavorTypeText,&scrapFlavorFlags);
    if(osError == noErr)
      EnableMenuItem(editMenuHdl,iPaste);
    else
      DisableMenuItem(editMenuHdl,iPaste);
      
    if((*textEditStrucHdl)->teLength > 0)
    {
      EnableMenuItem(fileMenuHdl,iSaveAs);
      EnableMenuItem(editMenuHdl,iSelectAll);
    }
    else
    {
      DisableMenuItem(fileMenuHdl,iSaveAs);
      DisableMenuItem(editMenuHdl,iSelectAll);
    }
  }
  else
  {
    DisableMenuItem(fileMenuHdl,iClose);
    DisableMenuItem(fileMenuHdl,iSaveAs);
    DisableMenuItem(editMenuHdl,iClear);
    DisableMenuItem(editMenuHdl,iSelectAll);
  }

  DrawMenuBar();
}

// ****************************************************************************** doMenuChoice

void  doMenuChoice(MenuID menuID, MenuItemIndex menuItem)
{
  if(menuID == 0)
    return;

  if(gRunningOnX)
    if(menuID == gHelpMenu)
      if(menuItem == 1)
        doHelp();

  switch(menuID)
  {
    case mAppleApplication:
      if(menuItem == iAbout)
        SysBeep(10);
      else if(menuItem == iHelp)
        doHelp();
      break;

    case mFile:
      doFileMenu(menuItem);
      break;

    case mEdit:
      doEditMenu(menuItem);
      break;
  }
}

// ******************************************************************************** doFileMenu

void  doFileMenu(MenuItemIndex menuItem)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;

  switch(menuItem)
  {
    case iNew:
      if(windowRef = doNewDocWindow())
        ShowWindow(windowRef);
      break;

    case iOpen:
      doOpenCommand();
      doAdjustScrollbar(FrontWindow());
      break;

    case iClose:
      doCloseWindow(FrontWindow());
      break;

    case iSaveAs:
      docStrucHdl = (docStructureHandle) (GetWRefCon(FrontWindow()));
      textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
      doSaveAsFile(textEditStrucHdl);
      break;
  }
}

// ******************************************************************************** doEditMenu

void  doEditMenu(MenuItemIndex menuItem)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt32             totalSize, contigSize, newSize;
  SInt16             selectionLength;
  ScrapRef           scrapRef;
  Size               sizeOfTextData;

  windowRef = FrontWindow();

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  switch(menuItem)
  {
    case iUndo:
      break;

    case iCut:
      if(ClearCurrentScrap() == noErr)
      {
        PurgeSpace(&totalSize,&contigSize);
        selectionLength = doGetSelectLength(textEditStrucHdl);
        if(selectionLength > contigSize)
          doErrorAlert(eNoSpaceCut);
        else
        {
          TECut(textEditStrucHdl);
          doAdjustScrollbar(windowRef);
          TEToScrap();
          if(TEToScrap() != noErr)
            ClearCurrentScrap();
        }
      }
      break;

    case iCopy:
      if(ClearCurrentScrap() == noErr)
        TECopy(textEditStrucHdl);
      TEToScrap();
      if(TEToScrap() != noErr)
        ClearCurrentScrap();
      break;

    case iPaste:
      GetCurrentScrap(&scrapRef);;
      GetScrapFlavorSize(scrapRef,kScrapFlavorTypeText,&sizeOfTextData);
      newSize = (*textEditStrucHdl)->teLength + sizeOfTextData;
      if(newSize > kMaxTELength)
        doErrorAlert(eNoSpacePaste);
      else
      {
        if(TEFromScrap() == noErr)
        {
          TEPaste(textEditStrucHdl);
          doAdjustScrollbar(windowRef);
        }
      }
      break;

    case iClear:
      TEDelete(textEditStrucHdl);
      doAdjustScrollbar(windowRef);
      break;

    case iSelectAll:
      TESetSelect(0,(*textEditStrucHdl)->teLength,textEditStrucHdl);
      break;
  }

  doDrawDataPanel(windowRef);
}

// ************************************************************************* doGetSelectLength

SInt16  doGetSelectLength(TEHandle textEditStrucHdl)
{
  SInt16 selectionLength;

  selectionLength = (*textEditStrucHdl)->selEnd - (*textEditStrucHdl)->selStart;
  return selectionLength;
}

// ************************************************************************* doAdjustScrollbar

void  doAdjustScrollbar(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             numberOfLines, controlMax, controlValue;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));;
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  numberOfLines = (*textEditStrucHdl)->nLines;
  if(*(*(*textEditStrucHdl)->hText + (*textEditStrucHdl)->teLength - 1) == kReturn)
    numberOfLines += 1;

  controlMax = numberOfLines - (((*textEditStrucHdl)->viewRect.bottom - 
               (*textEditStrucHdl)->viewRect.top) /
               (*textEditStrucHdl)->lineHeight);
  if(controlMax < 0)
    controlMax = 0;
  SetControlMaximum((*docStrucHdl)->vScrollbarRef,controlMax);

  controlValue = ((*textEditStrucHdl)->viewRect.top - (*textEditStrucHdl)->destRect.top) / 
                  (*textEditStrucHdl)->lineHeight;
  if(controlValue < 0)
    controlValue = 0;
  else if(controlValue > controlMax)
    controlValue = controlMax;

  SetControlValue((*docStrucHdl)->vScrollbarRef,controlValue);

  SetControlViewSize((*docStrucHdl)->vScrollbarRef,(*textEditStrucHdl)->viewRect.bottom - 
                     (*textEditStrucHdl)->viewRect.top);

  TEScroll(0,((*textEditStrucHdl)->viewRect.top - (*textEditStrucHdl)->destRect.top) - 
               (GetControlValue((*docStrucHdl)->vScrollbarRef) *
              (*textEditStrucHdl)->lineHeight),textEditStrucHdl);
}

// **************************************************************************** doAdjustCursor

void  doAdjustCursor(WindowRef windowRef)
{
  GrafPtr   oldPort;
  RgnHandle arrowRegion, iBeamRegion;
  Rect      portRect, cursorRect;
  Point     mouseXY;

  GetPort(&oldPort);
  SetPortWindowPort(windowRef);

  arrowRegion = NewRgn();
  iBeamRegion = NewRgn();  
  SetRectRgn(arrowRegion,-32768,-32768,32766,32766);

  GetWindowPortBounds(windowRef,&portRect);
  cursorRect = portRect;
  cursorRect.bottom -= 15;
  cursorRect.right  -= 15;
  LocalToGlobal(&topLeft(cursorRect));
  LocalToGlobal(&botRight(cursorRect));  

  RectRgn(iBeamRegion,&cursorRect);
  DiffRgn(arrowRegion,iBeamRegion,arrowRegion);
  
  GetGlobalMouse(&mouseXY);

  if(PtInRgn(mouseXY,iBeamRegion))
    SetThemeCursor(kThemeIBeamCursor);
  else
    SetThemeCursor(kThemeArrowCursor);

  DisposeRgn(arrowRegion);
  DisposeRgn(iBeamRegion);

  SetPort(oldPort);
}

// ***************************************************************************** doCloseWindow

void  doCloseWindow(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));;

  DisposeControl((*docStrucHdl)->vScrollbarRef);
  TEDispose((*docStrucHdl)->textEditStrucHdl);
  DisposeHandle((Handle) docStrucHdl);
  DisposeWindow(windowRef);

  gNumberOfWindows --;
}

// ****************************************************************************** doSaveAsFile

void  doSaveAsFile(TEHandle textEditStrucHdl)
{
  OSErr            osError = noErr;
  NavDialogOptions dialogOptions;
  NavEventUPP      navEventFunctionUPP;
  WindowRef        windowRef;
  OSType           fileType;
  NavReplyRecord   navReplyStruc;
  AEKeyword        theKeyword;
  DescType         actualType;
  FSSpec           fileSpec;  
  SInt16           fileRefNum;
  Size             actualSize;
  SInt32           dataLength;
  Handle           editTextHdl;
    
  osError = NavGetDefaultDialogOptions(&dialogOptions);

  if(osError == noErr)
  {
    windowRef = FrontWindow();

    fileType = 'TEXT';

    navEventFunctionUPP = NewNavEventUPP((NavEventProcPtr) navEventFunction);
    osError = NavPutFile(NULL,&navReplyStruc,&dialogOptions,navEventFunctionUPP,fileType,
                         'kjBb',NULL);
    DisposeNavEventUPP(navEventFunctionUPP);

    if(navReplyStruc.validRecord && osError == noErr)
    {
      if((osError = AEGetNthPtr(&(navReplyStruc.selection),1,typeFSS,&theKeyword,
                                &actualType,&fileSpec,sizeof(fileSpec),&actualSize)) == noErr)

      {
        if(!navReplyStruc.replacing)
        {
          osError = FSpCreate(&fileSpec,'kjBb',fileType,navReplyStruc.keyScript);
          if(osError != noErr)
          {
            NavDisposeReply(&navReplyStruc);
          }
        }
        
        if(osError == noErr)
          osError = FSpOpenDF(&fileSpec,fsRdWrPerm,&fileRefNum);

        if(osError == noErr)
        {
          SetWTitle(windowRef,fileSpec.name);
          dataLength = (*textEditStrucHdl)->teLength;
          editTextHdl = (*textEditStrucHdl)->hText;
          FSWrite(fileRefNum,&dataLength,*editTextHdl);
        }

        NavCompleteSave(&navReplyStruc,kNavTranslateInPlace);
      }

      NavDisposeReply(&navReplyStruc);
    }
  }
}

// ***************************************************************************** doOpenCommand

void  doOpenCommand(void)
{  
  OSErr            osError  = noErr;
  NavDialogOptions dialogOptions;
  NavEventUPP      navEventFunctionUPP;
  NavReplyRecord   navReplyStruc;
  SInt32           index, count;
  AEKeyword        theKeyword;
  DescType         actualType;
  FSSpec           fileSpec;  
  Size             actualSize;
  FInfo            fileInfo;

  osError = NavGetDefaultDialogOptions(&dialogOptions);

  if(osError == noErr)
  {
    navEventFunctionUPP = NewNavEventUPP((NavEventProcPtr) navEventFunction);
    osError = NavGetFile(NULL,&navReplyStruc,&dialogOptions,navEventFunctionUPP,NULL,NULL,
                         NULL,NULL);
    DisposeNavEventUPP(navEventFunctionUPP);

    if(osError == noErr && navReplyStruc.validRecord)
    {
      osError = AECountItems(&(navReplyStruc.selection),&count);
      if(osError == noErr)
      {
        for(index=1;index<=count;index++)
        {
          osError = AEGetNthPtr(&(navReplyStruc.selection),index,typeFSS,&theKeyword,
                                &actualType,&fileSpec,sizeof(fileSpec),&actualSize);
          {
            if((osError = FSpGetFInfo(&fileSpec,&fileInfo)) == noErr)
              doOpenFile(fileSpec);
          }
        }
      }
    
      NavDisposeReply(&navReplyStruc);  
    }
  }
}

// ******************************************************************************** doOpenFile

void  doOpenFile(FSSpec fileSpec)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             fileRefNum;
  SInt32             textLength;
  Handle             textBuffer;

  if((windowRef = doNewDocWindow()) == NULL)
    return;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));;
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  SetWTitle(windowRef,fileSpec.name);

  FSpOpenDF(&fileSpec,fsCurPerm,&fileRefNum);

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

  if(textLength > 32767)
    textLength = 32767;

  textBuffer = NewHandle((Size) textLength);

  FSRead(fileRefNum,&textLength,*textBuffer);

  MoveHHi(textBuffer);
  HLock(textBuffer);

  TESetText(*textBuffer,textLength,textEditStrucHdl);

  HUnlock(textBuffer);
  DisposeHandle(textBuffer);

  FSClose(fileRefNum);

  (*textEditStrucHdl)->selStart = 0;
  (*textEditStrucHdl)->selEnd = 0;

  doDrawContent(windowRef);
}

// *************************************************************************** doDrawDataPanel

void  doDrawDataPanel(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  RGBColor           whiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };
  RGBColor           blackColour = { 0x0000, 0x0000, 0x0000 };
  RGBColor           blueColour = { 0x1818, 0x4B4B, 0x8181 };
  ControlRef         controlRef;
  Rect               panelRect;
  Str255             textString;

  SetPortWindowPort(windowRef);

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));;
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
  controlRef = (*docStrucHdl)->vScrollbarRef;

  MoveTo(0,282);
  LineTo(495,282);

  RGBForeColor(&whiteColour);
  RGBBackColor(&blueColour);
  SetRect(&panelRect,0,283,495,300);
  EraseRect(&panelRect);

  MoveTo(3,295);
  DrawString("\pteLength               nLines          lineHeight");

  MoveTo(225,295);
  DrawString("\pdestRect.top              controlValue           contrlMax");

  SetRect(&panelRect,47,284,88,299);
  EraseRect(&panelRect);  
  SetRect(&panelRect,124,284,149,299);
  EraseRect(&panelRect);  
  SetRect(&panelRect,204,284,222,299);
  EraseRect(&panelRect);  
  SetRect(&panelRect,286,284,323,299);
  EraseRect(&panelRect);  
  SetRect(&panelRect,389,284,416,299);
  EraseRect(&panelRect);  
  SetRect(&panelRect,472,284,495,299);
  EraseRect(&panelRect);  

  NumToString((SInt32) (*textEditStrucHdl)->teLength,textString);
  MoveTo(47,295);
  DrawString(textString);

  NumToString((SInt32) (*textEditStrucHdl)->nLines,textString);
  MoveTo(124,295);
  DrawString(textString);

  NumToString((SInt32) (*textEditStrucHdl)->lineHeight,textString);
  MoveTo(204,295);
  DrawString(textString);

  NumToString((SInt32) (*textEditStrucHdl)->destRect.top,textString);
  MoveTo(286,295);
  DrawString(textString);

  NumToString((SInt32) GetControlValue(controlRef),textString);
  MoveTo(389,295);
  DrawString(textString);

  NumToString((SInt32) GetControlMaximum(controlRef),textString);
  MoveTo(472,295);
  DrawString(textString);

  RGBForeColor(&blackColour);
  RGBBackColor(&whiteColour);
}

// ****************************************************************************** doErrorAlert

void  doErrorAlert(SInt16 errorCode)
{
  Str255 errorString;
  SInt16 itemHit;

  GetIndString(errorString,rErrorStrings,errorCode);

  if(errorCode < eWindow)
  {
    StandardAlert(kAlertStopAlert,errorString,NULL,NULL,&itemHit);
    ExitToShell();
  }
  else
  {
    StandardAlert(kAlertCautionAlert,errorString,NULL,NULL,&itemHit);
  }
}

// ************************************************************************** navEventFunction

void  navEventFunction(NavEventCallbackMessage callBackSelector,NavCBRecPtr callBackParms,
                       NavCallBackUserData callBackUD)
{
}

// *******************************************************************************************
// HelpDialog.c
// *******************************************************************************************

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

#include <Carbon.h>

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

#define eHelpDialog           8
#define eHelpDocStructure     9
#define eHelpText             10
#define eHelpPicture          11
#define eHelpControls         12
#define rTextIntroduction     128
#define rTextCreatingText     129
#define rTextModifyHelp       130
#define rPictIntroductionBase 128
#define rPictCreatingTextBase 129
#define kTextInset            4

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

typedef struct
{
  Rect      bounds;
  PicHandle pictureHdl;
} pictInfoStructure;

typedef struct
{
  TEHandle          textEditStrucHdl;
  ControlRef        scrollbarHdl;
  SInt16            pictCount;
  pictInfoStructure *pictInfoStructurePtr;
}  docStructure, ** docStructureHandle;

typedef struct
{
  RGBColor     backColour;
  PixPatHandle backPixelPattern;
  Pattern      backBitPattern;
} backColourPattern;

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

GrafPtr                gOldPort;
EventHandlerUPP        helpWindowEventHandlerUPP;
ControlUserPaneDrawUPP userPaneDrawFunctionUPP;
ControlActionUPP       actionFunctionUPP;
SInt16                 gTextResourceID;
SInt16                 gPictResourceBaseID;
RgnHandle              gSavedClipRgn  = NULL;

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

void      doHelp                 (void);
OSStatus  helpWindowEventHandler (EventHandlerCallRef,EventRef,void *);
void      userPaneDrawFunction   (ControlRef,SInt16);
Boolean   doGetText              (WindowRef,SInt16,Rect);
Boolean   doGetPictureInfo       (WindowRef,SInt16);
void      actionFunction         (ControlRef,SInt16);
void      doScrollTextAndPicts   (WindowRef);
void      doDrawPictures         (WindowRef,Rect *);
void      doCloseHelp            (WindowRef);
void      doDisposeDescriptors   (void);
void      doSetBackgroundWhite   (void);

extern void  doErrorAlert        (SInt16);

// ************************************************************************************ doHelp

void  doHelp(void)
{
  OSStatus           osError;
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  ControlRef         controlRef;
  ControlID          controlID;
  Rect               windowRect      = { 0,  0,  353,382 };
  Rect               pushButtonRect  = { 312,297,332,366 };
  Rect               userPaneRect    = { 16, 16, 296,351 };
  Rect               scrollBarRect   = { 16, 350,296,366 };
  Rect               popupButtonRect = { 312,12, 332,256 };
  Rect               destRect, viewRect;
  EventTypeSpec      dialogEvents[] = {{ kEventClassControl, kEventControlClick } };

  GetPort(&gOldPort);

  // ..................................................... create universal procedure pointers

  helpWindowEventHandlerUPP = NewEventHandlerUPP((EventHandlerProcPtr)
                                                 helpWindowEventHandler);
  userPaneDrawFunctionUPP = NewControlUserPaneDrawUPP((ControlUserPaneDrawProcPtr) 
                                                       userPaneDrawFunction);
  actionFunctionUPP = NewControlActionUPP((ControlActionProcPtr) actionFunction);

  // ............................................................... create modal class window

  osError = CreateNewWindow(kMovableModalWindowClass,kWindowStandardHandlerAttribute,
                            &windowRect,&windowRef);
  if(osError == noErr)
  {
    RepositionWindow(windowRef,FrontWindow(),kWindowAlertPositionOnMainScreen);
    SetThemeWindowBackground(windowRef,kThemeBrushDialogBackgroundActive,false);

    InstallWindowEventHandler(windowRef,helpWindowEventHandlerUPP,
                              GetEventTypeCount(dialogEvents),dialogEvents,windowRef,NULL);

    if(!(docStrucHdl = (docStructureHandle) NewHandle(sizeof(docStructure))))
    {
      doErrorAlert(eHelpDocStructure);
      DisposeWindow(windowRef);
      doDisposeDescriptors();
      return;
    }

    SetWRefCon(windowRef,(SInt32) docStrucHdl);
    SetPortWindowPort(windowRef);

    // .......................... create root, push button, user pane, and scroll bar controls

    CreateRootControl(windowRef,&controlRef);

    if((osError = CreatePushButtonControl(windowRef,&pushButtonRect,CFSTR("OK"),&controlRef))
        == noErr)
    {
      SetWindowDefaultButton(windowRef,controlRef);
      controlID.id = 'done';
      SetControlID(controlRef,&controlID);
    }

    if(osError == noErr)
    {
      if((osError = CreateUserPaneControl(windowRef,&userPaneRect,0,&controlRef)) == noErr)
      {
        SetControlData(controlRef,kControlEntireControl,kControlUserPaneDrawProcTag, 
                       sizeof(userPaneDrawFunctionUPP),(Ptr) &userPaneDrawFunctionUPP);
      }
    }

    if(osError == noErr)
    {
      if((osError = CreateScrollBarControl(windowRef,&scrollBarRect,0,0,1,0,true,
                                           actionFunctionUPP,&controlRef)) == noErr)
      (*docStrucHdl)->scrollbarHdl = controlRef;
      controlID.id = 'scro';
      SetControlID(controlRef,&controlID);
    }

    if(osError == noErr)
    {
      if((osError = CreatePopupButtonControl(windowRef,&popupButtonRect,CFSTR("Title:"),131,
                                             false,-1,0,0,&controlRef)) == noErr)
      controlID.id = 'popu';
      SetControlID(controlRef,&controlID);
    }

    if(osError != noErr)
    {
      doErrorAlert(eHelpControls);
      DisposeWindow(windowRef);
      doDisposeDescriptors();
      return;
    }
  }
  else
  {
    doErrorAlert(eHelpDialog);
    doDisposeDescriptors();
    return;
  }
  // .......................... set destination and view rectangles, create TextEdit structure

  InsetRect(&userPaneRect,kTextInset,kTextInset / 2);
  destRect = viewRect = userPaneRect;
    (*docStrucHdl)->textEditStrucHdl = TEStyleNew(&destRect,&viewRect);

  // .................... initialise picture information structure field of document structure

  (*docStrucHdl)->pictInfoStructurePtr = NULL;

  // ............................ assign resource IDs of first topic's 'TEXT'/'styl' resources
  
  gTextResourceID      = rTextIntroduction;
  gPictResourceBaseID  = rPictIntroductionBase;

  // ...................................... load text resources and insert into edit structure

  if(!(doGetText(windowRef,gTextResourceID,viewRect)))
  {
    doCloseHelp(windowRef);
    doDisposeDescriptors();
    return;
  }

  // ......... search for option-space charas in text and load same number of 'PICT' resources

  if(!(doGetPictureInfo(windowRef,gPictResourceBaseID)))
  {
    doCloseHelp(windowRef);
    doDisposeDescriptors();
    return;
  }

  // ............................... create an empty region for saving the old clipping region

  gSavedClipRgn = NewRgn();

  // .......................................................... show window and run modal loop

  ShowWindow(windowRef);
  RunAppModalLoopForWindow(windowRef);
}

// ******************************************************************** helpWindowEventHandler

OSStatus  helpWindowEventHandler(EventHandlerCallRef eventHandlerCallRef,EventRef eventRef,
                                 void *userData)
{
  OSStatus           result = eventNotHandledErr;
  WindowRef          windowRef;
  UInt32             eventClass;
  UInt32             eventKind;
  Point              mouseLocation;
  ControlRef         controlRef;
  ControlPartCode    controlPartCode;
  ControlID          controlID;
  MenuItemIndex      menuItem;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  Rect               viewRect;

  windowRef  = userData;
  eventClass = GetEventClass(eventRef);
  eventKind  = GetEventKind(eventRef);

  if(eventClass == kEventClassControl)
  {
    if(eventKind == kEventControlClick)
    {
      GetEventParameter(eventRef,kEventParamMouseLocation,typeQDPoint,NULL,
                        sizeof(mouseLocation),NULL,&mouseLocation);
      GlobalToLocal(&mouseLocation);
      controlRef = FindControlUnderMouse(mouseLocation,windowRef,&controlPartCode);
      if(controlRef)
      {
        GetControlID(controlRef,&controlID);
        if(controlID.id == 'done')                                              // push button
        {
          if(TrackControl(controlRef,mouseLocation,NULL))
          {
            QuitAppModalLoopForWindow(windowRef);
            doCloseHelp(windowRef);
            doDisposeDescriptors();
            result = noErr;
          }
        }
        if(controlID.id == 'scro')                                               // scroll bar
        {
          TrackControl(controlRef,mouseLocation,actionFunctionUPP);
          result = noErr;  
        }
        else if(controlID.id == 'popu')                                  // pop-up menu button
        {
          TrackControl(controlRef,mouseLocation,(ControlActionUPP) -1);
          menuItem = GetControlValue(controlRef);
          switch(menuItem)
          {
            case 1:
              gTextResourceID     = rTextIntroduction;
              gPictResourceBaseID = rPictIntroductionBase;
              break;

            case 2:
              gTextResourceID     = rTextCreatingText;
              gPictResourceBaseID = rPictCreatingTextBase;
              break;

            case 3:
              gTextResourceID     = rTextModifyHelp;
              break;
          }

          docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
          textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
          viewRect = (*textEditStrucHdl)->viewRect;

          if(!(doGetText(windowRef,gTextResourceID,viewRect)))
          {
            doCloseHelp(windowRef);
            doDisposeDescriptors();
            return;
          }

          if(!(doGetPictureInfo(windowRef,gPictResourceBaseID)))
          {
            doCloseHelp(windowRef);
            doDisposeDescriptors();
            return;
          }

          doDrawPictures(windowRef,&viewRect);

          result = noErr;
        }
      }
    }
  }

  return result;
}

// ********************************************************************** userPaneDrawFunction

void  userPaneDrawFunction(ControlRef controlRef,SInt16 thePart)
{
  Rect               itemRect, viewRect;
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  Boolean           inState;

  windowRef = GetControlOwner(controlRef);

  GetControlBounds(controlRef,&itemRect);
  InsetRect(&itemRect,1,1);
  itemRect.right += 15;

  if(IsWindowVisible(windowRef))
    inState = IsWindowHilited(windowRef);
  DrawThemeListBoxFrame(&itemRect,inState);

  doSetBackgroundWhite();
  EraseRect(&itemRect);

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
  viewRect = (*textEditStrucHdl)->viewRect;

  TEUpdate(&viewRect,textEditStrucHdl);
  doDrawPictures(windowRef,&viewRect);
}

// ********************************************************************************* doGetText

Boolean  doGetText(WindowRef windowRef,SInt16 textResourceID,Rect viewRect)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  Handle             helpTextHdl;
  StScrpHandle       stylScrpStrucHdl;
  SInt16             numberOfLines, heightOfText, heightToScroll;

  doSetBackgroundWhite();

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  TESetSelect(0,32767,textEditStrucHdl);
  TEDelete(textEditStrucHdl);

  (*textEditStrucHdl)->destRect = (*textEditStrucHdl)->viewRect;
  SetControlValue((*docStrucHdl)->scrollbarHdl,0);

  helpTextHdl = GetResource('TEXT',textResourceID);
  if(helpTextHdl == NULL)
  {
    doErrorAlert(eHelpText);
    return false;
  }

  stylScrpStrucHdl = (StScrpHandle) GetResource('styl',textResourceID);
  if(stylScrpStrucHdl == NULL)
  {
    doErrorAlert(eHelpText);
    return false;
  }

  TEStyleInsert(*helpTextHdl,GetHandleSize(helpTextHdl),stylScrpStrucHdl,textEditStrucHdl);

  ReleaseResource(helpTextHdl);
  ReleaseResource((Handle) stylScrpStrucHdl);

  numberOfLines = (*textEditStrucHdl)->nLines;
  heightOfText = TEGetHeight((SInt32) numberOfLines,1,textEditStrucHdl);

  if(heightOfText > (viewRect.bottom - viewRect.top))
  {
    heightToScroll = TEGetHeight((SInt32) numberOfLines,1,textEditStrucHdl) -
                                 (viewRect.bottom - viewRect.top);
    SetControlMaximum((*docStrucHdl)->scrollbarHdl,heightToScroll);
    ActivateControl((*docStrucHdl)->scrollbarHdl);
    SetControlViewSize((*docStrucHdl)->scrollbarHdl,(*textEditStrucHdl)->viewRect.bottom - 
                        (*textEditStrucHdl)->viewRect.top);
  }
  else
  {
    DeactivateControl((*docStrucHdl)->scrollbarHdl);
  }

  return true;
}

// ************************************************************************** doGetPictureInfo

Boolean  doGetPictureInfo(WindowRef windowRef,SInt16 firstPictID)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  Handle             textHdl;
  SInt32             offset, textSize;
  SInt16             numberOfPicts, a, lineHeight, fontAscent;
  SInt8              optionSpace[1] = "\xCA";
  pictInfoStructure  *pictInfoPtr;
  Point              picturePoint;
  TextStyle          whatStyle;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);

  if((*docStrucHdl)->pictInfoStructurePtr != NULL)
  {
    for(a=0;a<(*docStrucHdl)->pictCount;a++)
      ReleaseResource((Handle) (*docStrucHdl)->pictInfoStructurePtr[a].pictureHdl);

    DisposePtr((Ptr) (*docStrucHdl)->pictInfoStructurePtr);
    (*docStrucHdl)->pictInfoStructurePtr = NULL;
  }

  (*docStrucHdl)->pictCount = 0;

  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
  textHdl = (*textEditStrucHdl)->hText;

  textSize = GetHandleSize(textHdl);
  offset = 0;
  numberOfPicts = 0;

  HLock(textHdl);

  offset = Munger(textHdl,offset,optionSpace,1,NULL,0);
  while((offset >= 0) && (offset <= textSize))
  {
    numberOfPicts++;
    offset++;
    offset = Munger(textHdl,offset,optionSpace,1,NULL,0);    
  }
  
  if(numberOfPicts == 0)
  {
    HUnlock(textHdl);
    return true;
  }

  pictInfoPtr = (pictInfoStructure *) NewPtr(sizeof(pictInfoStructure) * numberOfPicts);
  (*docStrucHdl)->pictInfoStructurePtr = pictInfoPtr;

  offset = 0L;

  for(a=0;a<numberOfPicts;a++)
  {
    pictInfoPtr[a].pictureHdl = GetPicture(firstPictID + a);
    if(pictInfoPtr[a].pictureHdl == NULL)
    {
      doErrorAlert(eHelpPicture);
      return false;
    }

    offset = Munger(textHdl,offset,optionSpace,1,NULL,0);
    picturePoint = TEGetPoint((SInt16)offset,textEditStrucHdl);

    TEGetStyle(offset,&whatStyle,&lineHeight,&fontAscent,textEditStrucHdl);
    picturePoint.v -= lineHeight;
    offset++;
    pictInfoPtr[a].bounds = (**pictInfoPtr[a].pictureHdl).picFrame;

    OffsetRect(&pictInfoPtr[a].bounds,
               (((*textEditStrucHdl)->destRect.right + (*textEditStrucHdl)->destRect.left) -
               (pictInfoPtr[a].bounds.right + pictInfoPtr[a].bounds.left) ) / 2,
               - pictInfoPtr[a].bounds.top + picturePoint.v);
  }

  (*docStrucHdl)->pictCount = a;

  HUnlock(textHdl);

  return true;
}

// **************************************************************************** actionFunction

void  actionFunction(ControlRef scrollbarHdl,SInt16 partCode)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             delta, oldValue, offset, lineHeight, fontAscent;
  Point              thePoint;
  Rect               viewRect, portRect;
  TextStyle          style;

  if(partCode)
  {
    windowRef = GetControlOwner(scrollbarHdl);
    docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
    textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
    viewRect = (*textEditStrucHdl)->viewRect;
    thePoint.h = viewRect.left + kTextInset;

    if(partCode != kControlIndicatorPart)
    {
      switch(partCode)
      {
        case kControlUpButtonPart:
          thePoint.v = viewRect.top - 4;
          offset = TEGetOffset(thePoint,textEditStrucHdl);
          thePoint = TEGetPoint(offset,textEditStrucHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,textEditStrucHdl);
          delta = thePoint.v - lineHeight - viewRect.top;
          break;

        case kControlDownButtonPart:
          thePoint.v = viewRect.bottom + 2;
          offset = TEGetOffset(thePoint,textEditStrucHdl);
          thePoint = TEGetPoint(offset,textEditStrucHdl);
          delta = thePoint.v - viewRect.bottom;
          break;

        case kControlPageUpPart:
          thePoint.v = viewRect.top + 2;
          offset = TEGetOffset(thePoint,textEditStrucHdl);
          thePoint = TEGetPoint(offset,textEditStrucHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,textEditStrucHdl);
          thePoint.v += lineHeight - fontAscent;
          thePoint.v -= viewRect.bottom - viewRect.top;
          offset = TEGetOffset(thePoint,textEditStrucHdl);
          thePoint = TEGetPoint(offset,textEditStrucHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,textEditStrucHdl);
          delta = thePoint.v - viewRect.top;
          if(offset == 0)
            delta -= lineHeight;
          break;

        case kControlPageDownPart:
          thePoint.v = viewRect.bottom - 2;
          offset = TEGetOffset(thePoint,textEditStrucHdl);
          thePoint = TEGetPoint(offset,textEditStrucHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,textEditStrucHdl);
          thePoint.v -= fontAscent;
          thePoint.v += viewRect.bottom - viewRect.top;
          offset = TEGetOffset(thePoint,textEditStrucHdl);
          thePoint = TEGetPoint(offset,textEditStrucHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,textEditStrucHdl);
          delta =  thePoint.v - lineHeight - viewRect.bottom;
          if(offset == (**textEditStrucHdl).teLength)
            delta += lineHeight;
          break;
      }

      oldValue = GetControlValue(scrollbarHdl);

      if(((delta < 0) && (oldValue > 0)) || ((delta > 0) && 
         (oldValue < GetControlMaximum(scrollbarHdl))))
      {
        GetClip(gSavedClipRgn);
        GetWindowPortBounds(windowRef,&portRect);
        ClipRect(&portRect);

        SetControlValue(scrollbarHdl,oldValue + delta);
        SetClip(gSavedClipRgn);
      }
    }

    doScrollTextAndPicts(windowRef);
  }
}

// ********************************************************************** doScrollTextAndPicts

void  doScrollTextAndPicts(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             scrollDistance, oldScroll;
  Rect               updateRect;

  doSetBackgroundWhite();

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  oldScroll = (*textEditStrucHdl)->viewRect.top -(*textEditStrucHdl)->destRect.top;
  scrollDistance = oldScroll - GetControlValue((*docStrucHdl)->scrollbarHdl);
  if(scrollDistance == 0)
    return;

  TEScroll(0,scrollDistance,textEditStrucHdl);

  if((*docStrucHdl)->pictCount == 0)
    return;

  updateRect = (*textEditStrucHdl)->viewRect;

  if(scrollDistance > 0)
  {
    if(scrollDistance < (updateRect.bottom - updateRect.top))
      updateRect.bottom = updateRect.top + scrollDistance;
  }
  else
  {
    if( - scrollDistance < (updateRect.bottom - updateRect.top))
      updateRect.top = updateRect.bottom + scrollDistance;
  }

  doDrawPictures(windowRef,&updateRect);
}

// **************************************************************************** doDrawPictures

void  doDrawPictures(WindowRef windowRef,Rect *updateRect)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             pictCount, pictIndex, vOffset;
  PicHandle          thePictHdl;
  Rect               pictLocRect, dummyRect;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  vOffset = (*textEditStrucHdl)->destRect.top - 
            (*textEditStrucHdl)->viewRect.top - kTextInset;
  pictCount = (*docStrucHdl)->pictCount;

  for(pictIndex = 0;pictIndex < pictCount;pictIndex++)
  {
    pictLocRect = (*docStrucHdl)->pictInfoStructurePtr[pictIndex].bounds;
    OffsetRect(&pictLocRect,0,vOffset);

    if(!SectRect(&pictLocRect,updateRect,&dummyRect))
      continue;

    thePictHdl = (*docStrucHdl)->pictInfoStructurePtr[pictIndex].pictureHdl;

    LoadResource((Handle) thePictHdl);
    HLock((Handle) thePictHdl);

    GetClip(gSavedClipRgn);
    ClipRect(updateRect);
    DrawPicture(thePictHdl,&pictLocRect);

    SetClip(gSavedClipRgn);
    HUnlock((Handle) thePictHdl);
  }
}

// ******************************************************************************* doCloseHelp

void  doCloseHelp(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             a;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  if(gSavedClipRgn)
    DisposeRgn(gSavedClipRgn);

  if((*docStrucHdl)->textEditStrucHdl)
    TEDispose((*docStrucHdl)->textEditStrucHdl);

  if((*docStrucHdl)->pictInfoStructurePtr)
  {
    for(a=0;a<(*docStrucHdl)->pictCount;a++)
      ReleaseResource((Handle) (*docStrucHdl)->pictInfoStructurePtr[a].pictureHdl);
    DisposePtr((Ptr) (*docStrucHdl)->pictInfoStructurePtr);
  }

  DisposeHandle((Handle) docStrucHdl);
  DisposeWindow(windowRef);
  SetPort(gOldPort);
}

// ********************************************************************** doDisposeDescriptors

void  doDisposeDescriptors(void)
{
  DisposeEventHandlerUPP(helpWindowEventHandlerUPP);
  DisposeControlUserPaneDrawUPP(userPaneDrawFunctionUPP);
  DisposeControlActionUPP(actionFunctionUPP);
}

// ********************************************************************** doSetBackgroundWhite

void  doSetBackgroundWhite(void)
{
  RGBColor whiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };
  Pattern  whitePattern;

  RGBBackColor(&whiteColour);
  BackPat(GetQDGlobalsWhite(&whitePattern));
}

// *******************************************************************************************

Demonstration Program MonoTextEdit Comments

When this program is run, the user should explore both the text editor and the Help dialog.

Text Editor

In the text editor, the user should perform all the actions usually associated with a simple
text editor, that is:

o Open a new document window, open an existing 'TEXT' file for display in a new document
  window, and save a document to a 'TEXT' file.  (A 'TEXT' file titled "MonoTextEdit Document"
  is included.)

o Enter new text and use the Edit menu Cut, Copy, Paste, and Clear commands to edit the text. 
  (Pasting between documents and other applications is supported.)

o Select text by clicking and dragging, double-clicking a word, shift-clicking, and choosing
  the Select All command from the Edit menu.  Also select large amounts of text by clicking in
  the text and dragging the cursor above or below the window so as to invoke auto-scrolling.

o Scroll a large document by dragging the scroll box/scroller (live scrolling is used),
  clicking once in a scroll arrow or gray area/track, and holding the mouse down in a scroll
  arrow or gray area/track.

Whenever any action is taken, the user should observe the changes to the values displayed in
the data panel at the bottom of each window.  In particular, the relationship between the
destination rectangle and scroll bar control value should be noted.

The user should also note that outline highlighting is activated for all windows and that the
forward-delete key is supported by the application.  (The forward-delete key is not supported
by TextEdit.)

Help Dialog

The user should choose MonoTextEdit Help from the Mac OS 8/9 Apple menu or Mac OS X Help menu
to open the Help dialog and then scroll through the three help topics, which may be chosen in
the pop-up menu at the bottom of the dialog.  The help topics contain documentation on the Help
dialog which supplements the source code comments below.

MonoTextEdit.c

defines

kMaxTELength represents the maximum allowable number of bytes in a TextEdit structure.  kTab,
kBackSpace, kForwardDelete, kReturn, and kEscape representing the character codes generated by
the tab, delete, forward delete, Return and escape keys.

typedefs

The docStructure data type will be used for a small document structure comprising a handle to a
TextEdit structure and a handle to a vertical scroll bar.

Global Variables

scrollActionFunctionUPP will be assigned a universal procedure pointer to an action (callback)
function for the scroll bar.  customClickLoopUPP will be assigned a universal procedure pointer
to a custom click loop (callback) function.  gOldControlValue will be assigned the scroll bar's
control value.

main

The main function creates universal procedure pointers for the application-defined scroll
action and custom click loop (callback) functions and installs a timer set to fire repeatedly
at the interval returned by a call to GetCaretTime.  When the timer fires, the function doIdle
is called.

windowEventHandler

When the kEventClassMouse event type is received, if the call to FindControlUnderMouse reveals
that a control (that is, the vertical scroll bar) is under the mouse, TrackControl is called
with a universal procedure pointer to an action function passed in the third parameter.

When the kEventRawKeyDown and kEventRawKeyRepeat event types are received, the function
doKeyEvent is called only if the Command key was not down.

doIdle

doIdle is called whenever the installed timer fires.

The first line gets a reference to the front window.  If the front window is a document window,
a handle to the window's document structure is retrieved.  A handle to the TextEdit structure
associated with the window is stored in the document structure's textEditStrucHdl field.  This
is passed in the call to TEIdle, which blinks the insertion point caret.

doKeyEvent

doKeyEvent is called when the kEventRawKeyDown and kEventRawKeyRepeat event types are received. 
It handles all key-down events that are not Command key equivalents.

If the character code is equal to that for the escape key or lower, and except if it is the
carriage return or backspace character, the function simply returns. 

The first three lines get a handle to the TextEdit structure whose handle is stored in the
front window's document structure.

The next line filters out the tab key character code.  (TextEdit does not support the tab key
and some applications may need to provide a tab key handler.)

The next character code to be filtered out is the forward-delete key character code.  TextEdit
does not recognise this key, so this else if block provides forward-delete key support for the
program.  The first line in this block gets the current selection length from the TextEdit
structure.  If this is zero (that is, there is no selection range and an insertion point is
being displayed), the selEnd field is incremented by one.  This, in effect, creates a selection
range comprising the character following the insertion point.  TEDelete deletes the current
selection range from the TextEdit structure.  Such deletions could change the number of text
lines in the TextEdit structure, requiring the vertical scroll bar to be adjusted; hence the
call to the function doAdjustScrollbar.

Processing of those character codes which have not been filtered out is performed in the else
block.  A new character must not be allowed to be inserted if the TextEdit limit of 32,767
characters will be exceeded.  Accordingly, and given that TEKey replaces the selection range
with the character passed to it, the first step is to get the current selection length.  If the
current number of characters minus the selection length plus 1 is less than 32,767, the
character code is passed to TEKey for insertion into the TextEdit structure.  In addition, and
since all this could change the number of lines in the TextEdit structure, the scroll bar
adjustment function is called.

If the TextEdit limit will be exceeded by accepting the character, an alert is invoked advising
the user of the situation.

The last line calls a function which prints data extracted from the edit text and control
structures at the bottom of the window.

scrollActionFunction

scrollActionFunction is associated with the vertical scroll bar.  It is the callback function
which will be repeatedly called by TrackControl when the kEventMouseDown event type is
received.  It will be called repeatedly while the mouse button remains down in the scroll
box/scroller, scroll arrows or gray areas/track of the vertical scroll bar.

The first line gets a reference to the window object for the window which "owns" the control. 
The next two lines get a handle to the TextEdit structure associated with the window.

Within the outer if block, the first if block executes if the control part is not the scroll
box/scroller (that is, the indicator).  The purpose of the switch is to get a value into the
variable linesToScroll.  If the mouse-down was in a scroll arrow, that value will be 1.  If the
mouse-down was in a gray area/track, that value will be equivalent to one less than the number
of text lines that will fit in the view rectangle.  (Subtracting 1 from the total number of
lines that will fit in the view rectangle ensures that the line of text at the bottom/top of
the view rectangle prior to a gray area/track scroll will be visible at the top/bottom of the
window after the scroll.)

Immediately after the switch, the value in linesToScroll is changed to a negative value if the
mouse-down occurred in either the down scroll arrow or down gray area/track.

The next block ensures that no scrolling action will occur if the document is currently
scrolled fully up (control value equals control maximum) or fully down (control value equals
0).  In either case, linesToScroll will be set to 0, meaning that the call to TEScroll near the
end of the function will not occur.

SetControlValue sets the control value to the value just previously calculated, that is, to the
current control value minus the value in linesToScroll.

The next line sets the value in linesToScroll back to what it was before the line linesToScroll
= controlvalue - linesToScroll executed.  This value, multiplied by the value in the lineHeight
field of the TextEdit structure, is later passed to TEScroll as the parameter which specifies
the number of pixels to scroll.

If the control part is the scroll box/scroller (that is, the indicator), the variable
linesToScroll is assigned a value equal to the control's value as it was the last time this
function was called minus the control's current value.  The global variable which holds the
control's "old" value is then assigned the control's current value preparatory to the next call
to this function.

With the number of lines to scroll determined, TEScroll is called to scroll the text within the
view rectangle by the number of pixels specified in the second parameter.  (Positive values
scroll the text towards the bottom of the screen.  Negative values scroll the text towards the
top.)

The last line is for demonstration purposes only.  It calls the function which prints data
extracted from the edit and control structures at the bottom of the window.

doInContent

doInContent is called when the kEventWindowClickContentRgn event type is received.

The first three lines retrieve a handle to the TextEdit structure associated with the front
window.  The call to PtInRect checks whether the mouse-down occurred within the view rectangle. 
(Note that the view rectangle is in local coordinates, so the mouse-down coordinates passed as
the first parameter to the PtInRect call must also be in local coordinates.)  If the mouse-down
was in the view rectangle, TEClick is called to advise TextEdit of the mouse-down event.  Note
that the position of the shift key is passed in the second parameter.  (TEClick's behaviour
depends on the position of the shift key.)

doDrawContent

doDrawContent is called when the kEventWindowDrawContent event type is received.

The first two lines get the handle to the TextEdit structure associated with the window.

UpdateControls is called to draw the scroll bar.  The call to TEUpdate draws the text currently
in the TextEdit structure.

doActivateDeactivate

doActivateDeactivatew performs window activation/deactivation.  It is called when the
kEventWindowActivated and kEventWindowDecativated event types are received.

The first two lines retrieve a handle to the TextEdit structure for the window.  If the window
is becoming active, its graphics port is set as the current graphics port.  The bottom of the
view rectangle is then adjusted so that the height of the view rectangle is an exact multiple
of the value in the lineHeight field of the TextEdit structure.  (This avoids the possibility
of only part of the full height of a line of text appearing at the bottom of the view
rectangle.)  TEActivate activates the TextEdit structure associated with the window,
ActivateControl activates the scroll bar, doAdjustScrollbar adjusts the scroll bar, and
doAdjustCursor adjusts the cursor shape.

If the window is becoming inactive, TEDeactivate deactivates the TextEdit structure associated
with the window and DeactivateControl deactivates the scroll bar.

doNewDocWindow

doNewDocWindow is called at program launch and when the user chooses New or Open from the File
menu.  It opens a new window, associates a document structure with that window, creates a
vertical scroll bar, creates a monostyled TextEdit structure, installs the custom click loop
(callback) function, enables automatic scrolling, and enables outline highlighting.

The call to CreateNewWindow and the following block creates a new window with the standard
document window attributes less the size box/resize control.  Note that the window's graphics
port is set as the current port before the later call to TENew.  (Since the TextEdit structure
assumes the drawing environment specified in the graphics port structure, setting the graphics
port must be done before TENew creates the TextEdit structure.)

The call to TextSize sets the text size.  This, together with the default application font,
will be copied from the graphics port to the TextEdit structure when TENew is called.)

After the window event handler is installed, a document structure is created and the handle
stored in the window's window object.  The following line increments the global variable which
keeps track of the number of open windows.  GetNewControl creates a vertical scroll bar and
assigns a handle to it to the appropriate field of the document structure.  The next block
establishes the view and destination rectangles two pixels inside the window's port rectangle
less the scroll bar.

MoveHHi and HLock move the document structure high and lock it.  A monostyled TextEdit
structure is then created by TENew and its handle is assigned to the appropriate field of the
document structure.  (If this call is not successful, the window and scroll bar are disposed
of, an error alert is displayed, and the function returns.)  The handle to the document
structure is then unlocked.

TESetClickLoop installs the universal procedure pointer to the custom click loop (callback)
function customClickLoop in the clickLoop field of the TextEdit structure.  TEAutoView enables
automatic scrolling for the TextEdit structure.  TEFeatureFlag enables outline highlighting for
the TextEdit structure.

The last line returns a reference to the newly opened window's window object.

customClickLoop

customClickLoop replaces the default click loop function so as to provide for scroll bar
adjustment in concert with automatic scrolling.  Following a mouse-down within the view
rectangle, customClickLoop is called repeatedly by TEClick as long as the mouse button remains
down.

The first three lines retrieve a handle to the TextEdit structure associated with the window. 
The next two lines save the current graphics port and set the window's graphics port as the
current port.

The window's current clip region will have been set by TextEdit to be equivalent to the view
rectangle.  Since the scroll bar has to be redrawn, the clipping region must be temporarily
reset to include the scroll bar.  Accordingly, GetClip saves the current clipping region and
the following two lines set the clipping region to the bounds of the coordinate plane.

GetMouse gets the current position of the cursor.  If the cursor is above the top of the port
rectangle, the text must be scrolled downwards.  Accordingly, the variable linesToScroll is set
to 1.  The subsidiary function doSetScrollBarValue (see below) is then called to, amongst other
things, reset the scroll bar's value.  Note that the value in linesToScroll may be modified by
doSetScrollBarValue.  If linesToScroll is not set to 0 by doSetScrollBarValue, TEScroll is
called to scroll the text by a number of pixels equivalent to the value in the lineHeight field
of the TextEdit structure, and in a downwards direction.

If the cursor is below the bottom of the port rectangle, the same process occurs except that
the variable linesToScroll is set to -1, thus causing an upwards scroll of the text (assuming
that the value in linesToScroll is not changed to 0 by doSetScrollBarValue).

If scrolling has occurred, doDrawDataPanel redraws the data panel.  SetClip restores the
clipping region to that established by the view rectangle and SetPort restores the saved
graphics port.  Finally, the last line returns true.  (A return of false would cause TextEdit
to stop calling customClickLoop, as if the user had released the mouse button.)

doSetScrollBarValue

doSetScrollBarValue is called from customClickLoop.  Apart from setting the scroll bar's value
so as to cause the scroll box to follow up automatic scrolling, the function checks whether the
limits of scrolling have been reached.

The first two lines get the current control value and the current control maximum value.  At
the next block, the value in the variable linesToScroll will be set to either 0 (if the current
control value is 0) or equivalent to the control maximum value (if the current control value is
equivalent to the control maximum value.  If these modifications do not occur, the value in
linesToScroll will remain as established at the first line in this block, that is, the current
control value minus the value in linesToScroll as passed to the function.

SetControlValue sets the control's value to the value in linesToScroll.  The last line sets the
value in linesToScroll to 0 if the limits of scrolling have already been reached, or to the
value as it was when the doSetScrollBarValue function was entered.

doAdjustMenus

doAdjustMenus adjusts the menus.  Much depends on whether any windows are currently open.

If at least one window is open, the first three lines in the if block get a handle to the
TextEdit structure associated with the front window and the first call to EnableMenuItem
enables the Close item in the File menu.  If there is a current selection range, the Cut, Copy,
and Clear items are enabled, otherwise they are disabled.  If there is data of flavour type
'TEXT' in the scrap (the call to GetScrapFlavourFlags), the Paste item is enabled, otherwise it
is disabled.  If there is any text in the TextEdit structure, the SaveAs and Select All items
are enabled, otherwise they are disabled.

If no windows are open, the Close, SaveAs, Clear, and Select All items are disabled.

doMenuChoice

If the MonoTextEdit Help item in the Mac OS 8/9 Apple menu or Mac OS X Help menu is chosen, the
function doHelp is called.

doFileMenu

doFileMenu handles File menu choices, calling the appropriate functions according to the menu
item chosen.  In the SaveAs case, a handle to the TextEdit structure associated with the front
window is retrieved and passed as a parameter to the function doSaveAsFile.

Note that, because TextEdit, rather than file operations, is the real focus of this program,
the file-related code has been kept to a minimum, even to the extent of having no Save-related,
as opposed to SaveAs-related, code.

doEditMenu

doEditMenu handles choices from the Edit menu.  Recall that, in the case of monostyled TextEdit
structures, TECut, TECopy, and TEPaste do not copy/paste text to/from the scrap.  This program,
however, supports copying/pasting to/from the scrap.

Before the usual switch is entered, a handle to the TextEdit structure associated with the
front window is retrieved.

The iCut case handles the Cut command.  Firstly, the call to ClearCurrentScrap attempts to
clear the scrap.  If the call succeeds, PurgeSpace establishes the size of the largest block in
the heap that would be available if a general purge were to occur.  The next line gets the
current selection length.  If the selection length is greater than the available memory, the
user is advised via an error message.  Otherwise, TECut is called to remove the selected text
from the TextEdit structure and copy it to the TextEdit private scrap.  The scroll bar is
adjusted, and TEToScrap is called to copy the private scrap to the scrap.  If the TEToScrap
call is not successful, ClearCurrentScrap cleans up as best it can by emptying the scrap.

The iCopy case handles the Copy command.  If the call to ClearCurrentScrap to empty the scrap
is successful, TECopy is called to copy the selected text from the TextEdit structure to the
TextEdit private scrap.  TEToScrap then copies the private scrap to the scrap. If the TEToScrap
call is not successful, ClearCurrentScrap cleans up as best it can by emptying the scrap.

The iPaste case handles the Paste command, which must not proceed if the paste would cause the
TextEdit limit of 32,767 bytes to be exceeded.  The third line establishes a value equal to the
number of bytes in the TextEdit structure plus the number of bytes of the 'TEXT' flavour type
in the scrap.  If this value exceeds the TextEdit limit, the user is advised via an error
message.  Otherwise, TEFromScrap copies the scrap to TextEdit's private scrap, TEPaste inserts
the private scrap into the TextEdit structure, and the following line adjusts the scroll bar.

The iClear case handles the Clear command.  TEDelete deletes the current selection range from
the TextEdit structure and the following line adjusts the scroll bar.

The iSelectAll case handle the Select All command.  TESetSelect sets the selection range
according to the first two parameters (selStart and selEnd).

doGetSelectLength

doGetSelectLength returns a value equal to the length of the current selection. 

doAdjustScrollbar

doAdjustScrollbar adjusts the vertical scroll bar.

The first two lines retrieve handles to the document structure and TextEdit structure
associated with the window in question.

At the next block, the value in the nLines field of the TextEdit structure is assigned to the
numberOfLines variable.  The next action is somewhat of a refinement and is therefore not
essential.  If the last character in the TextEdit structure is the return character,
numberOfLines is incremented by one.  This will ensure that, when the document is scrolled to
its end, a single blank line will appear below the last line of text.

At the next block, the variable controlMax is assigned a value equal to the number of lines in
the TextEdit structure less the number of lines that will fit in the view rectangle.  If this
value is less than 0 (indicating that the number of lines in the TextEdit structure is less
than the number of lines that will fit in the view rectangle), controlMax is set to 0. 
SetControlMaximum then sets the control maximum value.  If controlMax is 0, the scroll bar is
automatically unhighlighted by the SetControlMaximum call.

The first line of the next block assigns to the variable controlValue a value equal to the
number of text lines that the top of the destination rectangle is currently "above" the top of
the view rectangle.  If the calculation returns a value less than 0 (that is, the document has
been scrolled fully down), controlValue is set to 0.  If the calculation returns a value
greater than the current control maximum value (that is, the document has been scrolled fully
up), controlValue is set to equal that value.  SetControlValue sets the control value to the
value in controlValue.  For example, if the top of the view rectangle is 2, the top of the
destination rectangle is -34 and the lineHeight field of the TextEdit structure contains the
value 13, the control value will be set to 3.

SetControlViewSize is called to advise the Control Manager of the height of the view rectangle.
This will cause the scroll box/scroller to be a proportional scroll box/scroller.  (On Mac OS
8/9, this assumes that the user has selected Smart Scrolling on in the Appearance control
panel.)

With the control maximum value and the control value set, TEScroll is called to make sure the
text is scrolled to the position indicated by the scroll box/scroller.  Extending the example
in the previous paragraph, the second parameter in the TEScroll call is 2 - (34 - (3 * 13)),
that is, 0.  In that case, no corrective scrolling actually occurs.

doAdjustCursor

doAdjustCursor is called when the kEventMouseMoved, and kEventWindowActivated event types are
received.  It adjusts the cursor to the I-Beam shape when the cursor is over the content region
less the scroll bar area, and to the arrow shape when the cursor is outside that region.  It is
similar to the cursor adjustment function in the demonstration program GworldPicCursIcn
(Chapter 13). 

doCloseWindow

doCloseWindow is called when the kEventWindowClose event type is received and when the user
chooses Close from the File menu.  It disposes of the specified window.  The associated scroll
bar, the associated TextEdit structure and the associated document structure are disposed of
before the call to DisposeWindow.

doSaveAsFile, doOpenCommand, doOpenFile

The functions doSaveAsFile, doOpenCommand, and doOpenFile are document saving and opening
functions, enabling the user to open and save 'TEXT' documents.  Since the real focus of this
program is TextEdit, not file operations, the code is "bare bones" and as brief as possible,
Navigation Services 2.0 functions being used rather than the Navigation Services 3.0 functions
used in the demonstration program Files (Chapter 18).

For a complete example of opening and saving monostyled 'TEXT' documents, see the demonstration
program at Files (Chapter 18).

doDrawDataPanel

doDrawDataPanel draws the data panel at the bottom of each window.  Displayed in this panel are
the values in the teLength, nLines, lineHeight and destRect.top fields of the TextEdit
structure and the contrlValue and contrlMax fields of the scroll bar's control structure.

HelpDialog.c

defines

Constants are established for the index of error strings within a 'STR#' resource and 'TEXT',
'styl', and 'PICT' resource IDs.  kTextInset which will be used to inset the view and
destination rectangles a few pixels inside a user pane's rectangle.

typedef

The first two data types are for a picture information structure and a document structure. 
(Note that one field in the document structure is a pointer to a picture information
structure.)  The third data type will be used for saving and restoring the background colour
and pattern.

doHelp

doHelp is called when the user chooses the MonoTextEdit Help item in the Help menu.

The dialog will utilise a window event handler, a user pane drawing (callback) function, and a
control action (callback) function.  The first block creates the associated universal procedure
pointers.

The call to CreateNewWindow creates a new window of the movable modal class.  NewHandle creates
a block for a document structure and the handle is stored in the window object.  

The next block creates the dialog's controls and assigns an ID to each.  In the case of the
user pane control, SetControlData is called to set a user pane drawing function.  In the case
of the scroll bar, the control reference is assigned to the appropriate field of the dialog's
document structure.

At the next block, the destination and view rectangles are both made equal to the user pane's
rectangle, but inset four pixels from the left and right and two pixels from the top and
bottom.  The call to TEStyleNew creates a multistyled TextEdit structure based on those two
rectangles.

A pointer to a picture information structure will eventually be assigned to a field in the
document structure.  For the moment, that field is set to NULL.

At the next block, two global variables are assigned the resource IDs relating to the first
Help topic's 'TEXT'/'styl' resource and associated 'PICT' resources.

The next block calls the function doGetText which, amongst other things, loads the specified
'TEXT'/'styl' resources and inserts the text and style information into the TextEdit structure.

The next block calls the function doGetPictureInfo which, amongst other things, searches for
option-space characters in the 'TEXT' resource and, if option-space characters are found, loads
a like number of 'PICT' resources beginning with the specified ID.

NewRgn creates an empty region, which will be used to save the dialog's graphic's port's
clipping region.

To complete the initial setting up, ShowWindow is called to makes the dialog visible, following
which RunAppModalLoopForWindow is called to run the nodal loop.

helpWindowEventHandler

helpWindowEventHandler is the event handler for the dialog.  It responds to mouse clicks in the
dialog's three controls.

If the push button was clicked, QuitAppModalLoopForWindow is called to terminate the modal loop
and restore menu activation/deactivation status to that which obtained prior to the call to
RunAppModalLoopForWindow, and the dialog is closed down.

Note that, if the click was in the scroll bar, TrackControl is called with a universal
procedure pointer to an application-defined action (callback) function is passed in the
actionProc parameter.

If the click was in the pop-up menu button, the menu item chosen is determined, and the switch
assigns the appropriate 'TEXT'/'styl' and 'PICT' resource IDs to the global variables which
keep track of which of those resources are to be loaded and displayed.

The next three lines get the view rectangle from the TextEdit structure, allowing the next
blocks to perform the same "get text" and "get picture information" actions as were preformed
at start-up, but this time with the 'TEXT'/'styl' and 'PICT' resources as determined within the
preceding switch.

The call to doDrawPictures draws any pictures that might initially be located in the view
rectangle.

userPaneDrawFunction

userPaneDrawFunction is the user pane drawing function set within doHelp.

The first line gets a reference to the user pane control's owning window.  The next three lines
get the user pane's rectangle, insets that rectangle by one pixel all round, and then further
expands it to the right edge of the scroll bar.  At the next block, a list box frame is drawn
in the appropriate state, depending on whether the the movable modal dialog is currently the
active window.

The next block erases the previously defined rectangle with the white colour using the white
pattern.

The next three lines retrieve the view rectangle from the TextEdit structure.  The call to
TEUpdate draws the text in the TextEdit structure in the view rectangle.  The call to
doDrawPictures draws any pictures that might currently be located in the view rectangle.

doGetText

doGetText is called when the dialog is first opened and when the user chooses a new item from
the pop-up menu.  Amongst other things, it loads the 'TEXT'/'styl' resources associated with
the current menu item and inserts the text and style information into the TextEdit structure.

The first two lines get a handle to the TextEdit structure.  The next two lines set the
selection range to the maximum value and then delete that selection.  The destination rectangle
is then made equal to the view rectangle and the scroll bar's value is set to 0.

GetResource is called twice to load the specified 'TEXT'/'styl' resources, following which
TEStyleInsert is called to insert the text and style information into the TextEdit structure. 
Two calls to ReleaseResource then release the 'TEXT'/'styl' resources.

The next block gets the total height of the text in pixels.

At the next block, if the height of the text is greater than the height of the view rectangle,
the local variable heightToScroll is made equal to the total height of the text minus the
height of the view rectangle.  This value is then used to set the scroll bar's maximum value. 
The scroll bar is then made active.

SetControlViewSize is called to advise the Control Manager of the height of the view rectangle. 
This will cause the scroll box to be a proportional scroll box.

If the height of the text is less than the height of the view rectangle, the scroll bar is made
inactive.

true is returned if the GetResource calls did not return with false.

doGetPictureInfo

doGetPictureInfo is called after getText when the dialog is opened and when the user chooses a
new item from the pop-up menu.  Amongst other things, it searches for option-space characters
in the 'TEXT' resource and, if option-space characters are found, loads a like number of 'PICT'
resources beginning with the specified ID.

The first line gets a handle to the dialog's document structure.

If the picInfoRecPtr field of the document structure does not contain NULL, the currently
loaded 'PICT' resources are released, the picture information structures are disposed of, and
the picInfoRecPtr field of the document structure is set to NULL.

The next line sets to 0 the field of the document structure which keeps track of the number of
pictures associated with the current 'TEXT' resource.

The next two lines get a handle to the TextEdit structure, then a handle to the block
containing the actual text.  This latter is then used to assign the size of that block to a
local variable.  After two local variables are initialised, the block containing the text is
locked.

The next block counts the number of option-space characters in the text block.  At the
following block, if there are no option-space characters in the block, the block is unlocked
and the function returns.

A call to NewPtr then allocates a nonrelocatable block large enough to accommodate a number of
picture information structures equal to the number of option-space characters found.  The
pointer to the block is then assigned to the appropriate field of the dialog's document
structure.

The next line resets the offset value to 0.

The for loop repeats for each of the option-space characters found.  GetPicture loads the
specified 'PICT' resource (the resource ID being incremented from the base ID at each pass
through the loop) and assigns the handle to the appropriate field of the relevant picture
information structure.  Munger finds the offset to the next option-space character and
TEGetPoint gets the point, based on the destination rectangle, of the bottom left of the
character at that offset.  TEGetStyle is called to obtain the line height of the character at
the offset and this value is subtracted from the value in the point's v field.  The offset is
incremented and the rectangle in the picture structure's picFrame field is assigned to the
bounds field of the picture information structure.  The next block then offsets this rectangle
so that it is centred laterally in the destination rectangle with its top offset from the top
of the destination rectangle by the amount established at the line picturePoint.v -=
lineHeight;.

The third last line assigns the number of pictures loaded to the appropriate field of the
dialog's document structure.  The block containing the text is then unlocked.  The function
returns true if false has not previously been returned within the for loop.

actionFunction

actionFunction is the action function called from within the event filter (callback) function
eventFilter.  It is repeatedly called by TrackControl while the mouse button remains down
within the scroll bar.  Its ultimate purpose is to determine the new scrollbar value when the
mouse-down is within the scroll arrows or gray areas/track of the scroll bar, and then call a
separate function to effect the actual scrolling of the text and pictures based on the new
scrollbar value.  (The scroll bar is the live scrolling variant, so the CEDF automatically
updates the control's value while the mouse remains down in the scroll box/scroller.)

Firstly, if the cursor is still not within the control, execution falls through to the bottom
of the function and the action function exits.

The first block gets a pointer to the owner of the scrollbar, retrieves a handle to the
dialog's document structure, gets a handle to the TextEdit structure, gets the view rectangle,
and assigns a value to the h field of a point variable equal to the left of the view rectangle
plus 4 pixels.

The switch executes only if the mouse-down is not in the scroll box.

In the case of the Up scroll arrow, the variable delta is assigned a value which will ensure
that, after the scroll, the top of the incoming line of text will be positioned cleanly at top
of the view rectangle.

In the case of the Down scroll arrow, the variable delta is assigned a value which will ensure
that, after the scroll, the bottom of the incoming line of text will be positioned cleanly at
bottom of the view rectangle.

In the case of the Up gray area/track, the variable delta is assigned a value which will ensure
that, after the scroll, the top of the top line of text will be positioned cleanly at the top
of the view rectangle and the line of text which was previously at the top will still be
visible at the bottom of the view rectangle.

In the case of the Down gray area/track, the variable delta is assigned a value which will
ensure that, after the scroll, the bottom of the bottom line of text will be positioned cleanly
at the bottom of the view rectangle and the line of text which was previously at the bottom
will still be visible at the top of the view rectangle.

The first line after the switch gets the pre-scroll scroll bar value.  If the text is not fully
scrolled up and a scroll up is called for, or if the text is not fully scrolled down and a
scroll down is called for, the current clipping region is saved, the clipping region is set to
the dialog's port rectangle, the scroll bar value is set to the required new value, and the
saved clipping region is restored.  (TextEdit may have set the clipping region to the view
rectangle, so it must be changed to include the scroll bar area, otherwise the scroll bar will
not be drawn.)

With the scroll bar's new value set and the scroll box redrawn in its new position, the
function for scrolling the text and pictures is called.  Note that this last line will also be
called if the mouse-down was within the scroll box.

doScrollTextAndPicts

doScrollTextAndPicts is called from actionFunction.  It scrolls the text within the view
rectangle and calls another function to draw any picture whose rectangle intersects the
"vacated" area of the view rectangle.

The first line sets the background colour to white and the background pattern to white.

The next two lines get a handle to the TextEdit structure.  The next line determines the
difference between the top of the destination rectangle and the top of the view rectangle and
the next subtracts from this value the scroll bar's new value.  If the result is zero, the text
must be fully scrolled in one direction or the other, so the function simply returns.

If the text is not already fully scrolled one way or the other, TEScroll scrolls the text in
the view rectangle by the number of pixels determined at the fifth line.

If there are no pictures associated with the 'TEXT' resource in use, the function returns
immediately after the text is scrolled.

The next consideration is the pictures and whether any of their rectangles, as stored in the
picture information structure, intersect the area of the view rectangle "vacated" by the
scroll.  At the if/else block, a rectangle is made equal to the "vacated" area of the view
rectangle, the if block catering for the scrolling up case and the else block catering for the
scrolling down case.  This rectangle is passed as a parameter in the call to drawPictures.

doDrawPictures

doDrawPictures determines whether any pictures intersect the rectangle passed to it as a formal
parameter and draws any pictures that do.

The first two lines get handles to the dialog's document structure and the TextEdit structure.

The next line determines the difference between the top of the destination rectangle and the
top of the view rectangle.  This will be used later to offset the picture's rectangle from
destination rectangle coordinates to view rectangle coordinates.  The next line determines the
number of pictures associated with the current 'TEXT' resource, a value which will be used to
control the number of passes through the following for loop.

Within the loop, the picture's rectangle is retrieved from the picture information structure
and offset to the coordinates of the view rectangle.  SectRect determines whether this
rectangle intersects the rectangle passed to drawPictures from scrollTextAndPicts.  If it does
not, the loop returns for the next iteration.  If it does, the picture's handle is retrieved,
LoadResource checks whether the 'PICT' resource is in memory and, if necessary, loads it, HLock
locks the handle, DrawPicture draws the picture, and HUnlock unlocks the handle.  Before
DrawPicture is called, the clipping region is temporarily adjusted to equate to the rectangle
passed to drawPictures from scrollTextAndPicts so as to limit drawing to that rectangle.

doCloseHelp

doCloseHelp closes down the Help dialog.

The first two lines retrieve a handle to the dialog's document structure.  The next two lines
dispose of the region used to save the clipping region.  TEDispose disposes of the TextEdit
structure.  The next block disposes of any 'PICT' resources currently in memory, together with
the picture information structure.  Finally, the window's document structure is disposed of,
the window itself is disposed of, and the graphics port saved in doHelp is set as the current
port.

doSetBackgroundWhite

doSetBackgroundWhite sets the background colour to white and the background pattern to the
pattern white.

Demonstration Program DateTimeNumbers

// *******************************************************************************************
// DateTimeNumbers.c                                                        CARBON EVENT MODEL
// *******************************************************************************************
//
// This program, which opens a single modeless dialog, demonstrates the formatting and display
// of dates, times and numbers.
//
// The program utilises the following resources:
//
// o  A 'plst' resource.
//
// o  An 'MBAR' resource, and 'MENU' resources for Apple/Application, File, and Edit menus 
//    (preload,  non-purgeable).
//
// o  A 'DLOG' resource and associated 'dlgx', 'DITL', 'dfnt', and 'CNTL' resources 
//    (purgeable).  
//
// o  'hdlg' and 'STR#' resources (purgeable) for balloon help and help tags. 
//
// o  A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground, 
//    doesActivateOnFGSwitch, and isHighLevelEventAware flags set.
//
// *******************************************************************************************

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

#include <Carbon.h>
#include <string.h>

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

#define rMenubar                128
#define mAppleApplication       128
#define  iAbout                 1
#define mFile                   129
#define  iQuit                  12
#define mEdit                   130
#define  iCut                   3
#define  iCopy                  4
#define  iPaste                 5
#define  iClear                 6
#define rDialog                 128
#define  iStaticTextTodaysDate  2
#define  iStaticTextCurrentTime 4
#define  iEditTextTitle         10
#define  iEditTextQuantity      11
#define  iEditTextValue         12
#define  iEditTextDate          13
#define  iButtonEnter           18
#define  iButtonClear           19
#define  iStaticTextTitle       26
#define  iStaticTextQuantity    27
#define  iStaticTextUnitValue   28
#define  iStaticTextTotalValue  29
#define  iStaticTextDate        30
#define kReturn                 0x0D
#define kEnter                  0x03
#define kLeftArrow              0x1C
#define kRightArrow             0x1D
#define kUpArrow                0x1E
#define kDownArrow              0x1F
#define kBackspace              0x08
#define kDelete                 0x7F
#define topLeft(r)              (((Point *) &(r))[0])
#define botRight(r)             (((Point *) &(r))[1])

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

Boolean         gRunningOnX = false;
DialogRef       gDialogRef;
DateCacheRecord gDateCacheRec;
Boolean         gInBackground;

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

void                    main                (void);
void                    doPreliminaries     (void);
OSStatus                appEventHandler     (EventHandlerCallRef,EventRef,void *);
OSStatus                windowEventHandler  (EventHandlerCallRef,EventRef,void *);
void                    doIdle              (void);
void                    doMenuChoice        (MenuID,MenuItemIndex);
void                    doCopyPString       (Str255,Str255);
void                    doTodaysDate        (void);
void                    doAcceptNewRecord   (void);
void                    doUnitAndTotalValue (Str255,Str255);
void                    doDate              (Str255);
void                    doAdjustCursor      (WindowRef);
void                    doClearAllFields    (void);
ControlKeyFilterResult  numericFilter       (ControlRef,SInt16 *,SInt16 *,EventModifiers *);
void                    helpTags            (void);
  
// ************************************************************************************** main

void  main(void)
{
  MenuBarHandle       menubarHdl;
  SInt32              response;
  MenuRef             menuRef;
  ControlKeyFilterUPP numericFilterUPP;
  ControlRef          controlRef;
  EventTypeSpec   applicationEvents[] = { { kEventClassApplication, kEventAppActivated    },
                                          { kEventClassCommand,     kEventProcessCommand  },
                                          { kEventClassMouse,       kEventMouseMoved      } };
  EventTypeSpec   windowEvents[]      = { { kEventClassWindow,   kEventWindowDrawContent  },
                                          { kEventClassWindow,   kEventWindowActivated    },
                                          { kEventClassWindow,   kEventWindowClose        },
                                          { kEventClassMouse,    kEventMouseDown          },
                                          { kEventClassKeyboard, kEventRawKeyDown         } };

  // ........................................................................ do preliminaries

  doPreliminaries();

  // ............................................................... set up menu bar and menus

  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    ExitToShell();
  SetMenuBar(menubarHdl);
  DrawMenuBar();

  Gestalt(gestaltMenuMgrAttr,&response);
  if(response & gestaltMenuMgrAquaLayoutMask)
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
    {
      DeleteMenuItem(menuRef,iQuit);
      DeleteMenuItem(menuRef,iQuit - 1);
      DisableMenuItem(menuRef,0);
    }

    gRunningOnX = true;
  }
  else
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
      SetMenuItemCommandID(menuRef,iQuit,kHICommandQuit);
  }

   // ........................................... install application event handler and a timer
  
  InstallApplicationEventHandler(NewEventHandlerUPP((EventHandlerProcPtr) appEventHandler),
                                 GetEventTypeCount(applicationEvents),applicationEvents,
                                 0,NULL);

  InstallEventLoopTimer(GetCurrentEventLoop(),0,TicksToEventTime(GetCaretTime()),
                        NewEventLoopTimerUPP((EventLoopTimerProcPtr) doIdle),NULL,NULL);

  // ............................ open modeless dialog, change attributes, and install handler

  if(!(gDialogRef = GetNewDialog(rDialog,NULL,(WindowRef) -1)))
    ExitToShell();

  ChangeWindowAttributes(GetDialogWindow(gDialogRef),kWindowStandardHandlerAttribute |
                                                      kWindowCloseBoxAttribute,
                                                      kWindowCollapseBoxAttribute);

  InstallWindowEventHandler(GetDialogWindow(gDialogRef),
                            NewEventHandlerUPP((EventHandlerProcPtr) windowEventHandler),
                            GetEventTypeCount(windowEvents),windowEvents,0,NULL);

  // .... create universal procedure pointers for key filter, attach to two edit text controls

  numericFilterUPP = NewControlKeyFilterUPP((ControlKeyFilterProcPtr) numericFilter);

  GetDialogItemAsControl(gDialogRef,iEditTextQuantity,&controlRef);
  SetControlData(controlRef,kControlEntireControl,kControlEditTextKeyFilterTag,
                 sizeof(numericFilterUPP),&numericFilterUPP);

  GetDialogItemAsControl(gDialogRef,iEditTextValue,&controlRef);
  SetControlData(controlRef,kControlEntireControl,kControlEditTextKeyFilterTag,
                 sizeof(numericFilterUPP),&numericFilterUPP);

  // ............................. set help tags, get and display today's date and show window

  if(gRunningOnX)
    helpTags();

  doTodaysDate();

  ShowWindow(GetDialogWindow(gDialogRef));

  // ................................ display today's date and initialise date cache structure

  InitDateCache(&gDateCacheRec);

  // .............................................................. run application event loop

  RunApplicationEventLoop();
}

// *************************************************************************** doPreliminaries

void  doPreliminaries(void)
{
  MoreMasterPointers(64);
  InitCursor();
}

// *************************************************************************** appEventHandler

OSStatus  appEventHandler(EventHandlerCallRef eventHandlerCallRef,EventRef eventRef,
                          void * userData)
{
  OSStatus      result = eventNotHandledErr;
  UInt32        eventClass;
  UInt32        eventKind;
  HICommand     hiCommand;
  MenuID        menuID;
  MenuItemIndex menuItem;

  eventClass = GetEventClass(eventRef);
  eventKind  = GetEventKind(eventRef);

  switch(eventClass)
  {
    case kEventClassApplication:
      if(eventKind == kEventAppActivated)
        SetThemeCursor(kThemeArrowCursor);
      break;

    case kEventClassCommand:
      if(eventKind == kEventProcessCommand)
      {
        GetEventParameter(eventRef,kEventParamDirectObject,typeHICommand,NULL,
                          sizeof(HICommand),NULL,&hiCommand);
        menuID = GetMenuID(hiCommand.menu.menuRef);
        menuItem = hiCommand.menu.menuItemIndex;
        if((hiCommand.commandID != kHICommandQuit) && 
           (menuID >= mAppleApplication && menuID <= mEdit))
        {
          doMenuChoice(menuID,menuItem);
          result = noErr;
        }
      }
      break;

    case kEventClassMouse:
      if(eventKind == kEventMouseMoved)
      {
        doAdjustCursor(GetDialogWindow(gDialogRef));
        result = noErr;
      }
      break;
  }

  return result;
}

// ************************************************************************ windowEventHandler

OSStatus  windowEventHandler(EventHandlerCallRef eventHandlerCallRef,EventRef eventRef,
                             void* userData)
{
  OSStatus    result = eventNotHandledErr;
  UInt32      eventClass;
  UInt32      eventKind;
  EventRecord eventRecord;
  SInt16      itemHit;
  SInt8       charCode;
  ControlRef  controlRef;
  UInt32      finalTicks;

  eventClass = GetEventClass(eventRef);
  eventKind  = GetEventKind(eventRef);

  switch(eventClass)
  {
    case kEventClassWindow:                                              // event class window
      ConvertEventRefToEventRecord(eventRef,&eventRecord);
      switch(eventKind)
      {
        case kEventWindowActivated:
          DialogSelect(&eventRecord,&gDialogRef,&itemHit);
          result = noErr;
          break;

        case kEventWindowClose:
          QuitApplicationEventLoop();
          result = noErr;
          break;
      }

    case kEventClassMouse:                                                // event class mouse
      ConvertEventRefToEventRecord(eventRef,&eventRecord);
      switch(eventKind)
      {
        case kEventMouseDown:
          if(IsDialogEvent(&eventRecord))
          {
            if(DialogSelect(&eventRecord,&gDialogRef,&itemHit))
            {
              if(itemHit == iButtonEnter)
              {
                doAcceptNewRecord();
                doClearAllFields();
              }
              else if(itemHit == iButtonClear)
                doClearAllFields();
            }
          }
          doAdjustCursor(GetDialogWindow(gDialogRef));
          break;
      }
      break;

    case kEventClassKeyboard:                                          // event class keyboard
      switch(eventKind)
      {
        case kEventRawKeyDown:
          ConvertEventRefToEventRecord(eventRef,&eventRecord);
          GetEventParameter(eventRef,kEventParamKeyMacCharCodes,typeChar,NULL,
                            sizeof(charCode),NULL,&charCode);
          if((charCode == kReturn) || (charCode == kEnter))
          {
            GetDialogItemAsControl(gDialogRef,iButtonEnter,&controlRef);
            HiliteControl(controlRef,kControlButtonPart);
            Delay(8,&finalTicks);
            HiliteControl(controlRef,kControlEntireControl);
            doAcceptNewRecord();
            doClearAllFields();
            return noErr;
          }
          break;
      }
      break;
  }

  return result;
}

// ************************************************************************************ doIdle

void  doIdle(void)
{
  UInt32        rawSeconds;
  static UInt32 oldRawSeconds;
  Str255        timeString;
  ControlRef    controlRef;

  if(gRunningOnX)
    IdleControls(GetDialogWindow(gDialogRef));

  GetDateTime(&rawSeconds);

  if(rawSeconds > oldRawSeconds) 
  {
    TimeString(rawSeconds,true,timeString,NULL);

    GetDialogItemAsControl(gDialogRef,iStaticTextCurrentTime,&controlRef);
    SetControlData(controlRef,kControlEntireControl,kControlStaticTextTextTag,timeString[0],
                   &timeString[1]);
    Draw1Control(controlRef);

    oldRawSeconds = rawSeconds;
  }
}

// ****************************************************************************** doMenuChoice

void  doMenuChoice(MenuID menuID,MenuItemIndex menuItem)
{
  if(menuID == 0)
    return;

  switch(menuID)
  {
    case mAppleApplication:
      if(menuItem == iAbout)
        SysBeep(10);
      break;

    case mEdit:
      switch(menuItem)
      {
        case iCut:
          DialogCut(gDialogRef);
          break;

        case iCopy:
          DialogCopy(gDialogRef);
          break;

        case iPaste:
          DialogPaste(gDialogRef);
          break;

        case iClear:
          DialogDelete(gDialogRef);
          break;
      }
      break;
  }
}

// ***************************************************************************** doCopyPString

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

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

// ****************************************************************************** doTodaysDate

void  doTodaysDate(void)
{
  UInt32     rawSeconds;
  Str255     dateString;
  ControlRef controlRef;

  GetDateTime(&rawSeconds);
  DateString(rawSeconds,longDate,dateString,NULL);

  GetDialogItemAsControl(gDialogRef,iStaticTextTodaysDate,&controlRef);
  SetControlData(controlRef,kControlEntireControl,kControlStaticTextTextTag,dateString[0],
                 &dateString[1]);
}

// ************************************************************************* doAcceptNewRecord

void  doAcceptNewRecord(void)
{
  SInt16     theType;
  Handle     theHandle;
  Rect       theRect;
  Str255     titleString, quantityString, valueString, dateString;
  ControlRef controlRef;

  GetDialogItem(gDialogRef,iEditTextTitle,&theType,&theHandle,&theRect);
  GetDialogItemText(theHandle,titleString);

  GetDialogItem(gDialogRef,iEditTextQuantity,&theType,&theHandle,&theRect);
  GetDialogItemText(theHandle,quantityString);

  GetDialogItem(gDialogRef,iEditTextValue,&theType,&theHandle,&theRect);
  GetDialogItemText(theHandle,valueString);

  GetDialogItem(gDialogRef,iEditTextDate,&theType,&theHandle,&theRect);
  GetDialogItemText(theHandle,dateString);

  if(titleString[0] == 0 || quantityString[0] == 0 || valueString[0] == 0 || 
     dateString[0] == 0)
  {
    SysBeep(10);
    return;
  }

  GetDialogItemAsControl(gDialogRef,iStaticTextTitle,&controlRef);
  SetControlData(controlRef,kControlEntireControl,kControlStaticTextTextTag,titleString[0],
                 &titleString[1]);
  Draw1Control(controlRef);

  GetDialogItemAsControl(gDialogRef,iStaticTextQuantity,&controlRef);
  SetControlData(controlRef,kControlEntireControl,kControlStaticTextTextTag,quantityString[0],
                 &quantityString[1]);
  Draw1Control(controlRef);

  doUnitAndTotalValue(valueString,quantityString);

  doDate(dateString);
}

// *********************************************************************** doUnitAndTotalValue

void  doUnitAndTotalValue(Str255 valueString, Str255 quantityString)
{
  Handle           itl4ResourceHdl;
  SInt32           numpartsOffset;
  SInt32           numpartsLength;
  NumberParts      *numpartsTablePtr;
  Str255           formatString = "\p'$'###,###,###.00;'Valueless';'Valueless'";
  NumFormatString  formatStringRec;
  Str255           formattedNumString;
  extended80       value80Bit;
  SInt32           quantity;
  double           valueDouble;
  FormatResultType result;
  ControlRef       controlRef;

  GetIntlResourceTable(smSystemScript,iuNumberPartsTable,&itl4ResourceHdl,&numpartsOffset,
                       &numpartsLength);
  numpartsTablePtr = (NumberPartsPtr) ((SInt32) *itl4ResourceHdl + numpartsOffset);

  StringToFormatRec(formatString,numpartsTablePtr,&formatStringRec);

  StringToExtended(valueString,&formatStringRec,numpartsTablePtr,&value80Bit);
  ExtendedToString(&value80Bit,&formatStringRec,numpartsTablePtr,formattedNumString);

  GetDialogItemAsControl(gDialogRef,iStaticTextUnitValue,&controlRef);
  SetControlData(controlRef,kControlEntireControl,kControlStaticTextTextTag,
                 formattedNumString[0],&formattedNumString[1]);
  Draw1Control(controlRef);

  StringToNum(quantityString,&quantity);

  valueDouble = x80tod(&value80Bit);
  valueDouble = valueDouble * quantity;
  dtox80(&valueDouble,&value80Bit);

  result = ExtendedToString(&value80Bit,&formatStringRec,numpartsTablePtr,
                            formattedNumString);

  if(result == fFormatOverflow)
    doCopyPString("\p(Too large to display)",formattedNumString);

  GetDialogItemAsControl(gDialogRef,iStaticTextTotalValue,&controlRef);
  SetControlData(controlRef,kControlEntireControl,kControlStaticTextTextTag,
                 formattedNumString[0],&formattedNumString[1]);
  Draw1Control(controlRef);
}

// ************************************************************************************ doDate

void  doDate(Str255 dateString)
{
  SInt32       lengthUsed;
  LongDateRec  longDateTimeRec;
  LongDateTime longDateTimeValue;
  ControlRef   controlRef;

  longDateTimeRec.ld.hour = 0;
  longDateTimeRec.ld.minute = 0;
  longDateTimeRec.ld.second = 0;

  StringToDate((Ptr) dateString + 1,dateString[0],&gDateCacheRec,&lengthUsed,
               &longDateTimeRec);

  LongDateToSeconds(&longDateTimeRec,&longDateTimeValue);
  LongDateString(&longDateTimeValue,longDate,dateString,NULL);

  GetDialogItemAsControl(gDialogRef,iStaticTextDate,&controlRef);
  SetControlData(controlRef,kControlEntireControl,kControlStaticTextTextTag,dateString[0],
                 &dateString[1]);
  Draw1Control(controlRef);
}

// **************************************************************************** doAdjustCursor

void  doAdjustCursor(WindowRef windowRef)
{
  GrafPtr    oldPort;
  RgnHandle  arrowRegion,iBeamRegion;
  ControlRef controlRef;
  Rect       iBeamRect;
  Point      mouseLocation;

  GetPort(&oldPort);
  SetPortWindowPort(windowRef);

  arrowRegion = NewRgn();
  iBeamRegion = NewRgn();

  SetRectRgn(arrowRegion,-32768,-32768,32767,32767);
  
  GetKeyboardFocus(windowRef,&controlRef);
  GetControlBounds(controlRef,&iBeamRect);

  LocalToGlobal(&topLeft(iBeamRect));
  LocalToGlobal(&botRight(iBeamRect));  

  RectRgn(iBeamRegion,&iBeamRect);
  DiffRgn(arrowRegion,iBeamRegion,arrowRegion);

  GetMouse(&mouseLocation);
  LocalToGlobal(&mouseLocation);

  if(PtInRgn(mouseLocation,iBeamRegion))
    SetThemeCursor(kThemeIBeamCursor);
  else
    SetThemeCursor(kThemeArrowCursor);

  DisposeRgn(arrowRegion);
  DisposeRgn(iBeamRegion);

  SetPort(oldPort);
}

// ************************************************************************** doClearAllFields

void  doClearAllFields(void)
{
  SInt16     a;
  ControlRef controlRef;
  Str255     theString = "\p";

  for(a = iEditTextTitle;a <= iEditTextDate;a++)
  {
    GetDialogItemAsControl(gDialogRef,a,&controlRef);
    SetControlData(controlRef,kControlEntireControl,kControlEditTextTextTag,theString[0],
                 &theString[1]);
    Draw1Control(controlRef);

    if(a == iEditTextTitle)
      SetKeyboardFocus(GetDialogWindow(gDialogRef),controlRef,kControlFocusNextPart);
  }
}

// ***************************************************************************** numericFilter

ControlKeyFilterResult  numericFilter(ControlRef controlRef,SInt16* keyCode,SInt16 *charCode,
                                      EventModifiers *modifiers)
{
  if(((char) *charCode >= '0') && ((char) *charCode <= '9') || (char) *charCode == '.' ||
     (BitTst(modifiers,15 - cmdKeyBit)))
  {
    return kControlKeyFilterPassKey;
  }

  switch(*charCode)
  {
    case kLeftArrow:
    case kRightArrow:
    case kUpArrow:
    case kDownArrow:
    case kBackspace:
    case kDelete:
      return kControlKeyFilterPassKey;
      break;
  }

  SysBeep(10);
  return kControlKeyFilterBlockKey;
}

// ********************************************************************************** helpTags

void  helpTags(void)
{
  HMHelpContentRec helpContent;
  SInt16           a;
  static SInt16    itemNumber[7] = { 1,3,21,22,23,24,25 };
  ControlRef       controlRef;

  HMSetTagDelay(5);
  HMSetHelpTagsDisplayed(true);
  helpContent.version = kMacHelpVersion;
  helpContent.tagSide = kHMOutsideTopCenterAligned;

  helpContent.content[kHMMinimumContentIndex].contentType = kHMStringResContent;
  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmResID = 128;

  for(a = 1;a <= 7; a++)
  {
    helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = a;
    GetDialogItemAsControl(gDialogRef,itemNumber[a - 1],&controlRef);
    HMSetControlHelpContent(controlRef,&helpContent);
  }
}

// *******************************************************************************************

Demonstration Program DateTimeNumbers Comments

When this program is run, the user should enter data in the four edit text controls, using
the tab key or mouse clicks to select the required control and pressing the Return key or
clicking the Enter Record button when data has been entered in all controls.  Note that numeric
filters are used in the Quantity and Value edit text controls.

In order to observe number formatting effects, the user should occasionally enter very large
numbers and negative numbers in the Value field.  In order to observe the effects of date
string parsing and formatting, the user should enter dates in a variety of formats, for
example: "2 Mar 95", "2/3/95", "March 2 1995", "2 3 95", etc.

Global Variables

gDateCacheRec is used within the function doDate.

main

The call to InstallEventLoopTimer installs a timer which will fire repeatedly at the
interval returned by the call to GetCaretTime.  When the timer fires, the function doIdle is
called.  In addition to calling IdleControls, doIdle updates the current time displayed in a
static text control in the top of the dialog.

doTadaysDate is called to get the date and set it in a static text control at the top of the
dialog.

In the function doDate, the function that creates the long date-time structure takes an
initialised date cache structure as a parameter.  The call to InitDateCache initialises a date
cache structure.

windowEventHandler

Note that all events are passed to DialogSelect.

When the kEventMouseDown event is received, if the Enter Record push button was hit, the
function doAcceptNewRecord is called, following which doClearAllFields is called to clear all
of the edit text controls.  The same occurs when the kEventRawKeyDown event is received if the
key pressed was Return or Enter.

doIdle

doIdle, which is called when the timer fires, blinks the insertion point caret and sets
the current time in the static text control at top-right in the dialog.

If the program is running on Mac OS 8/9, IdleControls is called to ensure that the caret blinks
regularly in the edit text control with current keyboard focus.  (On Mac OS X, these controls
have their own in-built timers.)

GetDateTime retrieves the "raw" seconds value, as known to the system.  (This is the number of
seconds since 1 Jan 1904.)  If that value is greater than the value retrieved the last time
doIdle was called, TimeString converts the raw seconds value to a string containing the time
formatted according to flags in the numeric format ('itl0') resource.  (Since NULL is specified
in the resource handle parameter, the appropriate 'itl0' resource for the current script system
is used.)  This string is then set in the static text control, following which Draw1Control is 
called to redraw the control.  The retrieved raw seconds value is assigned to the static
variable oldRawSeconds for use next time doIdle is called.

doTodaysDate

doTodaysDate sets the date in the static text control at top-left of the dialog.

GetDateTime gets the raw seconds value, as known to the system.  DateString converts the raw
seconds value to a string containing a date formatted in long date format according to flags in
the numeric format ('itl0') resource.  (Since NULL is specified in the resource handle
parameter, the appropriate 'itl0' resource for the current script system is used.)  This string
is then set in the static text control.

doAcceptNewRecord

doAcceptNewRecord is called when the Return or Enter key is pressed, or when the Enter
Record button is clicked.  Assuming each edit text control contains at least one character of
text, it calls other functions to format (where necessary) and display strings in the "Last
Record Entered" group box area.

The calls to GetDialogItem get the handle in the hText field of each edit text control's
TextEdit structure, allowing the calls to GetDialogItemText to get the text into four local
variables of type Str255.

If the length of any of these strings is 0, the system alert sound is played and
doAcceptNewRecord returns.

The text from the Item Title and Quantity edit text controls are set in the relevant static
text controls within the Last Record Entered group box, and DrawIControl is called to draw
those controls.  doUnitAndTotalValue and doDate are then called.

doUnitAndTotalValue

doUnitAndTotalValue is called by doAcceptNewRecord to convert the string from the Value
edit text control to a floating point number, convert that number to a formatted number string,
set that string in the relevant static text control, convert the string from the Quantity edit
text control to an integer, multiply the floating point number by the integer to arrive at the
"Total Value" value, convert the result to a formatted number string, and set that string in
the relevant static text control.

A pointer to a number parts table is required by the functions that convert between floating
point numbers and strings.  Accordingly, the first three lines get the required pointer.

StringToFormatRec converts the number format specification string into the internal numeric
representation required by the functions that convert between floating point numbers and
strings.

StringToExtended converts the received Value string into a floating point number of type
extended (80 bits).  ExtendedToString converts that number back to a string, formatted
according to the internal numeric representation of the number format specification string. 
That string is then set in the relevant static text control and Draw1Control is called to draw
that control.

The intention now is to multiply the quantity by the unit value to arrive at a total value. 
The string received in the quantityString formal parameter is converted to an integer value of
type SInt32 by StringToNum.  The extended80 value is converted to a value of type double before
the multiplication occurs.  The result of the multiplication is assigned to the variable of
type double.  This is then converted to an extended80.

The extended80 value is then passed in the first parameter of ExtendedToString for conversion
to a formatted string.  If ExtendedToString does not return fFormatOverflow, the formatted
string is set in the relevant static text control and Draw1Control is called to draw that
control.

doDate

doDate is called by doAcceptNewRecord to create a long date-time structure from the string
in the "Date" edit text control, format the date as a string (long date format), and set that
string in the relevant static text control.

A pointer to the string containing the date as entered by the user, and the length of that
string, are passed in the call to StringToDate.  StringToDate parses the input string and fills
in the relevant fields of the long date-time structure.

The function StringToDate fills in only the year, month, day, and day of the week fields of a long date-time structure.  The function StringToTime fills in the hour, minute, and second.  If you do not call StringToTime, as is the case here, you need to zero the time-related fields of the long date-time structure.  If this is not done, the call to LongDateToSeconds will return an erroneous value.  (LongDateToSeconds always assumes that all the fields of the long date-time structure passed to it are valid.)

LongDateToSeconds converts the long date-time structure to a long date-time value.  The long
date-time value is then passed as a parameter to LongDateString, which converts the long
date-time value to a long format date string formatted according to the specified international
resource.  (In this case, NULL is passed as the international resource parameter, meaning that
the appropriate 'itl1' resource for the current script system is used.)

The formatted date string is then set in the relevant static text control and Draw1Control is
called to draw that control.
 

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

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
Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Apple’s standard one-year warranty... Read more

Jobs Board

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
IT Systems Engineer ( *Apple* Platforms) - S...
IT Systems Engineer ( Apple Platforms) at SpaceX Hawthorne, CA SpaceX was founded under the belief that a future where humanity is out exploring the stars is Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.