home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / human interface toolbox / packagetool / sample package / htmlsample sources / htmlsample.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  26.0 KB  |  803 lines

  1. /*
  2.     File: HTMLSample.c
  3.     
  4.     Description:
  5.         This file contains the main application program for the HTMLSample.
  6.     Routines in this file are responsible for handling events directed
  7.     at the application.
  8.     
  9.     HTMLSample is an application illustrating how to use the new
  10.     HTMLRenderingLib services found in Mac OS 9. HTMLRenderingLib
  11.     is Apple's light-weight HTML rendering engine capable of
  12.     displaying HTML files.
  13.  
  14.     Copyright:
  15.         © Copyright 1999 Apple Computer, Inc. All rights reserved.
  16.     
  17.     Disclaimer:
  18.         IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  19.         ("Apple") in consideration of your agreement to the following terms, and your
  20.         use, installation, modification or redistribution of this Apple software
  21.         constitutes acceptance of these terms.  If you do not agree with these terms,
  22.         please do not use, install, modify or redistribute this Apple software.
  23.  
  24.         In consideration of your agreement to abide by the following terms, and subject
  25.         to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
  26.         copyrights in this original Apple software (the "Apple Software"), to use,
  27.         reproduce, modify and redistribute the Apple Software, with or without
  28.         modifications, in source and/or binary forms; provided that if you redistribute
  29.         the Apple Software in its entirety and without modifications, you must retain
  30.         this notice and the following text and disclaimers in all such redistributions of
  31.         the Apple Software.  Neither the name, trademarks, service marks or logos of
  32.         Apple Computer, Inc. may be used to endorse or promote products derived from the
  33.         Apple Software without specific prior written permission from Apple.  Except as
  34.         expressly stated in this notice, no other rights or licenses, express or implied,
  35.         are granted by Apple herein, including but not limited to any patent rights that
  36.         may be infringed by your derivative works or by other works in which the Apple
  37.         Software may be incorporated.
  38.  
  39.         The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
  40.         WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  41.         WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  42.         PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  43.         COMBINATION WITH YOUR PRODUCTS.
  44.  
  45.         IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  46.         CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  47.         GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  48.         ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
  49.         OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
  50.         (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
  51.         ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  52.  
  53.     Change History (most recent first):
  54.         Wed, Dec 22, 1999 -- created
  55. */
  56.  
  57.  
  58. #include "HTMLSample.h"
  59.  
  60. #include <Menus.h>
  61. #include <Windows.h>
  62. #include <Dialogs.h>
  63. #include <Events.h>
  64. #include <Fonts.h>
  65. #include <SegLoad.h>
  66. #include <Resources.h>
  67. #include <Balloons.h>
  68. #include <Devices.h>
  69. #include <AppleEvents.h>
  70. #include <StdIO.h>
  71. #include <StdArg.h>
  72. #include <string.h>
  73. #include <ToolUtils.h>
  74. #include <Appearance.h>
  75. #include <Navigation.h>
  76. #include <StandardFile.h>
  77.  
  78.  
  79. #include <HTMLRendering.h>
  80.  
  81. #include "RenderingWindow.h"
  82. #include "SampleUtils.h"
  83. #include "AboutBox.h"
  84.  
  85.     /* true while the app is running */
  86. Boolean gRunning = true;
  87.  
  88. #ifndef __MWERKS__
  89. QDGlobals    qd; /* QuickDraw globals */
  90. #endif
  91.  
  92.  
  93.  
  94. /* OpenOneFile is called for each file the application is asked to open
  95.     either by way of Apple event or from the file menu.  spec points to
  96.     a file specification record referring to the file to open.  The file will
  97.     be opened in a new window. */
  98. static OSErr OpenOneFile(FSSpec *spec) {
  99.     Handle urlHandle, errorPageLink;
  100.     WindowPtr rWindow;
  101.     Str255 errStr;
  102.     OSErr err;
  103.         /* initial state */
  104.     urlHandle = NULL;
  105.     rWindow = NULL;
  106.         /* allocate locals */
  107.     urlHandle = NewHandle(0);
  108.     if (urlHandle == NULL) { err = memFullErr; goto bail; }
  109.     errorPageLink = GetResource(kCStyleStringResourceType, kErrorPageURLString);
  110.     if (errorPageLink == NULL)  { err = resNotFound; goto bail; }
  111.         /* convert the fsspec to a url */
  112.     err = HRUtilGetURLFromFSSpec(spec, urlHandle);
  113.     if (err != noErr) goto bail;
  114.         /* open the window */
  115.     err = RWOpen(&rWindow);
  116.     if (err != noErr) goto bail;
  117.         /* attempt to open the url */
  118.     MoveHHi(urlHandle);
  119.     HLock(urlHandle);
  120.     err = RWGotoURL(rWindow, *urlHandle, true);
  121.     HUnlock(urlHandle);
  122.         /* if that fails, try to show the error page */
  123.     if (err != noErr) {
  124.         MoveHHi(errorPageLink);
  125.         HLock(errorPageLink);
  126.         err = RWGotoAppRelLink(rWindow, *errorPageLink, true);
  127.         HUnlock(errorPageLink);
  128.         if (err != noErr) goto bail;
  129.     }
  130.         /* clean up and leave */
  131.     DisposeHandle(urlHandle);
  132.     return noErr;
  133. bail:
  134.         /* we display an alert here if there's an error. returning
  135.         an error from this routine will abort any open routine that
  136.         is going on--even if we're in the middle of a list of files. */
  137.     NumToString(err, errStr);
  138.     ParamAlert(kOpenFileErrorAlert, errStr, spec->name);
  139.     if (rWindow != NULL) RWCloseWindow(rWindow);
  140.     if (urlHandle != NULL) DisposeHandle(urlHandle);
  141.     return err;
  142. }
  143.  
  144. /* IdentifyPackage identifies a Mac OS 9 package and returns a reference
  145.     to it's main file inside of mainPackageFile.  In Mac OS 9, packages are
  146.     the special folders that have their bundle bits set and contain an alias
  147.     at their topmost level referring to a file somewhere in the package. */
  148. static Boolean IdentifyPackage(FSSpec *target, FSSpec *mainPackageFile) {
  149.     CInfoPBRec cat;
  150.     OSErr err;
  151.     long pDir;
  152.     Str255 name;
  153.     FSSpec aliasFile;
  154.     Boolean targetIsFolder, wasAliased;
  155.         /* check the target's flags */
  156.     cat.dirInfo.ioNamePtr = target->name;
  157.     cat.dirInfo.ioVRefNum = target->vRefNum;
  158.     cat.dirInfo.ioFDirIndex = 0;
  159.     cat.dirInfo.ioDrDirID = target->parID;
  160.     err = PBGetCatInfoSync(&cat);
  161.     if (err != noErr) return false;
  162.         /* if it's a folder and the bundle bit is set....*/
  163.     if (((cat.dirInfo.ioFlAttrib & 16) != 0) && ((cat.dirInfo.ioDrUsrWds.frFlags & kHasBundle) != 0)) {
  164.             /* search for a top level alias file.  Here, we enumerate all of the
  165.             objects in the directory until we find a file with the alias flag set. */
  166.         pDir = cat.dirInfo.ioDrDirID;
  167.         cat.dirInfo.ioNamePtr = name;
  168.         cat.dirInfo.ioVRefNum = target->vRefNum;
  169.         cat.dirInfo.ioFDirIndex = 1;
  170.         cat.dirInfo.ioDrDirID = pDir;
  171.         while (PBGetCatInfoSync(&cat) == noErr) {
  172.                 /* if the thing we're looking at is not a directory and it's alias flag is set,
  173.                 try to resolve it as an alias file. */
  174.             if (((cat.dirInfo.ioFlAttrib & 16) == 0) && ((cat.dirInfo.ioDrUsrWds.frFlags & kIsAlias) != 0)) {
  175.                 err = FSMakeFSSpec(target->vRefNum, pDir, name, &aliasFile);
  176.                 if (err != noErr) return false;
  177.                 err = ResolveAliasFile(&aliasFile, false, &targetIsFolder, &wasAliased);
  178.                 if (err != noErr) return false;
  179.                 if (mainPackageFile != NULL)
  180.                     *mainPackageFile = aliasFile;
  181.                 return true;
  182.             }
  183.                 /* move on to the next file in the directory. */
  184.             cat.dirInfo.ioFDirIndex++;
  185.             cat.dirInfo.ioDrDirID = pDir;
  186.         }
  187.     }
  188.         /* we found nothing matching our criteria, so we
  189.         fail. */
  190.     return false;
  191. }
  192.  
  193.  
  194. /* OpenTheDocuments is called to open a list of documents provided by
  195.     either an open documents Apple event or one of the Navigation services
  196.     dialogs.  */
  197. static OSErr OpenTheDocuments(AEDescList *theDocuments) {
  198.     OSErr err;
  199.     long i, n;
  200.     FSSpec fileSpec, packageSpec;
  201.     AEKeyword keyWd;
  202.     DescType typeCd;
  203.     Size actSz;
  204.     
  205.         /* open them */
  206.     err = AECountItems(theDocuments, &n);
  207.     if (err != noErr) goto bail;
  208.         /* and then open each one */
  209.     for (i = 1 ; i <= n; i++) {
  210.             /* get the i'th FSSpec record.  NOTE: implicity, we are calling
  211.             a coercion handler because this list actually contains alias records. 
  212.             In particular, the coercion handler converts them from alias records
  213.             into FSSpec records. */
  214.         err = AEGetNthPtr(theDocuments, i, typeFSS, &keyWd, &typeCd,
  215.             (Ptr) &fileSpec, sizeof(fileSpec), (actSz = sizeof(fileSpec), &actSz));
  216.         if (err != noErr) goto bail;
  217.             /* if it's a package, we'll open it's main file, otherwise
  218.             we'll open the file itself */
  219.         if (IdentifyPackage(&fileSpec, &packageSpec))
  220.             err = OpenOneFile(&packageSpec);
  221.         else err = OpenOneFile(&fileSpec);
  222.         if (err != noErr) goto bail;
  223.     }
  224.     return noErr;
  225. bail:
  226.     return err;
  227. }
  228.  
  229.  
  230. /* MyNavFilterProc This is the filter function we provide for the Navigation services
  231.     dialogs.  We only allow files of type TEXT. */
  232. static pascal Boolean MyNavFilterProc( AEDesc* theItem, void* info, NavCallBackUserData callBackUD, NavFilterModes filterMode) {
  233.     NavFileOrFolderInfo* theInfo;
  234.     if ( theItem->descriptorType == typeFSS ) {
  235.         theInfo = (NavFileOrFolderInfo*) info;
  236.         if ( theInfo->isFolder ) return true;
  237.         if ( theInfo->fileAndFolder.fileInfo.finderInfo.fdType != 'TEXT' )
  238.             return false;
  239.     }
  240.     return true;
  241. }
  242.  
  243.  
  244. /* NavEventCallBack is the event handling call back we provide for Navigation
  245.     services.  It's presence is important so our windows will be updated appropriately
  246.     when the navigation window is resized or moved. */
  247. static pascal void NavEventCallBack( NavEventCallbackMessage callBackSelector,
  248.             NavCBRecPtr callBackParms, NavCallBackUserData callBackUD) {
  249.     if (callBackSelector == kNavCBEvent && callBackParms->eventData.eventDataParms.event->what == updateEvt) {
  250.  
  251.         HandleEvent(callBackParms->eventData.eventDataParms.event);
  252.  
  253.     }
  254. }
  255.  
  256.  
  257. /* MyFileFilterProc is used by the older standard file calls.  We fall back to
  258.     standard file when navigation services is not present or unavailable.  In this
  259.     routine, we filter out all invisible files. */
  260. static pascal Boolean MyFileFilterProc(CInfoPBPtr pb) {
  261.         /* don't display invisible files */
  262.     return ((pb->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) != 0);
  263. }
  264.  
  265.  
  266. /* SelectAndOpenFile is the inner workings of the Open... command
  267.     when it is chosen from the file menu.  Here, we use the navigation
  268.     services dialogs when they are available, but if they're not, then we
  269.     use the standard file ones. */
  270. static OSStatus SelectAndOpenFile(void) {
  271.     NavDialogOptions dialogOptions;
  272.     NavReplyRecord theReply;
  273.     NavEventUPP eventf;
  274.     NavObjectFilterUPP filterf;
  275.     FileFilterUPP stdFilterf;
  276.     Boolean hasreply;
  277.     OSStatus err;
  278.         /* set up locals */
  279.     eventf = NULL;
  280.     filterf = NULL;
  281.     stdFilterf = NULL;
  282.     hasreply = false;
  283.     memset(&theReply, 0, sizeof(theReply));
  284.         /* if Navigation services is available, then we
  285.         use those calls. */
  286.     if (NavServicesAvailable()) {
  287.             /* allocate data */
  288.         filterf = NewNavObjectFilterUPP(MyNavFilterProc);
  289.         if (filterf == NULL) { err = memFullErr; goto bail; }
  290.         eventf = NewNavEventProc(NavEventCallBack);
  291.         if (eventf == NULL) { err = memFullErr; goto bail; }
  292.             /* set up dialog options */
  293.         err = NavGetDefaultDialogOptions(&dialogOptions);
  294.         if (err != noErr) goto bail;
  295.             /* NOTE: by setting the kNavAllowMultipleFiles flag, we make it possible
  296.             for the user to select more than one file.  And, setting the kNavSupportPackages
  297.             flag allows us to open package documents. */
  298.         dialogOptions.dialogOptionFlags = (kNavDontAutoTranslate | kNavAllowMultipleFiles | kNavSupportPackages);
  299.         GetIndString(dialogOptions.message, kMainStringList, kNavMessageString);
  300.             /* pick one or more files */
  301.         err = NavChooseFile(NULL, &theReply, &dialogOptions, eventf,  NULL,  filterf,  NULL, NULL);
  302.         if (err != noErr) goto bail;
  303.         if (!theReply.validRecord) { err = userCanceledErr; goto bail; }
  304.         hasreply = true;
  305.             /* if we have a valid reply, then call our
  306.             open documents routine. */
  307.         err = OpenTheDocuments(&theReply.selection);
  308.         if (err != noErr) goto bail;
  309.             /* clean up the structures we allocated */
  310.         NavDisposeReply(&theReply);
  311.         DisposeNavEventUPP(eventf);
  312.         DisposeNavObjectFilterUPP(filterf);
  313.     } else {
  314.         StandardFileReply reply;
  315.         SFTypeList typeList = { 'TEXT', 'TEXT', 'TEXT', 'TEXT' };
  316.             /* set up our locals */
  317.         stdFilterf = NewFileFilterUPP(MyFileFilterProc);
  318.         if (stdFilterf == NULL) { err = memFullErr; goto bail; }
  319.             /* ask for a file.  NOTE: with standard file
  320.             we can only get one file at a time. */
  321.         StandardGetFile(stdFilterf, 1, typeList, &reply);
  322.         if ( ! reply.sfGood) { err = userCanceledErr; goto bail; }
  323.             /* if a file was chosen, open it. */
  324.         err = OpenOneFile(&reply.sfFile);
  325.         if (err != noErr) goto bail;
  326.             /* clean up the structures we allocated */
  327.         DisposeFileFilterUPP(stdFilterf);
  328.     }
  329.     return noErr;
  330. bail:
  331.     if (hasreply) NavDisposeReply(&theReply);
  332.     if (eventf != NULL) DisposeNavEventUPP(eventf);
  333.     if (filterf != NULL) DisposeNavObjectFilterUPP(filterf);
  334.     if (stdFilterf != NULL) DisposeFileFilterUPP(stdFilterf);
  335.     return err;
  336. }
  337.  
  338.  
  339.  
  340.  
  341. /* ResetMenus is called immediately before all calls to
  342.     MenuSelect or MenuKey.  In this routine, we re-build
  343.     or enable the menus as appropriate depending on the
  344.     current environment */
  345. static void ResetMenus(void) {
  346.     WindowPtr target;
  347.     target = FrontWindow();
  348.         /* if the front most window is a rendering
  349.         window, then we let the window handle the
  350.         menu. */
  351.     if (IsARenderingWindow(target))
  352.         RWResetGotoMenu(target);
  353.     else {
  354.         MenuHandle goMenu;
  355.             /* otherwise, we clear the menu
  356.             and disable all of its commands. */
  357.         goMenu = GetMenuHandle(mGo);
  358.         DisableItem(goMenu, iBack);
  359.         DisableItem(goMenu, iForward);
  360.         DisableItem(goMenu, iHome);
  361.         while (CountMItems(goMenu) >= iGoSep)
  362.             DeleteMenuItem(goMenu, iGoSep);
  363.     }
  364. }
  365.  
  366.  
  367. /* DoMenuCommand is called in response to MenuKey
  368.     or MenuSelect.  Here, we dispatch the menu command
  369.     to its appropriate handler, or if it's a small action
  370.     we do it here. */
  371. static void DoMenuCommand(long rawMenuSelectResult) {
  372.     short menu, item;
  373.         /* decode the MenuSelect result */
  374.     menu = (rawMenuSelectResult >> 16);
  375.     if (menu == 0) return;
  376.     item = (rawMenuSelectResult & 0x0000FFFF);
  377.         /* dispatch on result */
  378.     switch (menu) {
  379.             /* apple menu commands */
  380.         case mApple:
  381.             if (item == iAbout) {
  382.                 WindowPtr aboutBox;
  383.                 OSStatus err;
  384.                     /* open the about box */
  385.                 err = OpenAboutBox(&aboutBox);
  386.                 if (err != noErr) {
  387.                     Str255 errStr;
  388.                     NumToString(err, errStr);
  389.                     ParamAlert(kNoAboutBoxErrorAlert, errStr, NULL);
  390.                 }
  391.             } else if (item >= iFirstAppleItem) {
  392.                     /* if the item selected is below the separator
  393.                     line, then we open it as a desk accessory. */
  394.                 Str255 deskAccName;
  395.                 GetMenuItemText(GetMenuHandle(mApple), item, deskAccName);
  396.                 OpenDeskAcc(deskAccName);
  397.             }
  398.             break;
  399.             
  400.                 /* file menu commands */
  401.         case mFile:
  402.             if (item == iOpen) {
  403.                 SelectAndOpenFile();
  404.             } else if (item == iQuit) {
  405.                 if (CloseRenderingWindows() == noErr)
  406.                     gRunning = false;
  407.             }
  408.             break;
  409.             
  410.                 /* selections in the Go menu are handled by
  411.                 the frontmost rendering window. */
  412.         case mGo:
  413.             {    WindowPtr target;
  414.                 target = FrontWindow();
  415.                 if (IsARenderingWindow(target))
  416.                     RWHandleGotoMenu(target, item);
  417.             }
  418.             break;
  419.  
  420.     }
  421.         /* unhilite the menu bar */
  422.     HiliteMenu(0);
  423. }
  424.  
  425.  
  426. /* QuitAppleEventHandler is our quit Apple event handler.  this routine
  427.     is called when a quit Apple event is sent to our application.  Here,
  428.     we set the gRunning flag to false. NOTE:  it is not appropriate to
  429.     call ExitToShell here.  Instead, by setting the flag to false we
  430.     fall through the bottom of our main loop the next time we're called. 
  431.     Here, if we are unable to close all of the rendering windows,
  432.     we return an error.  This will abort the shutdown process,
  433.     if that's why we were called.  */
  434. static pascal OSErr QuitAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) {
  435.     if (CloseRenderingWindows() == noErr) {
  436.         gRunning = false;
  437.         return noErr;
  438.     } else return userCanceledErr;
  439. }
  440.  
  441.  
  442. /* OpenAppleEventHandler is called when our application receives
  443.     an 'open application' apple event.  Here, we put up a window
  444.     referring to the default page. */
  445. static pascal OSErr OpenAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) {
  446.     WindowPtr rWin;
  447.     Handle urlHandle;
  448.     OSErr err;
  449.         /* creat a rendering window. */
  450.     err = RWOpen(&rWin);
  451.     if (err != noErr) return err;
  452.         /* get the link to the default page from the resource
  453.         file. */
  454.     urlHandle = GetResource(kCStyleStringResourceType, kDefaultPageURLString);
  455.     if (urlHandle == NULL) return memFullErr;
  456.         /* lock down the resource and point the rendering
  457.         window at it. */
  458.     MoveHHi(urlHandle);
  459.     HLock(urlHandle);
  460.     RWGotoAppRelLink(rWin, *urlHandle, true);
  461.     HUnlock(urlHandle);
  462.         /* done */
  463.     return noErr;
  464. }
  465.  
  466.  
  467. /* ReOpenAppleEventHandler is called whenever the application receives
  468.     a re-open Apple event.  This will happen if the application is already
  469.     running and the user attempts to open it again by either double clicking
  470.     on its icon in the Finder or by selecting its icon and choosing Open in
  471.     the Finder's file menu. Here, if there is no window showing, then we
  472.     open a new one as we would if an open application event was received. */
  473. static pascal OSErr ReOpenAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) {
  474.     
  475.     if (FrontWindow() == NULL)
  476.         return OpenAppleEventHandler(appleEvt, reply, refcon);
  477.     else return noErr;
  478.     
  479.     return noErr;
  480. }
  481.  
  482.  
  483. /* OpenDocumentsEventHandler is called whenever we receive an open documents
  484.     Apple event.  Here, we extract the list of documents from the event
  485.     and pass them along to our OpenTheDocuments routine. */
  486. static pascal OSErr OpenDocumentsEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) {
  487.     OSErr err;
  488.     AEDescList documents;
  489.     
  490.         /* initial state */
  491.     AECreateDesc(typeNull, NULL, 0, &documents);
  492.     
  493.         /* get the open parameter */
  494.     err = AEGetParamDesc(appleEvt, keyDirectObject, typeAEList, &documents);
  495.     if (err != noErr) goto bail;
  496.     
  497.         /* open the documents */
  498.     err = OpenTheDocuments(&documents);
  499.     if (err != noErr) goto bail;
  500.  
  501. bail:
  502.     AEDisposeDesc(&documents);
  503.     return err;
  504. }
  505.  
  506.  
  507.  
  508.  
  509.  
  510. /* HandleMouseDown is called for mouse down events.  Processing of
  511.     mouse down events in the HTML rendering area of windows is 
  512.     handled by the HTMLRenderinLib, but clicks in the controls and
  513.     other parts of the windows are handled here. */
  514. static void HandleMouseDown(EventRecord *ev) {
  515.     WindowPtr theWindow;
  516.     short partcode;
  517.     partcode = FindWindow(ev->where, &theWindow);
  518.     switch (partcode) {
  519.             /* inside the window's content area */
  520.         case inContent:
  521.             if (theWindow != FrontWindow()) {
  522.                     /* if it's not the frontmost window,
  523.                     then make it the frontmost window. */
  524.                 SelectWindow(theWindow);
  525.             } else {
  526.                     /* otherwise, if it's a rendering window,
  527.                     pass the click along to the window. */
  528.                 Point where;
  529.                 SetPort(theWindow);
  530.                 SetOrigin(0, 0);
  531.                 where = ev->where;
  532.                 GlobalToLocal(&where);
  533.                 if (IsARenderingWindow(theWindow))
  534.                     RWHandleMouseDown(theWindow, where);
  535.             }
  536.             break;
  537.             
  538.             /* menu bar clicks */
  539.         case inMenuBar:
  540.             ResetMenus();
  541.             DoMenuCommand(MenuSelect(ev->where));
  542.             break;
  543.             
  544.             /* track clicks in the close box */
  545.         case inGoAway:
  546.             if (TrackGoAway(theWindow, ev->where)) {
  547.                 if (IsARenderingWindow(theWindow))
  548.                     RWCloseWindow(theWindow);
  549.                 else if (IsAboutBox(theWindow))
  550.                     AboutBoxCloseWindow(theWindow);
  551.             }
  552.             break;
  553.             
  554.             /* allow window drags */
  555.         case inDrag:
  556.             {    Rect boundsRect = {0,0, 32000, 32000};
  557.                 DragWindow(theWindow, ev->where, &boundsRect);
  558.             }
  559.             break;
  560.             
  561.             /* allow window drags */
  562.         case inGrow:
  563.             {    Rect sizerect;
  564.                 long grow_result;
  565.                 SetRect(&sizerect, 300, 150, 32767, 32767);
  566.                 grow_result = GrowWindow(theWindow, ev->where, &sizerect);
  567.                 if (grow_result != 0) {
  568.                     SizeWindow(theWindow, LoWord(grow_result), HiWord(grow_result), true);
  569.                     SetPort(theWindow);
  570.                     InvalRect(&theWindow->portRect);
  571.                     if (IsARenderingWindow(theWindow))
  572.                         RWRecalculateSize(theWindow);
  573.  
  574.                 }
  575.             }
  576.             break;
  577.             
  578.             /* zoom box clicks.  NOTE:  since the rendering window
  579.             always sets the standard rectangle to the 'best size' for
  580.             displaying the current HTML window, the inZoomOut partcode
  581.             will zoom the window to that size rather than the entire screen.*/
  582.         case inZoomIn:
  583.         case inZoomOut:
  584.             SetPort(theWindow);
  585.             EraseRect(&theWindow->portRect);
  586.             ZoomWindow(theWindow, partcode, true);
  587.             SetPort(theWindow);
  588.             if (IsARenderingWindow(theWindow))
  589.                 RWRecalculateSize(theWindow);
  590.             break;
  591.             
  592.             /* desktop clicks, etc... */
  593.         case inSysWindow:
  594.             SystemClick(ev, theWindow);
  595.             break;
  596.     }
  597. }
  598.  
  599.  
  600. /* HandleEvent is the main event handling routine for the
  601.     application.  ev points to an event record returned by
  602.     WaitNextEvent. */
  603. void HandleEvent(EventRecord *ev) {
  604.     WindowPtr target;
  605.         /* process menu key events */
  606.     if (((ev->what == keyDown) || (ev->what == autoKey)) && ((ev->modifiers & cmdKey) != 0)) {
  607.         ResetMenus();
  608.         DoMenuCommand(MenuKey((char) (ev->message & charCodeMask)));
  609.         ev->what = nullEvent;
  610.     }
  611.     
  612.         /* process HR events.  NOTE: this call handles most of the events
  613.         for the active HTML rendering object.  But, be careful it may set
  614.         the clip region and origin of that window to an unknown state. */
  615.     if (HRIsHREvent(ev))
  616.         ev->what = nullEvent;
  617.     
  618.         /* process other event types */
  619.     switch (ev->what) {
  620.             /* the application may be switching in to the forground
  621.             or into the background.  Either way, we need to activate
  622.             the frontmost window accordingly. */
  623.         case osEvt:
  624.             target = FrontWindow();
  625.             if (IsARenderingWindow(target))
  626.                 RWActivate(target, ((ev->message & resumeFlag) != 0));
  627.             else if (IsAboutBox(target))
  628.                 AboutBoxActivate(target, ((ev->message & resumeFlag) != 0));
  629.             break;
  630.             
  631.             /* for activate events we call the window's activate event
  632.             handler. */
  633.         case activateEvt:
  634.             target = (WindowPtr) ev->message;
  635.             if (IsARenderingWindow(target))
  636.                 RWActivate(target, ((ev->modifiers&1) != 0));
  637.             else if (IsAboutBox(target))
  638.                 AboutBoxActivate(target, ((ev->modifiers&1) != 0));
  639.             break;
  640.             
  641.             /* for update events we call the window's update event
  642.             handler. if the window is of an unknown type, we ignore the
  643.             event. */
  644.         case updateEvt:
  645.             target = (WindowPtr) ev->message;
  646.             if (IsARenderingWindow(target))
  647.                 RWUpdate(target);
  648.             else if (IsAboutBox(target))
  649.                 AboutBoxUpdate(target);
  650.             else {
  651.                 BeginUpdate(target);
  652.                 EndUpdate(target);
  653.             }
  654.             break;    
  655.             
  656.             /* for mouse events we call the the HandleMouseDown routine
  657.             defined above. */
  658.         case mouseDown:
  659.             HandleMouseDown(ev);
  660.             break;
  661.         
  662.             /* for key down events we call the window's key down event
  663.             handler. */
  664.         case keyDown:
  665.         case autoKey:
  666.             target = FrontWindow();
  667.             if (IsARenderingWindow(target))
  668.                 RWKeyDown(target, (char) (ev->message & charCodeMask));
  669.             break;
  670.         
  671.             /* Apple events. */
  672.         case kHighLevelEvent:
  673.             AEProcessAppleEvent(ev);
  674.             break;
  675.     }
  676. }
  677.  
  678.  
  679. /* FDPIdleProcedure is the idle procedure called by AEInteractWithUser while we are waiting
  680.     for the application to be pulled into the forground.  It simply passes the event along
  681.     to HandleNextEvent */
  682. static pascal Boolean MyIdleInteractProc(EventRecord *theEvent, long *sleepTime, RgnHandle *mouseRgn) {
  683.     HandleEvent(theEvent);
  684.     return ( ! gRunning ); /* quit waiting if we're not running */
  685. }
  686.  
  687.  
  688. /* ParamAlert is a general alert handling routine.  If Apple events exist, then it
  689.     calls AEInteractWithUser to ensure the application is in the forground, and then
  690.     it displays an alert after passing the s1 and s2 parameters to ParamText. */
  691. short ParamAlert(short alertID, StringPtr s1, StringPtr s2) {
  692.     AEIdleUPP aeIdleProc;
  693.     OSStatus err;
  694.     aeIdleProc = NewAEIdleUPP(MyIdleInteractProc);
  695.     if (aeIdleProc == NULL) { err = memFullErr; goto bail; }
  696.     err = AEInteractWithUser(kNoTimeOut, NULL, aeIdleProc);
  697.     if (err != noErr) goto bail;
  698.     ParamText(s1, s2, NULL, NULL);
  699.     err = Alert(alertID, NULL);
  700.     DisposeAEIdleUPP(aeIdleProc);
  701.     return err;
  702. bail:
  703.     if (aeIdleProc != NULL) DisposeAEIdleUPP(aeIdleProc);
  704.     return err;
  705. }
  706.  
  707.  
  708. /* MyGrowZone is called by the Memory Manager whenever it
  709.     cannot fulfil a memory request.  Here, we try to get back
  710.     enough memory for the request by asking the HTML rendering
  711.     library to free up some cache space. */
  712. static pascal long MyGrowZone(Size cbNeeded) {
  713.     return HRFreeMemory(cbNeeded);
  714. }
  715.  
  716.  
  717.  
  718.     /* the main program */
  719.  
  720. int main(void) {
  721.     OSStatus err;
  722.     Boolean isAppearanceClient;
  723.     Str255 errStr;
  724.     
  725.     isAppearanceClient = false;
  726.     
  727.         /* set up the macintosh managers */
  728.     SetApplLimit(GetApplLimit());
  729.     MaxApplZone();
  730.     InitGraf(&qd.thePort);
  731.     InitFonts();
  732.     InitWindows();
  733.     TEInit();
  734.     InitMenus();
  735.     InitDialogs(0);
  736.     FlushEvents(everyEvent, 0);
  737.     InitCursor();
  738.     
  739.         /* register ourselves with the appearance manager. */
  740.     err = RegisterAppearanceClient();
  741.     if (err != noErr) goto bail;
  742.     isAppearanceClient = true;
  743.         
  744.         /* install our event handlers */
  745.     err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, NewAEEventHandlerProc(OpenAppleEventHandler), 0, false);
  746.     if (err != noErr) goto bail;
  747.     err = AEInstallEventHandler(kCoreEventClass, 'rapp', NewAEEventHandlerProc(ReOpenAppleEventHandler), 0, false);
  748.     if (err != noErr) goto bail;
  749.     err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerProc(OpenDocumentsEventHandler), 0, false);
  750.     if (err != noErr) goto bail;
  751.     err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, NewAEEventHandlerProc(QuitAppleEventHandler), 0, false);
  752.     if (err != noErr) goto bail;
  753.  
  754.         /* set up the menu bar */
  755.     SetMenuBar(GetNewMBar(kMenuBarID));
  756.     DrawMenuBar();
  757.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');
  758.  
  759.         /* set up the rendering library */
  760.     if ( ! HRHTMLRenderingLibAvailable()) {
  761.         ParamAlert(kNoRenderingLibErrorAlert, NULL, NULL);
  762.         err = userCanceledErr;
  763.         goto bail;
  764.     }
  765.     
  766.         /* install our memory manger grow zone
  767.         routine. */
  768.     SetGrowZone(NewGrowZoneUPP(MyGrowZone));
  769.     
  770.         /* initialize the rendering windows library */
  771.     err = InitRenderingWindows();
  772.     if (err != noErr) goto bail;
  773.     
  774.         /* run the app */
  775.     while (gRunning) {
  776.         EventRecord ev;
  777.         
  778.             /* get the next event */
  779.         if ( ! WaitNextEvent(everyEvent, &ev,  GetCaretTime(), NULL))
  780.             ev.what = nullEvent;
  781.             
  782.             /* call our handler to deal with it. */
  783.         HandleEvent(&ev);
  784.  
  785.     }
  786.     
  787.         /* close all of our windows. */
  788.     CloseRenderingWindows();
  789.     EnsureAboutBoxIsClosed();
  790.     
  791.         /* unregister ourselves with the appearance manager. */
  792.     UnregisterAppearanceClient();
  793.     ExitToShell();
  794.     return 0;
  795. bail:
  796.     NumToString(err, errStr);
  797.     if (err != userCanceledErr)
  798.         ParamAlert(kOpenApplicationErrorAlert, errStr, NULL);
  799.     if (isAppearanceClient)
  800.         UnregisterAppearanceClient();
  801.     ExitToShell();
  802.     return 0;
  803. }