/*
    Foundation Resource Editor Module Example - Pascal String

	Marc Wolfgram and Mark Collins, 8/ 7/92 21:58:58

    1.0 release 30Sep92, Lunar Productions

    1.0.1 revised 7/11/93 to incorporate new function-style callbacks and a
        little bit of code cleanup.

    This source code may be adapted and used freely for developing Foundation
    modules.  It is intended only as an example for the sole purpose of this
    development.  Use for other purposes is prohibited.

        Copyright 1993-1993 by Lunar Productions - ALL RIGHTS RESERVED
*/
#pragma noroot
#pragma lint        3
#pragma memorymodel 0
#pragma optimize    14

#include <foundation.h>
#include <event.h>
#include <textedit.h>
#include <scrap.h>
#include <window.h>

/*
	Custom items - this is an idea of what a Pascal String editor would use
    for storing an editing sessions data.  Your editor may have different
    needs.  Consider this a minimum.
*/
struct privateData {
	handle      data;   /* this holds the handle of the original resource data */
    CtlRecHndl  ctlH;   /* the master TEControl handle */
};
typedef struct privateData **privateDataHndl;

/*
	Externals
*/
extern EventRecordPtr fEventPtr;

/*
	Globals
*/
extern word fUserID;	    /* editor's memory manager ID */
extern long remHelpID;      /* place to store the ID of an editor's rText
                               resource ID for use by Foundations Help */

word    edResFileID,		/* editor's resource file */
        shResFileID,		/* Foundation shell resource file */
        fdResFileID;		/* user's Foundation.Data resource file */

void DrawProc(void);        /* prototype declarations */
void CheckData(GrafPortPtr, long);
void SetTitle (GrafPortPtr, long);

/*	========================================================================
    REM_OPEN initiates an editing session (or a selector session) and is the
    first resource specific call made to the editor.

	The editor must support four types of open, five if it has a selector.

	A new item needs to be created from scratch.
		fFlag bits 8 (F_OPENSILENT) and 9 (F_OPENDATA) are clear.
		The resID is NIL so the editor must assign the resource's new ID.
		The resource does not exist in the workfile and must be added.
		The editor MUST open an editing window.
		The editor can provide default data for the new item.

	A new item needs to be created with default data provided by another source.  The data is provided in a handle, and the new item may
		fFlag bit 8 is clear.
		fFlag bit 9 is set and default data for the new item is provided.
		The resID MIGHT be pre-assigned.
		The resource does not exist in the workfile and must be added.
		The editor MUST open an editing window.

	A new item needs to be created, but the REM should simply make it and return.
		fFlag bit 8 is set.
		fFlag bit 9 MAY BE set with default data for the new item provided.
		The resID MIGHT be pre-assigned.
		The resource does not exist in the workfile and must be added.
		The editor MUST NOT open an editing window.

	An existing resource needs to be edited.  This is the most common case.
		fFlag bits 8 and 9 are clear.
		The resID is a valid ID
		the resource to be edited exists in the workfile.
		The editor MUST open an editing window.

     The REM supports a resource selector (this sample doesn't have a selector)
     	fFlag bits 9 and 10 (F_REMSELECT) will be set.
        The list member record array that the shell would normally have used in
        the standard type window's item list is the provided data.
		The editor MUST open a selector window.

    The REM_OPEN parameter block looks like this:

        struct fOpenRec {
            word    resType;    - must be rPString for this example editor
            long    resID;      - 0x00000000 ... 0x07ffffff
            word    fFlag;      - 0x0200 Data Valid, 0x0100 Silent, 0x0300 B0oth
            Handle  Data;       - If Data Valid, this is a pstring handle
            Pointer wColorPtr;  - The color table for the active workfile
        };
        typedef struct fOpenRec fOpenRec, *fOpenRecPtr;
 */
word REM_OPEN(fOpenRecPtr p)
{
Handle	        ctlH, resData;
privateDataHndl private;
GrafPortPtr     winP;
ParamListHndl   tempH;


    /*
    	First see if we edit this type...
	    (a multi-type editor will have to case those types it supports)
	*/
    if (p->resType != rPString)
        return resInvalidTypeOrID;

	/*
		Now handle a "new resource" check.  First we will see if there
		is data provided and either load it or the editor's built-in
		default (ID 1).  Then we add the resource, posibly getting a new
		ID in the process.  Last, if the request was silent, we're done
		and hould just release the resource because something else needs
		needs it.
	*/

	if (!p->resID || (p->fFlag & (F_OPENSILENT | F_OPENDATA))) {

		if (p->fFlag & F_OPENDATA) 		/* data is provided - handle is ours */
        	resData = p->Data;
        else {							/* no data, load default from editor */
			resData = fLoadResource2(rPString, 1L, 0L, edResFileID, 0L);    /* we want this one here */
			fDetachResource2(rPString, 1L, edResFileID);
		}

        p->resID = fAddResource2(rPString, p->resID, resData, 0, 0, 0L);

		fReleaseResource2(rPString, p->resID, 0);

        if (p->fFlag & F_OPENSILENT) {  /* this was a silent request - done! */

           	return 0;
        }
	}

	/*
    	If we get here, we definitely have a valid resource to edit in the
        workfile, and we move into "normal open" mode:
		    Load the editor's window converted to a handle reference
		    Get the official window title from the shell
			Open the editor's window (hidden for appearance only)
			Set the window's private data interface structure in the shell
		    Load the resource we're to edit
			Assign the resource and title handles in our private data handle
    */

    private = (privateDataHndl) NewHandle(8L, fUserID, 0x0018, 0L);

	tempH = (ParamListHndl) fSpecialMagic2(rWindParam1, 1L, 0L, 0, edResFileID);
	tempH = (ParamListHndl) fSpecialMagic2(rWindParam1, 0L, (Handle) tempH, 1, edResFileID);
    (**private).ctlH = (CtlRecHndl) (**tempH).wStorage;

    winP = NewWindow2(0L, 0L, &DrawProc, 0L, 1, tempH, rWindParam1);
    SetTitle(winP, p->resID);
    SetPort(winP);

    fAddPrivateData2(winP, 0L, 0, (Handle) private, 0x0020, rPString, p->resID);

    (**private).data = fLoadResource2(rPString, p->resID, 0L, 0, 0L);

	/*
		Now we set the data into the TE control, set the window colors,
		and show the window, completing the REM_OPEN call.
	*/

    ctlH = (handle) GetCtlHandleFromID(winP, 1L);
    TESetText(teTextIsHandle + teDataIsPString, (**private).data, 0L, 0, 0L, ctlH);

    SetFrameColor(p->wColorPtr, winP);
    ShowWindow(winP);

    return  0;
}

/*	========================================================================
    REM_CLOSE updates the resource if it has changed, and closes up shop.

    The REM_CLOSE parameter block looks like this:

        struct fCloseRec {
            word    resType;    - rPString
            long    resID;      - 0x00000001 ... 0x07ffffff
            word    fFlag;      - no input
            Pointer windowPtr;  - editor's GrafPortPtr
        };
        typedef struct fCloseRec fCloseRec, *fCloseRecPtr;

 */
word REM_CLOSE(fCloseRecPtr p)
{
    CheckData((GrafPortPtr) p->windowPtr, p->resID);

    DisposeHandle(fGetPrivateData2((GrafPortPtr) p->windowPtr, 0L, 0L));

    fRelPrivateData2((GrafPortPtr) p->windowPtr);

    CloseWindow((GrafPortPtr) p->windowPtr);

	fReleaseResource2(rPString, p->resID, 0);

    return  0;
}

/*	========================================================================
    REM_WRITE updates the resource if it has changed.

    The REM_CLOSE parameter block is used by REM_WRITE too:

        struct fCloseRec {
            word    resType;    - rPString
            long    resID;      - 0x00000001 ... 0x07ffffff
            word    fFlag;      - no input
            Pointer windowPtr;  - editor's GrafPortPtr
        };
        typedef struct fCloseRec fCloseRec, *fCloseRecPtr;

 */
word REM_WRITE(fCloseRecPtr p)
{
    CheckData((GrafPortPtr) p->windowPtr, p->resID);

    return 0;
}

/*	========================================================================
    REM_EVENT is called for any and all events that occur while the editor
    window is active.  Since the rPString editor is mainly a TextEdit control
    the only event that we need to worry about in this example is Foundation's
    app3Evt which signals a change in the resource name which should be used in
    the window's title.

    The REM_EVENT parameter block looks like this:

        struct fEventRec {
            word    resType;    - rPString
            long    resID;      - 0x00000001 ... 0x07ffffff
            word    fFlag;      - no input
            word    taskCode;   - Event task code
        };
        typedef struct fEventRec fEventRec, *fEventRecPtr;

 */
word REM_EVENT(fEventRecPtr p)
{
GrafPortPtr     winP;

    winP = (GrafPortPtr) fEventPtr->wmTaskData;

    switch (p->taskCode) {
    case app3Evt:
        SetTitle(winP, p->resID);
        break;

    default:
        break;
    }

    return  0;
}

/*	========================================================================
    REM_ACTIVATE is called for both Activate and Deactivate events.  For the
    rPString editor Activate simply provides a place to setup the Edit Menu
    items properly, while Deactivate requires updating of the resource data.

    The REM_ACTIVATE parameter block looks like this:

        struct fActivateRec {
            word    resType;    - rPString
            long    resID;      - 0x00000001 ... 0x07ffffff
            word    fFlag;      - bit 15 (0x8000) set if Activate
            Handle  linkHand;   - null, used by native selectors only
        };
        typedef struct fActivateRec fActivateRec, *fActivateRecPtr;

 */
word REM_ACTIVATE(fActivateRecPtr p)
{

	if (p->fFlag & F_ACTIVATE)  /* Activate - set fFlag for menu changes */
		p->fFlag |= (F_CLIP + F_CLEAR + F_MENUAPPLY);

    else                        /* Deactivate - clean up but don't go away */
        CheckData((GrafPortPtr) fEventPtr->wmTaskData, p->resID);

    return  0;
}

/*	========================================================================
    REM_GETLINK is an extension to the shell's dependency structure.  Pascal
    Strings don't have dependents.  The supports REM_GETLINK rFlag bit 6 in
    the remStartRes is clear.

    IF YOU ARE WRITING A CUSTOM EDITOR WITH RESOURCE DEPENDENCY SUPPORT YOU
    SHOULD USE THIS AND CONTACT US FOR USEFUL HINTS AND TIPS BECAUSE MANY
    RESOURCE TYPES ARE AUTOMATICALLY HANDLED BY THE SHELL.
*/
word REM_GETLINK(fLinkRecPtr p)
{
    return  0;
}

/*	========================================================================
    REM_VIDMODE lets the editor know when it's world is changing.  We didn't
    see a valid reason for a 320 mode Pascal String editor, but decided to
    support one anyway.

    The REM_VIDMODE parameter block looks like this:

        struct fVidModeRec {
            word    resType;    - rPString
            long    resID;      - 0x00000001 ... 0x07ffffff
            word    fFlag;      - bit 14 (0x4000) set if entering 640 mode
            Pointer windowPtr;  - editor's GrafPortPtr
            Pointer wColorPtr;  - new mode's standard window color table
        };
        typedef struct fVidModeRec fVidModeRec, *fVidModeRecPtr;

*/
word REM_VIDMODE(fVidModeRecPtr p)
{
GrafPortPtr     winP;
Handle          ctlH;
long            txtLength;
ParamListHndl   winH;
handle          txtData;
privateDataHndl private;

    winP = (GrafPortPtr) p->windowPtr;

    ctlH = (handle) GetCtlHandleFromID(winP, 1L);
    txtLength = TEGetText(0x0018, &txtData, 0L, 0, 0L, ctlH);
    HideControl(ctlH);
    DisposeControl(ctlH);

    private = (privateDataHndl) fGetPrivateData2(winP, 0L, 0L);
    ctlH = (Handle) NewControl2(winP, 1, (long) (**private).ctlH);

    TESetText(teTextIsHandle + teDataIsPString, txtData, 0L, 0, 0L, ctlH);
    SetFrameColor(p->wColorPtr, winP);
    return  0;
}

/*	========================================================================
    REM_PRINT must be supported entirely by the editor.  Why would we want
    to print a Pascal String?  We don't.

    The REM_CLOSE parameter block is used by REM_PRINT too:

        struct fCloseRec {
            word    resType;    - rPString
            long    resID;      - 0x00000001 ... 0x07ffffff
            word    fFlag;      - PageSetup request if bit 13 (0x2000) is set
            Pointer windowPtr;  - editor's GrafPortPtr
        };
        typedef struct fCloseRec fCloseRec, *fCloseRecPtr;
 */
word REM_PRINT(fCloseRecPtr p)
{
    return  0;
}

/*	========================================================================
    REM_STARTUP is called only when the REM is first loaded.

    The REM should do whatever it needs to do to setup globals, etc.

    This is the only time when the various Foundation and REM resource file
    ID's are passed to the REM.  Also, the REM's specific memory manager ID
    is passed here - READ THE WARNING IN REM_SHUTDOWN!

    The REM_STARTUP parameter block looks like this:

        struct fStartStopRec {
            word    edUserID;       - Memory Manager ID assigned to the editor
            word    edResFileID;    - The editor's resource fork ID
            word    shResFileID;    - The shell's resource fork ID
            word    fdResFileID;    - Foundation.Data's resource fork ID
        };
        typedef struct fStartStopRec fStartStopRec, *fStartStopRecPtr;

*/
word REM_STARTUP(fStartStopRecPtr p)
{
    fUserID = p->edUserID;
    edResFileID = p->edResFileID;
    shResFileID = p->shResFileID;
    fdResFileID = p->fdResFileID;

    remHelpID = 1L; /* stuff a rText ID into the editors header help field */

    return  0;
}

/*	========================================================================
    REM_SHUTDOWN is called when an editor is unloaded.

    WARNING: The REM must release any memory allocated at startup. There is
             no guarantee that once the editor is released that it will be
             reloaded.

    The REM_SHUTDOWN parameter block looks like this:

        struct fStartStopRec {
            word    edUserID;       - Memory Manager ID assigned to the editor
            word    edResFileID;    - The editor's resource fork ID
            word    shResFileID;    - null
            word    fdResFileID;    - null
        };
        typedef struct fStartStopRec fStartStopRec, *fStartStopRecPtr;

*/

word REM_SHUTDOWN(fStartStopRecPtr p)
{
	DisposeAll(fUserID);	/* before assuming that this simplicity is 	*/
    return  0;				/* all you need, read TBR-1, page 12-23!	*/
}

#pragma databank 1
/*
    DrawProc - this is all the editor needs to update its window
*/
void DrawProc(void)
{
    DrawControls(GetPort());
}
#pragma databank 0

/*
    CheckData writes a pstring resource only if neecessary.

    This is used in REM_CLOSE, REM_WRITE and REM_ACTIVATE.
*/
void CheckData(GrafPortPtr winP, long resID)
{
int             i, changed;
long            size;
char            *resP, *txtP;
handle          resData, txtData;
privateDataHndl private;

    private = (privateDataHndl) fGetPrivateData2(winP, 0L, 0L);
    resData = (**private).data;
    HLock(resData);
    resP = *resData;

    size = TEGetText(0x0018, &txtData, 0L, 0, 0L, GetCtlHandleFromID(winP, 1L));
    HLock(txtData);
    txtP = *txtData;

    changed = FALSE;

    if (resP[0] != txtP[0]) /* length check - easy test for difference */
        changed = TRUE;
    else {                  /* check characters until a change noticed */
        for(i = resP[0]; i > 0; i--) {
            if (resP[i] != txtP[i]) {
                changed = TRUE;
                break;
            }
        }
    }

    if (changed) {          /* update the resData with the TE data */
        size = GetHandleSize(txtData);
        HUnlock(resData);
        SetHandleSize(size, resData);
        HLock(resData);
        HandToHand(txtData, resData, size);

        fWriteResource2(rPString, resID, 0);
    }

    HUnlock(resData);
    DisposeHandle(txtData);
}

/*
    SetTitle is passed a PString editor window pointer and the pascal string's
    resource ID.  It calls the shell to build the proper title string for the
    window, and then sets the title as a handle with the high-bit of the handle
    reference set so the Window Manager will use and dispose it properly.

    This is used in REM_OPEN and REM_EVENT.
*/
void SetTitle (GrafPortPtr winP, long ID)
{
handle  title;

	title = NewHandle(128L, fUserID, 0x8018, 0L);

	fGetWindowTitle2(rPString, ID, *title);

    SetHandleSize((long) (**title & 0x000000FFL), title);

    SetWTitle((title | 0x80000000L), winP);
}
