HTMLControl.c

/*
    File: HTMLControl.c
    
    Description:
        HTMLControl implementation.
 
    Copyright:
        © Copyright 2000 Apple Computer, Inc. All rights reserved.
    
    Disclaimer:
        IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
        ("Apple") in consideration of your agreement to the following terms, and your
        use, installation, modification or redistribution of this Apple software
        constitutes acceptance of these terms.  If you do not agree with these terms,
        please do not use, install, modify or redistribute this Apple software.
 
        In consideration of your agreement to abide by the following terms, and subject
        to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
        copyrights in this original Apple software (the "Apple Software"), to use,
        reproduce, modify and redistribute the Apple Software, with or without
        modifications, in source and/or binary forms; provided that if you redistribute
        the Apple Software in its entirety and without modifications, you must retain
        this notice and the following text and disclaimers in all such redistributions of
        the Apple Software.  Neither the name, trademarks, service marks or logos of
        Apple Computer, Inc. may be used to endorse or promote products derived from the
        Apple Software without specific prior written permission from Apple.  Except as
        expressly stated in this notice, no other rights or licenses, express or implied,
        are granted by Apple herein, including but not limited to any patent rights that
        may be infringed by your derivative works or by other works in which the Apple
        Software may be incorporated.
 
        The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
        WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
        WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
        COMBINATION WITH YOUR PRODUCTS.
 
        IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
        CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
        GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
        OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
        (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
        ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
    Change History (most recent first):
        Fri, Jan 28, 2000 -- created
*/
 
 
 
 
 
#ifdef __APPLE_CC__
#include <Carbon/Carbon.h>
#else
#include <Carbon.h>
#endif
 
#include "HTMLControl.h"
 
enum {
    kShiftKeyCode = 56
};
 
/* kUserClickedToFocusPart is a part code we pass to the SetKeyboardFocus
    routine.  In our focus switching routine this part code is understood
    as meaning 'the user has clicked in the control and we need to switch
    the current focus to ourselves before we can continue'. */
#define kUserClickedToFocusPart 100
 
 
/* kHTMLClickScrollDelayTicks is a time measurement in ticks used to
    slow the speed of 'auto scrolling' inside of our clickloop routine.
    This value prevents the text from wizzzzzing by while the mouse
    is being held down inside of the text area. */
#define kHTMLClickScrollDelayTicks 3
 
#define kNavButtonHeight 22
#define kNavButtonWidth 44
#define kNavButtonSpacing 5
 
 
 
/* STPTextPaneVars is a structure used for storing the the HTML Control's
    internal variables and state information.  A handle to this record is
    stored in the pane control's reference value field using the
    SetControlReference routine. */
 
typedef struct {
        /* OS records referenced */
    HRReference fHTMLRec; /* the txn record */
    ControlHandle fUserPaneRec;  /* handle to the user pane control */
    WindowPtr fOwner; /* window containing control */
    CGrafPtr fDrawingEnvironment; /* grafport where control is drawn */
        /* flags */
    Boolean fInFocus; /* true while the focus rect is drawn around the control */
    Boolean fIsActive; /* true while the control is drawn in the active state */
    Boolean fTEActive; /* reflects the activation state of the text edit record */ 
    Boolean fInDialogWindow; /* true if displayed in a dialog window */ 
        /* calculated locations */
    Rect fRCachedBounds; /* area where the text is drawn */
    Rect fRHTMLArea; /* area where the text is drawn */
    Rect fRFocusOutline;  /* rectangle used to draw the focus box */
    Rect fRTextOutline; /* rectangle used to draw the border */
    RgnHandle fTextBackgroundRgn; /* background region for the text, erased before calling TEUpdate */
} STPTextPaneVars;
 
 
 
 
/* Univeral Procedure Pointer variables used by the
    HTML Control.  These variables are set up
    the first time that HTMLOpenControl is called. */
ControlUserPaneDrawUPP gTPDrawProc = NULL;
ControlUserPaneHitTestUPP gTPHitProc = NULL;
ControlUserPaneTrackingUPP gTPTrackProc = NULL;
ControlUserPaneIdleUPP gTPIdleProc = NULL;
ControlUserPaneKeyDownUPP gTPKeyProc = NULL;
ControlUserPaneActivateUPP gTPActivateProc = NULL;
ControlUserPaneFocusUPP gTPFocusProc = NULL;
 
 
 
 
static void CalculateHTMLControlRectangles(STPTextPaneVars **tpvars, Rect *bounds) {
    STPTextPaneVars *varsp;
    char state;
        /* set up our globals */
    state = HGetState((Handle) tpvars);
    HLock((Handle) tpvars);
    varsp = *tpvars;
        /* basic outline and general areas */
    varsp->fRHTMLArea = varsp->fRTextOutline = *bounds;
    varsp->fRFocusOutline = varsp->fRCachedBounds = *bounds;
    RectRgn(varsp->fTextBackgroundRgn, &varsp->fRHTMLArea);
        /* restore state */
    HSetState((Handle) tpvars, state);
}
 
 
/* TPActivatePaneText activates or deactivates the text edit record
    according to the value of setActive.  The primary purpose of this
    routine is to ensure each call is only made once. */
static void TPActivatePaneText(STPTextPaneVars **tpvars, Boolean setActive) {
    STPTextPaneVars *varsp;
    varsp = *tpvars;
    if (varsp->fTEActive != setActive) {
        varsp->fTEActive = setActive;
        if (setActive)
            HRActivate(varsp->fHTMLRec);
        else HRDeactivate(varsp->fHTMLRec);
    }
}
 
 
/* TPFocusPaneText set the focus state for the text record. */
static void TPFocusPaneText(STPTextPaneVars **tpvars, Boolean setFocus) {
    STPTextPaneVars *varsp;
    varsp = *tpvars;
    if (varsp->fInFocus != setFocus) {
        varsp->fInFocus = setFocus;
        /* no special processing for HTML structures */
    }
}
 
 
 
 
 
 
/* TPPaneDrawProc is called to redraw the control and for update events
    referring to the control.  This routine erases the text area's background,
    and redraws the text.  This routine assumes the scroll bar has been
    redrawn by a call to DrawControls. */
static pascal void TPPaneDrawProc(ControlRef theControl, ControlPartCode thePart) {
    STPTextPaneVars **tpvars, *varsp;
    char state;
    Rect bounds;
        /* set up our globals */
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
    if (tpvars != NULL) {
        RgnHandle clipSave;
        state = HGetState((Handle) tpvars);
        HLock((Handle) tpvars);
        varsp = *tpvars;
            
            /* save the drawing state */
        SetPort((**tpvars).fDrawingEnvironment);
        
            /* verify our boundary */
        GetControlBounds(theControl, &bounds);
        if ( ! EqualRect(&bounds, &varsp->fRCachedBounds) ) {
            CalculateHTMLControlRectangles(tpvars, &bounds);
            HRSetRenderingRect( varsp->fHTMLRec, &varsp->fRHTMLArea);
        } else {
            HRSetRenderingRect( varsp->fHTMLRec, &varsp->fRHTMLArea);
        }           
        
            /* update the navigation controls */
        SetPort((**tpvars).fDrawingEnvironment);
        GetClip((clipSave = NewRgn()));
        ClipRect(&varsp->fRHTMLArea);
 
            /* update the text region */
        EraseRgn(varsp->fTextBackgroundRgn);
        HRDraw( varsp->fHTMLRec, varsp->fTextBackgroundRgn);
 
            /* restore the drawing environment */
        SetClip(clipSave);
        DisposeRgn(clipSave);
 
            /* draw the text frame and focus frame (if necessary) */
        DrawThemeEditTextFrame(&varsp->fRTextOutline, varsp->fIsActive ? kThemeStateActive: kThemeStateInactive);
        if ((**tpvars).fIsActive && varsp->fInFocus) DrawThemeFocusRect(&varsp->fRFocusOutline, true);
            /* release our globals */
        HSetState((Handle) tpvars, state);
    }
}
 
 
/* TPPaneHitTestProc is called when the control manager would
    like to determine what part of the control the mouse resides over.
    We also call this routine from our tracking proc to determine how
    to handle mouse clicks. */
static pascal ControlPartCode TPPaneHitTestProc(ControlHandle theControl, Point where) {
    STPTextPaneVars **tpvars;
    ControlPartCode result;
    char state;
        /* set up our locals and lock down our globals*/
    result = 0;
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
    if (tpvars != NULL) {
        state = HGetState((Handle) tpvars);
        HLock((Handle) tpvars);
            /* find the region where we clicked */
        if (PtInRect(where, &(**tpvars).fRHTMLArea)) {
            result = kHTMLTextPart;
        } else result = 0;
            /* release oure globals */
        HSetState((Handle) tpvars, state);
    }
    return result;
}
 
 
 
 
 
/* TPPaneTrackingProc is called when the mouse is being held down
    over our control.  This routine handles clicks in the text area
    and in the scroll bar. */
static pascal ControlPartCode TPPaneTrackingProc(ControlHandle theControl, Point startPt, ControlActionUPP actionProc) {
    STPTextPaneVars **tpvars, *varsp;
    char state;
    ControlPartCode partCodeResult;
        /* make sure we have some variables... */
    partCodeResult = 0;
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
    if (tpvars != NULL) {
            /* lock 'em down */
        state = HGetState((Handle) tpvars);
        HLock((Handle) tpvars);
        varsp = *tpvars;
            /* we don't do any of these functions unless we're in focus */
        if ( ! varsp->fInFocus) {
            WindowPtr owner;
            owner = GetControlOwner(theControl);
            ClearKeyboardFocus(owner);
            SetKeyboardFocus(owner, theControl, kUserClickedToFocusPart);
        }
            /* find the location for the click */
        switch (TPPaneHitTestProc(theControl, startPt)) {
                
                /* handle clicks in the text part */
            case kHTMLTextPart:
                {   EventRecord ev;
                    SetPort((**tpvars).fDrawingEnvironment);
                    ev.what = mouseDown;
                    ev.message = 0;
                    ev.when = TickCount();
                    GetMouse(&ev.where);
                    LocalToGlobal(&ev.where);
                    ev.modifiers = 0;
                    HRIsHREvent(&ev);
                }
                break;
            
        }
        
        HSetState((Handle) tpvars, state);
    }
    return partCodeResult;
}
 
 
/* TPPaneIdleProc is our user pane idle routine.  When our text field
    is active and in focus, we use this routine to set the cursor. */
static pascal void TPPaneIdleProc(ControlHandle theControl) {
    STPTextPaneVars **tpvars, *varsp;
        /* set up locals */
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
    if (tpvars != NULL) {
            /* if we're not active, then we have nothing to say about the cursor */
        if ((**tpvars).fIsActive) {
            char state;
            EventRecord ev;
                /* lock down the globals */
            state = HGetState((Handle) tpvars);
            HLock((Handle) tpvars);
            varsp = *tpvars;
                /* set up our port */
            SetPort(GetWindowPort(GetControlOwner(theControl)));
                /* fake a null event */
            ev.what = nullEvent;
            ev.message = 0;
            ev.when = TickCount();
            GetMouse(&ev.where);
            LocalToGlobal(&ev.where);
            ev.modifiers = 0;
            HRIsHREvent(&ev);           
                /* clean up */
            HSetState((Handle) tpvars, state);
        }
    }
}
 
 
/* TPPaneKeyDownProc is called whenever a keydown event is directed
    at our control.  Here, we direct the keydown event to the text
    edit record and redraw the scroll bar and text field as appropriate. */
static pascal ControlPartCode TPPaneKeyDownProc(ControlHandle theControl,
                            SInt16 keyCode, SInt16 charCode, SInt16 modifiers) {
    STPTextPaneVars **tpvars;
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
    if (tpvars != NULL) {
        if ((**tpvars).fInFocus) {
                /* turn autoscrolling on and send the key event to text edit */
            SetPort((**tpvars).fDrawingEnvironment);
            
            /* no operation here, but it would be cool tu use the arrow
            keys and the page up and page down keys for scrolling... */
        }
    }
    return kControlEntireControl;
}
 
 
/* TPPaneActivateProc is called when the window containing
    the user pane control receives activate events. Here, we redraw
    the control and it's text as necessary for the activation state. */
static pascal void TPPaneActivateProc(ControlHandle theControl, Boolean activating) {
    Rect bounds;
    STPTextPaneVars **tpvars, *varsp;
    char state;
        /* set up locals */
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
    if (tpvars != NULL) {
        state = HGetState((Handle) tpvars);
        HLock((Handle) tpvars);
        varsp = *tpvars;
            /* de/activate the text edit record */
        SetPort((**tpvars).fDrawingEnvironment);
            GetControlBounds(theControl, &bounds);
            varsp->fIsActive = activating;
            TPActivatePaneText(tpvars, varsp->fIsActive /* && varsp->fInFocus */ );
            /* redraw the frame */
        DrawThemeEditTextFrame(&varsp->fRTextOutline, varsp->fIsActive ? kThemeStateActive: kThemeStateInactive);
        if (varsp->fInFocus) DrawThemeFocusRect(&varsp->fRFocusOutline, varsp->fIsActive);
        HSetState((Handle) tpvars, state);
    }
}
 
 
/* TPPaneFocusProc is called when every the focus changes to or
    from our control.  Herein, switch the focus appropriately
    according to the parameters and redraw the control as
    necessary.  */
static pascal ControlPartCode TPPaneFocusProc(ControlHandle theControl, ControlFocusPart action) {
    ControlPartCode focusResult;
    STPTextPaneVars **tpvars, *varsp;
    char state;
        /* set up locals */
    focusResult = kControlFocusNoPart;
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
    if (tpvars != NULL) {
        state = HGetState((Handle) tpvars);
        HLock((Handle) tpvars);
        varsp = *tpvars;
            /* if kControlFocusPrevPart and kControlFocusNextPart are received when the user is
            tabbing forwards (or shift tabbing backwards) through the items in the dialog,
            and kControlFocusNextPart will be received.  When the user clicks in our field
            and it is not the current focus, then the constant kUserClickedToFocusPart will
            be received.  The constant kControlFocusNoPart will be received when our control
            is the current focus and the user clicks in another control.  In your focus routine,
            you should respond to these codes as follows:
 
            kControlFocusNoPart - turn off focus and return kControlFocusNoPart.  redraw
                the control and the focus rectangle as necessary.
 
            kControlFocusPrevPart or kControlFocusNextPart - toggle focus on or off
                depending on its current state.  redraw the control and the focus rectangle
                as appropriate for the new focus state.  If the focus state is 'off', return the constant
                kControlFocusNoPart, otherwise return a non-zero part code.
            kUserClickedToFocusPart - is a constant defined for this example.  You should
                define your own value for handling click-to-focus type events. */
            /* save the drawing state */
        SetPort((**tpvars).fDrawingEnvironment);
            /* calculate the next highlight state */
        switch (action) {
            default:
            case kControlFocusNoPart:
                TPFocusPaneText(tpvars, false);
                focusResult = kControlFocusNoPart;
                break;
            case kUserClickedToFocusPart:
                TPFocusPaneText(tpvars, true);
                focusResult = 1;
                break;
            case kControlFocusPrevPart:
            case kControlFocusNextPart:
                TPFocusPaneText(tpvars, ( ! varsp->fInFocus));
                focusResult = varsp->fInFocus ? 1 : kControlFocusNoPart;
                break;
        }
            TPActivatePaneText(tpvars, varsp->fIsActive /* && varsp->fInFocus */ );
            /* redraw the text fram and focus rectangle to indicate the
            new focus state */
        DrawThemeEditTextFrame(&varsp->fRTextOutline, varsp->fIsActive ? kThemeStateActive: kThemeStateInactive);
        DrawThemeFocusRect(&varsp->fRFocusOutline, varsp->fIsActive && varsp->fInFocus);
            /* done */
        HSetState((Handle) tpvars, state);
    }
    return focusResult;
}
 
 
 
 
/* HTMLOpenControl initializes a user pane control so it will be drawn
    and will behave as a scrolling text edit field inside of a window.
    This routine performs all of the initialization steps necessary,
    except it does not create the user pane control itself.  theControl
    should refer to a user pane control that you have either created
    yourself or extracted from a dialog's control heirarchy using
    the GetDialogItemAsControl routine.  */
OSStatus HTMLOpenControl(ControlHandle theControl) {
    Rect bounds;
    WindowPtr theWindow;
    STPTextPaneVars **tpvars, *varsp;
    OSStatus err;
    
    if ( ! HRHTMLRenderingLibAvailable()) return paramErr;
    
        /* set up our globals */
    if (gTPDrawProc == NULL) gTPDrawProc = NewControlUserPaneDrawUPP(TPPaneDrawProc);
    if (gTPHitProc == NULL) gTPHitProc = NewControlUserPaneHitTestUPP(TPPaneHitTestProc);
    if (gTPTrackProc == NULL) gTPTrackProc = NewControlUserPaneTrackingUPP(TPPaneTrackingProc);
    if (gTPIdleProc == NULL) gTPIdleProc = NewControlUserPaneIdleUPP(TPPaneIdleProc);
    if (gTPKeyProc == NULL) gTPKeyProc = NewControlUserPaneKeyDownUPP(TPPaneKeyDownProc);
    if (gTPActivateProc == NULL) gTPActivateProc = NewControlUserPaneActivateUPP(TPPaneActivateProc);
    if (gTPFocusProc == NULL) gTPFocusProc = NewControlUserPaneFocusUPP(TPPaneFocusProc);
        
        /* allocate our private storage */
    tpvars = (STPTextPaneVars **) NewHandleClear(sizeof(STPTextPaneVars));
    SetControlReference(theControl, (long) tpvars);
    HLock((Handle) tpvars);
    varsp = *tpvars;
        /* set the initial settings for our private data */
    varsp->fInFocus = false;
    varsp->fIsActive = true;
    varsp->fTEActive = false;
    varsp->fUserPaneRec = theControl;
    theWindow = varsp->fOwner = GetControlOwner(theControl);
    varsp->fDrawingEnvironment = GetWindowPort(varsp->fOwner);
    SetPort(varsp->fDrawingEnvironment);
    varsp->fInDialogWindow = ( GetWindowKind(varsp->fOwner) == kDialogWindowKind );
    varsp->fTextBackgroundRgn = NewRgn();
        /* set up the user pane procedures */
    SetControlData(theControl, kControlEntireControl, kControlUserPaneDrawProcTag, sizeof(gTPDrawProc), &gTPDrawProc);
    SetControlData(theControl, kControlEntireControl, kControlUserPaneHitTestProcTag, sizeof(gTPHitProc), &gTPHitProc);
    SetControlData(theControl, kControlEntireControl, kControlUserPaneTrackingProcTag, sizeof(gTPTrackProc), &gTPTrackProc);
    SetControlData(theControl, kControlEntireControl, kControlUserPaneIdleProcTag, sizeof(gTPIdleProc), &gTPIdleProc);
    SetControlData(theControl, kControlEntireControl, kControlUserPaneKeyDownProcTag, sizeof(gTPKeyProc), &gTPKeyProc);
    SetControlData(theControl, kControlEntireControl, kControlUserPaneActivateProcTag, sizeof(gTPActivateProc), &gTPActivateProc);
    SetControlData(theControl, kControlEntireControl, kControlUserPaneFocusProcTag, sizeof(gTPFocusProc), &gTPFocusProc);
        /* calculate the rectangles used by the control */
    GetControlBounds(theControl, &bounds);
    CalculateHTMLControlRectangles(tpvars, &bounds);
        
        /* create the new edit field */
    err = HRNewReference( &varsp->fHTMLRec, kHRRendererHTML32Type, varsp->fDrawingEnvironment);
    if (err == NULL) goto bail;
    HRSetRenderingRect( varsp->fHTMLRec, &varsp->fRHTMLArea);
        /* unlock our storage */
    HUnlock((Handle) tpvars);
    
        /* perform final activations and setup for our text field.  Here,
        we assume that the window is going to be the 'active' window. */
    TPActivatePaneText(tpvars, varsp->fIsActive /* && varsp->fInFocus */ );
        /* all done */
    return noErr;
bail:
    return err;
}
 
 
 
/* HTMLCloseControl deallocates all of the structures allocated
    by HTMLOpenControl.  */
OSStatus HTMLCloseControl(ControlHandle theControl) {
    STPTextPaneVars **tpvars;
        /* set up locals */
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
        /* release our sub records */
    HRDisposeReference((**tpvars).fHTMLRec);
        /* delete our private storage */
    DisposeHandle((Handle) tpvars);
        /* zero the control reference */
    SetControlReference(theControl, 0);
    return noErr;
}
 
/* STUPCreateControl creates a new user pane control and then it passes it
    to STUPOpenControl to initialize it as a scrolling text user pane control. */
OSStatus HTMLCreateControl(WindowPtr theWindow, Rect *bounds, ControlHandle *theControl) {
    short featurSet;
        /* the following feature set can be specified in CNTL resources by using
        the value 1214.  When creating a user pane control, we pass this value
        in the 'value' parameter. */
    featurSet = kControlSupportsEmbedding | kControlSupportsFocus | kControlWantsIdle
            | kControlWantsActivate | kControlHandlesTracking | kControlHasSpecialBackground
            | kControlGetsFocusOnClick | kControlSupportsLiveFeedback;
        /* create the control */
    *theControl = NewControl(theWindow, bounds, "\p", true, featurSet, 0, featurSet, kControlUserPaneProc, 0);
        /* set up the STUP specific features and data */
    HTMLOpenControl(*theControl);
        /* all done.... */
    return noErr;
}
 
 
/* STUPDisposeControl calls STUPCloseControl and then it calls DisposeControl. */
OSStatus HTMLDisposeControl(ControlHandle theControl) {
        /* deallocate the STUP specific data */
    HTMLCloseControl(theControl);
        /* deallocate the user pane control itself */
    DisposeControl(theControl);
    return noErr;
}
 
 
 
 
/* HTMLGotoURL displays HTML file referred to by the url in the
    rendering control.  if addToHistory is true, then the window
    will be added to the window's history list. */
OSStatus HTMLDisplayFile(ControlHandle theControl, FSSpec *theFile) {
    STPTextPaneVars **tpvars;
        /* set up locals */
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
        /* display the file */
    return HRGoToFile( (**tpvars).fHTMLRec, theFile, true, true);
}
 
 
    /* HTMLDisplayBuffer displays the HTML described by the buffer. */
OSStatus HTMLDisplayBuffer(ControlHandle theControl, char *buffer, long bytecount) {
    STPTextPaneVars **tpvars;
        /* set up locals */
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
        /* display the file */
    return HRGoToPtr(  (**tpvars).fHTMLRec, buffer, bytecount, false, true);
}
 
 
    /* HTMLDisplayHandle displays the HTML in the handle htmlData. */
OSStatus HTMLDisplayHandle(ControlHandle theControl, Handle htmlData) {
    STPTextPaneVars **tpvars;
    char state;
    OSStatus err;
        /* set up locals, verify parameters */
    if (htmlData == NULL || theControl == NULL) return paramErr;
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
        /* display the file */
    state = HGetState(htmlData);
    HLock(htmlData);
    err = HRGoToPtr(  (**tpvars).fHTMLRec, (char*) (*htmlData), GetHandleSize(htmlData), false, true);
    HSetState(htmlData, state);
    return err;
}
 
 
/* IsHTMLControl returns true if theControl is not NULL
    and theControl refers to a HTML Control.  */
Boolean IsHTMLControl(ControlHandle theControl) {
    Size theSize;
    ControlUserPaneFocusUPP localFocusProc;
        /* a NULL control is not a mUP control */
    if (theControl == NULL) return false;
        /* check if the control is using our focus procedure */
    theSize = sizeof(localFocusProc);
    if (GetControlData(theControl, kControlEntireControl, kControlUserPaneFocusProcTag,
        sizeof(localFocusProc), &localFocusProc, &theSize) != noErr) return false;
    if (localFocusProc != gTPFocusProc) return false;
        /* all tests passed, it's a mUP control */
    return true;
}
 
 
 
    /* HTMLDisplayHandle displays the HTML in the handle htmlData. */
OSStatus HTMLDisplayURL(ControlHandle theControl, char const* url) {
    STPTextPaneVars **tpvars;
    OSStatus err;
        /* set up locals, verify parameters */
    if (url == NULL || theControl == NULL) return paramErr;
    tpvars = (STPTextPaneVars **) GetControlReference(theControl);
    
        /* display the file */
    err = HRGoToURL( (**tpvars).fHTMLRec, url, true, true);
 
    return err;
}
 
 
 
 
 
 
 
#if 0
/* RWGotoURL displays HTML file referred to by the url in the
    rendering window.  if addToHistory is true, then the window
    will be added to the window's history list. */
OSStatus RWGotoURL(WindowPtr rWin, char* url, Boolean addToHistory) {
    RWindowHandle rwv;
    OSStatus err;
    Point origin = {0, 0};
    Rect donedrawingbox;
        /* set up locals */
    rwv = (RWindowHandle) GetWRefCon(rWin);
 
        /* if the URL does not contain an anchor, then scroll
        the HTML view to the origin. */
    if ( ! URLContainsAnchor(url)) {
        err = HRScrollToLocation((**rwv).renderer, &origin);
        if (err != noErr) goto bail;
    }
        /* ask the rendering object to display the HTML page
        referred to by the URL.  note, we pass the addToHistory to
        this routine which in turn passes it to our MyNewURLProc
        routine. */
    err = HRGoToURL((**rwv).renderer, url, addToHistory, false);
    if (err != noErr) goto bail;
    
        /* draw the new page */
    err = HRDraw((**rwv).renderer, NULL);
    if (err != noErr) goto bail;
    
        /* if addToHistory is false, then we would not
        have re-drawn the buttons inside of our MyNewURLProc
        routine so we should draw them here. */
    if ( ! addToHistory)
        RedrawWindowButtons(rwv);
        
        /* we validate the areas that were drawn by this
        routine.  This is to avoid unnecessary redraws when
        the window is first opened.  When the window is opened,
        an update event will be posted for its entire contents.
        These calls prevent the areas we have just drawn from
        being re-drawn when the update event is processed. */
    SetRect(&donedrawingbox, 0, 0, 32*3, 32);
    ValidWindowRect(rWin, &donedrawingbox);
    GetPortBounds(GetWindowPort((**rwv).rwindow), &donedrawingbox);
    donedrawingbox.top += 33;
    ValidWindowRect(rWin, &donedrawingbox);
 
        /* done */
    return noErr;
bail:
    return err;
}
 
 
/* GetApplicationFolder returns the volume reference number and
    directory id of the folder containing the application. */
OSStatus GetApplicationFolder(FSSpec *spec) {
    OSStatus err;
    FCBPBRec fcpb;
    Str255 name;
    CInfoPBRec cat;
    Handle h;
    
    h = GetResource('vTeZ', 0);
    if (h == NULL) return resNotFound;
    
    BlockZero(&fcpb, 0);
    fcpb.ioVRefNum = 0;
    fcpb.ioNamePtr = name;
    fcpb.ioFCBParID = 0;
    fcpb.ioRefNum = HomeResFile(h);
    fcpb.ioFCBIndx = 0;
    if ((err = PBGetFCBInfoSync(&fcpb)) != noErr) return err;
    
    BlockZero(&cat, 0);
    cat.dirInfo.ioNamePtr = name;
    cat.dirInfo.ioVRefNum = fcpb.ioFCBVRefNum;
    cat.dirInfo.ioFDirIndex = -1;
    cat.dirInfo.ioDrDirID = fcpb.ioFCBParID;
    err = PBGetCatInfoSync(&cat);
    if (err != noErr) return err;
    
    err = FSMakeFSSpec(fcpb.ioFCBVRefNum, cat.dirInfo.ioDrParID, name, spec);
    if (err != noErr) return err;
    
    return noErr;
}
 
 
 
/* GetApplicationFolder returns a URL referring to the folder
    containing the application.  If successful, *url is set
    to a new handle containing the URL.  It is the caller's
    responsibility to dispose of this handle. */
OSStatus GetApplicationFolderURL(Handle *url) {
    OSStatus err;
    FSSpec spec;
    Handle theURL;
        /* set up locals to a known state */
    theURL = NULL;
        /* find the application's folder */ 
    err = GetApplicationFolder(&spec);
    if (err != noErr) goto bail;
        /* create a new handle for storing the URL */
    theURL = NewHandle(0);
    if (theURL == NULL) { err = memFullErr; goto bail; }
        /* ask the HTML rendering library to convert
        the FSSpec into a URL */
    err = HRUtilGetURLFromFSSpec(&spec, theURL);
    if (err != noErr) goto bail;
    
        /* add a slash at the end of the name, if one is not there */
    if ( (*theURL)[GetHandleSize(theURL) - 2] != '/')
        Munger(theURL, (GetHandleSize(theURL) - 1), NULL, 0, "/", 1);
 
        /* return the URL */
    *url = theURL;
    return noErr;
bail:
    if (theURL != NULL) DisposeHandle(theURL);
    return err;
}
 
 
/* RWGotoAppRelLink displays HTML file referred to by the application
    relative link in the rendering window.  if addToHistory is true, then
    the window will be added to the window's history list. */
OSStatus RWGotoAppRelLink(WindowPtr rWin, char* linkstr, Boolean addToHistory) {
    OSStatus err;
    Handle rootURL, fullURL;
        /* set up our locals */
    rootURL = fullURL = NULL;
        /* get the URL for the application's folder */
    err = GetApplicationFolderURL(&rootURL);
    if (err != noErr) goto bail;
        /* allocate a handle for storing the full URL */
    fullURL = NewHandle(0);
    if (fullURL == NULL) { err = memFullErr; goto bail; }
        /* ask the HTML rendering library to combine
        the url and the link to make a complete URL */
    HLock(rootURL);
    err = HRUtilCreateFullURL(*rootURL, linkstr, fullURL);
    if (err != noErr) goto bail;
    HUnlock(rootURL);
        /* call the RWGotoURL to bring the HTML file into view. */
    MoveHHi(fullURL);
    HLock(fullURL);
    err = RWGotoURL(rWin, *fullURL, addToHistory);
    if (err != noErr) goto bail;
    
        /* clean up our locals */
    DisposeHandle(rootURL);
    DisposeHandle(fullURL);
        /* done */
    return noErr;
bail:
        /* on error, clean up and return the error. */
    if (rootURL != NULL) DisposeHandle(rootURL);
    if (fullURL != NULL) DisposeHandle(fullURL);
    return err;
}
#endif