/*
    Foundation Resource Editor Module Example - Pascal String

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

    1.0 release 30Sep92, Lunar Productions

    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.
*/
#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;
};
typedef struct privateData **privateDataHndl;

/*
	Externals
*/
extern EventRecordPtr fEventPtr;

/*
	Globals
*/
extern word fUserID;	    /* editor's memory manager ID  			*/
extern long remHelpID;

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

fPrivateDataRec 	PrivPB;	/* callback parameter blocks...			*/
fResDataRec     	DataPB;
fResData2Rec    	Data2PB;
fResNameRec     	NamePB;
fResRefRec      	RefPB;	/*	 ___________________________________
fResCopyRec			CopyPB;		|									|
fResLinkRec			LinkPB;		|	these are not needed in the		|
fResConverterRec	ConvertPB;	|	Pascal String sample editor     |
fGetFileRec			FilePB;		|___________________________________| */
fColorRec           ColorPB;

void DrawProc(void);        /* prototype declarations */
void checkData(long, handle, handle);
privateDataHndl getPrivate(GrafPortPtr);
handle getTitle (word type, long ID);


/*	========================================================================
    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)
{
word            retVal,
                retErr;
Handle	        title, ctlH;
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))) {

		DataPB.resType   = rPString;
		RefPB.resType = rPString;

		if (p->fFlag & F_OPENDATA) 		/* data is provided - handle is ours */
        	DataPB.resData = p->Data;

        else {							/* no data, load default from editor */
        	DataPB.pCount    = 6;
			DataPB.resID     = 1L;              /* the default resource's ID */
			DataPB.resFileID = edResFileID;
			retErr = fLoadResource(&DataPB);    /* we want this one here */

        	RefPB.pCount = 3;				/* detach default so we own it		*/
			RefPB.resID = 1L;
			RefPB.resFileID = edResFileID;
			retErr = fDetachResource(&RefPB);
		}
        DataPB.pCount = 4;
		DataPB.resID = p->resID;		/* if null, then Add returns ID added */
		DataPB.special = 0;				/* no attributes assigned */
		retErr = fAddResource(&DataPB);
        p->resID = DataPB.resID;		/* this may change so pass it back */

        RefPB.pCount = 2;
		RefPB.resID = DataPB.resID;
		retErr = fReleaseResource(&RefPB);

        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
    */

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

    DataPB.pCount = 5;                  /* get and lock our window stuff */
	DataPB.resType = rWindParam1;
	DataPB.resID = 0x00000001L;
	DataPB.special = 0;                 /* load */
	DataPB.resFileID = edResFileID;
	retErr = fSpecialMagic(&DataPB);
    DataPB.special = 1;                 /* lock */
    retErr = fSpecialMagic(&DataPB);

    tempH = (ParamListHndl) DataPB.resData;
    (**private).ctlH = (CtlRecHndl) (**tempH).wStorage;

    winP = NewWindow2(0L, 0L, &DrawProc, 0L, 1, DataPB.resData, rWindParam1);
    SetWTitle(getTitle(rPString, p->resID), winP);
    SetPort(winP);
	PrivPB.pCount = 7;              /* setup the window refCon for the shell */
	(GrafPortPtr) PrivPB.winPtr = winP;
	PrivPB.remTaskMask = 0L;        /* use standard task mask */
	PrivPB.remSignature = 0;        /* personal signature - no significance */
	PrivPB.Data = (Handle) private;
    PrivPB.groupSignature = 0x0020;
    PrivPB.resType = rPString;
    PrivPB.resID = p->resID;
    fAddPrivateData(&PrivPB);

    DataPB.pCount = 3;              /* finally, get the resource we're editing */
	DataPB.resType = rPString;
    DataPB.resID = p->resID;
	retErr = fLoadResource(&DataPB);

    (**private).data = DataPB.resData;

	/*
		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, DataPB.resData, 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)
{
word            retErr;
handle          txtData;
long            txtLength;
GrafPortPtr     winP;
privateDataHndl private;

    winP = (GrafPortPtr) p->windowPtr;
    txtLength = TEGetText(0x0018, &txtData, 0L, 0, 0L, GetCtlHandleFromID(winP, 1L));
    private = getPrivate(winP);
    checkData(p->resID, (**private).data, txtData);

    DisposeHandle(getPrivate(winP));
    PrivPB.pCount = 1;
    PrivPB.winPtr = (pointer)winP;
    fRelPrivateData(&PrivPB);

    CloseWindow(winP);

    RefPB.pCount = 2;
    RefPB.resID = p->resID;
	retErr = fReleaseResource(&RefPB);

    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)
{
handle          txtData;
long            txtLength;
GrafPortPtr     winP;
privateDataHndl private;
word            retErr;

    winP = (GrafPortPtr) p->windowPtr;
    txtLength = TEGetText(0x0018, &txtData, 0L, 0, 0L, GetCtlHandleFromID(winP, 1L));
    private = getPrivate(winP);
    checkData(p->resID, (**private).data, txtData);

    return 0;
}

/*	========================================================================
 */
word REM_EVENT(fEventRecPtr p)
{
word            i, retErr, menuItem;
GrafPortPtr     winP;

    winP = (GrafPortPtr) fEventPtr->wmTaskData;

    switch (p->taskCode) {

    case app3Evt:
        SetWTitle(getTitle(rPString, p->resID), winP);
        break;

    default:
        break;
    }

    return  0;
}

/*	========================================================================
 */
word REM_ACTIVATE(fActivateRecPtr p)
{
handle          txtData;
GrafPortPtr     winP;
long    txtLength;
privateDataHndl private;


	if (p->fFlag & F_ACTIVATE)
		p->fFlag |= (F_CLIP + F_CLEAR + F_MENUAPPLY);
    else {
        winP = (GrafPortPtr) fEventPtr->wmTaskData;
        txtLength = TEGetText(0x0018, &txtData, 0L, 0, 0L, GetCtlHandleFromID(winP, 1L));
        private = getPrivate(winP);
        checkData(p->resID, (**private).data, txtData);
    }
    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.
*/
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.
*/
word REM_VIDMODE(fVidModeRecPtr p)
{

GrafPortPtr     winP;
Handle          ctlH;
long            txtLength;
ParamListHndl   winH;
handle          txtData;
privateDataHndl private;

    (pointer) winP = p->windowPtr;
    private = getPrivate(winP);
    ctlH = (handle) GetCtlHandleFromID(winP, 1L);
    txtLength = TEGetText(0x0018, &txtData, 0L, 0, 0L, ctlH);
    HideControl(ctlH);
    DisposeControl(ctlH);

    (CtlRecHndl) ctlH = NewControl2(winP, 1, (long) (**private).ctlH);

    TESetText(teTextIsHandle + teDataIsPString, txtData, 0L, 0, 0L, ctlH);
    SetFrameColor(p->wColorPtr, p->windowPtr);
    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!

*/
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.
*/
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 it is needed - this is
    used by REM_CLOSE, REM_WRITE and REM_ACTIVATE.
*/

void checkData(long resID, handle resH, handle txtH)
{
int     i, changed;
long    size;
char    *resP, *txtP;

    HLock(resH);
    resP = *resH;
    HLock(txtH);
    txtP = *txtH;
    changed = FALSE;
    if (resP[0] != txtP[0])
        changed = TRUE;
    else {
        for(i = resP[0]; i > 0; i--) {
            if (resP[i] != txtP[i]) {
                changed = TRUE;
                break;
            }
        }
    }
    if (changed) {
        size = GetHandleSize(txtH);
        HUnlock(resH);
        SetHandleSize(size, resH);
        HLock(resH);
        HandToHand(txtH, resH, size);

        RefPB.pCount = 2;
        RefPB.resType = rPString;
        RefPB.resID = resID;
        fWriteResource(&RefPB);
    }
    HUnlock(resH);
    DisposeHandle(txtH);

}

/*--------------------------------------
    getPrivate is an example of how a common callback can be optimized to
    clean up the functions that use it.
    PrivPB is global
*/
privateDataHndl getPrivate(GrafPortPtr winP)
{
    PrivPB.pCount = 4;
    (GrafPortPtr) PrivPB.winPtr = winP;
    fGetPrivateData(&PrivPB);
    return (privateDataHndl) PrivPB.Data;
}

handle getTitle (word type, long ID)
{
long    **title;
word    retErr;
long    size;

	(handle) title = NewHandle(128L, fUserID, 0x8018, 0L);
	NamePB.pCount = 3;
	NamePB.resType = rPString;
	NamePB.resID = ID;
	NamePB.resName = (Pointer) *title;
	retErr = fGetWindowTitle(&NamePB);
    size = **title & 0xFFL;
    SetHandleSize(size, title);
    return (handle) (title | 0x80000000L);
}
