{ $keep 'o/pstring' $rangecheck- $names- $list- $optimize -1 }

{
    Foundation Resource Editor Module Example - Pascal String

    Adapted for ORCA/Pascal by Mark Collins
	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.
}

UNIT PStringRem;

INTERFACE

USES Common, EventMgr, QuickDrawII, MemoryMgr, MscToolSet, WindowMgr,
     ControlMgr, LineEdit, IntegerMath, ResourceMgr, TextEdit, Foundation;

{ 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. }

TYPE
    privateData = record
        data :      handle;     {the handle to the resource being edited}
        ctlH:	handle;     {a quick way to access our window title}
        end;

    privateDataPtr = ^privateData;
    privateDataHndl = ^privateDataPtr;

{ Additional types to supplement handle referencing not found in the
  current interfaces }

    paramListHndl = ^paramListPtr;
    PStrPtr  = ^PString;

VAR
    edResFileID :   integer;	{editor resource file}
    shResFileID :   integer;	{shell resource file}
    fdResFileID :   integer;	{users Foundation.Data resource file}
    PrivPB :        fPrivateDataRec;
    DataPB :        fResDataRec;
    Data2PB :       fResData2Rec;
    NamePB :        fResNameRec;
    RefPB :         fResRefRec;
    CopyPB :        fResCopyRec;
  { LinkPB :        fResLinkRec;	    these parameter blocks are
    ConvertPB :     fResConverterRec	not used in this
    FilePB :        fGetFileRec;	    Pascal String sample Editor }


{ these external variables are declared in Foundation interface
    fEventPtr:      eventRecPtr;
    fWinProc:       longint;
    fUserID:        integer;
    remHelpID:      longint;                        }

{ Forward Declarations- for items called outside this routine }

FUNCTION REM_Open (p: fOpenRecPtr): integer;
FUNCTION REM_Close(p: fCloseRecPtr): integer;
FUNCTION REM_Write(p: fCloseRecPtr): integer;
FUNCTION REM_Event(p: fEventRecPtr): integer;
FUNCTION REM_Activate(p: fEventRecPtr): integer;
FUNCTION REM_GetLink(p: fLinkRecPtr): integer;
FUNCTION REM_VidMode(p: fVidModeRecPtr): integer;
FUNCTION REM_Print (p: fCloseRecPtr): integer;
FUNCTION REM_STARTUP(p: fStartStopRecPtr):integer;
FUNCTION REM_SHUTDOWN(p: fStartStopRecPtr): integer;

{ External Declarations-  the Shell Callbacks-  we don't use anywhere
  near all of them here, but here they are anyway }

FUNCTION fAddPrivateData(p:fPrivateDataRecPtr): integer;  EXTERNAL;
FUNCTION fAddResource(p:fResDataRecPtr): integer;  EXTERNAL;
FUNCTION fBuildLinkHand(p:fResLinkRecPtr): integer;  EXTERNAL;
FUNCTION fCopyResource(p:fResCopyRecPtr): integer;  EXTERNAL;
FUNCTION fCountResources(p:fResData2RecPtr): integer;  EXTERNAL;
FUNCTION fDetachResource(p:fResRefRecPtr): integer;  EXTERNAL;
FUNCTION fDisposeLinkHand(p:fResLinkRecPtr): integer;  EXTERNAL;
FUNCTION fExtendLinkHand(p:fResLinkRecPtr): integer;  EXTERNAL;
FUNCTION fGetEditorStatus(p:fResData2RecPtr): integer;  EXTERNAL;
FUNCTION fGetIndResource(p:fResData2RecPtr): integer;  EXTERNAL;
FUNCTION fGetPrivateData(p:fPrivateDataRecPtr): integer;  EXTERNAL;
FUNCTION fGetResourceAttr(p:fResDataRecPtr): integer;  EXTERNAL;
FUNCTION fGetResourceName(p:fResNameRecPtr): integer;  EXTERNAL;
FUNCTION fGetResourceSize(p:fResData2RecPtr): integer;  EXTERNAL;
FUNCTION fGetScrap(p:fResDataRecPtr): integer;  EXTERNAL;
FUNCTION fGetStdTypeName(p:fResNameRecPtr): integer;  EXTERNAL;
FUNCTION fGetWindowTitle(p:fResNameRecPtr): integer;  EXTERNAL;
FUNCTION fLoadFileData(p:fLoadFileRecPtr): integer;  EXTERNAL;
FUNCTION fLoadResource(p:fResDataRecPtr): integer;  EXTERNAL;
FUNCTION fQueryLinkHand(p:fResLinkRecPtr): integer;  EXTERNAL;
FUNCTION fRelPrivateData(p:fPrivateDataRecPtr): integer;  EXTERNAL;
FUNCTION fReleaseResource(p:fResRefRecPtr): integer;  EXTERNAL;
FUNCTION fRemoveResource(p:fResRefRecPtr): integer;  EXTERNAL;
FUNCTION fRequestEdit(p:fResDataRecPtr): integer;  EXTERNAL;
FUNCTION fResourceConverter(p:fResConverterRecPtr): integer;  EXTERNAL;
FUNCTION fSaveScrap(p:fResDataRecPtr): integer;  EXTERNAL;
FUNCTION fSelectorMenu(p:fResDataRecPtr): integer;  EXTERNAL;
FUNCTION fSelectorRequest(p:fResDataRecPtr): integer;  EXTERNAL;
FUNCTION fSetPrivateData(p:fPrivateDataRecPtr): integer;  EXTERNAL;
FUNCTION fSetResourceAttr(p:fResDataRecPtr): integer;  EXTERNAL;
FUNCTION fSetResourceID(p:fResData2RecPtr): integer;  EXTERNAL;
FUNCTION fSetResourceName(p:fResNameRecPtr): integer;  EXTERNAL;
FUNCTION fSetStdTypeName(p:fResNameRecPtr): integer; EXTERNAL;
FUNCTION fSpecialMagic(p:fResDataRecPtr): integer;  EXTERNAL;
FUNCTION fSpinCursor(p: longint): integer;   EXTERNAL;
FUNCTION fUniqueResourceID(p:fResRefRecPtr): integer;  EXTERNAL;
FUNCTION fWriteResource(p:fResRefRecPtr): integer;  EXTERNAL;

IMPLEMENTATION

{ --------------------------------------
    DrawProc
    This is a simple window draw proc for our editor.  It simply draws
    the textedit control.  That's all there is to it.
}

{$ToolParms+,DataBank+}
PROCEDURE DrawProc;
BEGIN
    DrawControls(GetPort);
END;
{$ToolParms-,DataBank-}



{  --------------------------------------
    checkData - writes a pstring resource only if it is needed - this is
    used by REM_Close, REM_Write and REM_Activate.
}

PROCEDURE checkData(resID: longint; resH: handle; txtH: handle);

VAR
    i, changed: integer;
    retVal:     integer;
    size:       longInt;
    resP, txtP: PStrPtr;

BEGIN
    HLock(resH);
    resP := PStrPtr(resH^);
    HLock(txtH);
    txtP := PStrPtr(txtH^);

    IF txtP^ <> resP^ THEN BEGIN
        size := GetHandleSize(txtH);
        HUnlock(resH);
        SetHandleSize(size, resH);
        HLock(resH);
        HandToHand(txtH, resH, size);

        RefPB.pCount := 2;
        RefPB.resType := rPString;
        RefPB.resID := resID;
        retVal := fWriteResource(@RefPB);
        END;
    HUnlock(resH);
    DisposeHandle(txtH);
    END;


{ --------------------------------------
    getPrivate is an example of how a common callback can be optimized to
    clean up the functions that use it.
    PrivPB is global
}

FUNCTION getPrivate(winP : GrafPortPtr): privateDataHndl;
VAR
    retVal:     integer;

BEGIN
    PrivPB.pCount := 4;
    PrivPB.winPtr := winP;
    retVal := fGetPrivateData(@PrivPB);
    getPrivate := privateDataHndl(PrivPB.Data);
END;

FUNCTION getTitle (rType:integer; ID: longint): handle;

VAR
    title:  handle;
    retErr:  integer;
    size:   longint;
BEGIN
	title := NewHandle(128, fUserID, $8018, ptr(0));
	NamePB.pCount := 3;
	NamePB.resType := rType;
	NamePB.resID := ID;
	NamePB.resName := pstringptr(title^);
	retErr := fGetWindowTitle(@NamePB);
    size := title^^ & $FF;
    SetHandleSize(size, title);
    getTitle := handle(longint(title) | $80000000);
END;





{	========================================================================
    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:

    fOpenRec = record
        resType :   integer;    must be rPString for this example editor
        resID :     longint;    $00000000 ... $07ffffff
        fFlag :     integer;    $0200 Data Valid, $0100 Silent, $0300 Both
        Data :      Handle;     If Data Valid, this is a pstring handle
        wColorPtr : ptr;        The color table for the active workfile
		end;

    fOpenRecPtr = ^fOpenRec;}


FUNCTION REM_Open;

LABEL   1;          {This is a quick cheat to emulate the RETURN}
                    {that is available in C- to keep the code as similar}
                    {as possible}

VAR
    retVal :    integer;
    retErr :    integer;
    Silent :    integer;
    title :     handle;
    ctlH :      handle;
    private :   privateDataHndl;
    tempH:      paramListHndl;
    winP :      GrafPortPtr;

{first see if we edit this type...
 (a multi-type editor will have to case those types it supports)}

BEGIN
    IF p^.resType <> rPString THEN BEGIN    {if not, get ready to blow out}
        REM_Open := resInvalidTypeOrID;      {of here}
        GOTO 1;
    END;

{		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 = 0) OR (p^.fFlag & (F_OPENSILENT + F_OPENDATA) <> 0) THEN BEGIN

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

        IF (p^.fFlag & F_OPENDATA) <> 0 THEN  { data is provided - handle is ours }
            DataPB.resData := p^.Data
        ELSE BEGIN              { no data, load default from editor }
            DataPB.pCount := 6;
            DataPB.resID := $00000001;  {happens to be the default ID}
            DataPB.resFileID := edResFileID;
            retErr := fLoadResource(@DataPB); {we want this one here }

            RefPB.pCount := 3;	{ detach default so we own it}
            RefPB.resID := $00000001;
            RefPB.resFileID := edResFileID;
            retErr := fDetachResource(@RefPB);
        END;
        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) <> 0 THEN BEGIN {this was a silent request}
            REM_Open := 0;
            GOTO 1;                                  {done}
        END;
    END;

{    	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(8, fUserID, $0018, ptr(0)));

    DataPB.pCount := 5;                 {get and lock our window stuff}
    DataPB.resType := rWindParam1;
    DataPB.resID := $00000001;
    DataPB.special := 0;                {load}
    DataPB.resFileID := edResFileID;
    retErr := fSpecialMagic(@DataPB);
    DataPB.special := 1;                {lock}
    retErr := fSpecialMagic(@DataPB);

    tempH := paramListHndl(DataPB.resData);
    private^^.ctlH := handle(tempH^^.wStorage);

    winP := NewWindow2(title^, 0, @DrawProc, ptr(0), 1, DataPB.resData, rWindParam1);
    SetWTitle(getTitle(rPString, p^.resID), winP);
    SetPort(winP);
    PrivPB.pCount := 7;                 {setup the window refCon for the shell}
    PrivPB.winPtr := winP;
    PrivPB.remTaskMask := 0;            {use standard task mask}
    PrivPB.remSignature := 0;           {personal signature - no significance here}
    PrivPB.Data := Handle(private);
    PrivPB.groupSignature := $0020;
    PrivPB.resType := rPString;
    PrivPB.resID := p^.resID;
    retErr := 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, 1));
    TESetText($0008, DataPB.resData, 0, 0, 0, ctlH);

    SetFrameColor(p^.wColorPtr, winP);
    ShowWindow(winP);
    REM_Open := 0;

1:;
END;

{ ========================================================================
  REM_Close updates the resource if it has changed, and closes up shop.

  The REM_Open parameter block looks like this:

    fCloseRec = record
        resType:    integer;    rPString
        resID:      longint;    $00000001 ... $07ffffff
        fFlag:      integer;    no input
        windowPtr:  pointer;    editor's GrafPortPtr
        END;

    fCloseRecPtr = ^fCloseRec;
    --------------------------------------------------------------- }

FUNCTION REM_Close;

VAR
    retErr:     integer;
    resData:    handle;
    txtData :   handle;
    txtLength:  longint;
    winP:       GrafPortPtr;
    private:    privateDataHndl;

BEGIN
    winP := GrafPortPtr(p^.windowPtr);
    txtLength := TEGetText($0018, @txtData, 0, 0, 0, GetCtlHandleFromID(p^.windowPtr, 1));
    private := getPrivate(winP);
    resData := private^^.data;
    checkData(p^.resID, resData, txtData);

    DisposeHandle(getPrivate(winP));
    PrivPB.pCount := 1;
    PrivPB.winPtr := pointer(winP);
    retErr := fRelPrivateData(@PrivPB);
    CloseWindow(winP);

    RefPB.pCount := 2;
    RefPB.resID := p^.resID;
	retErr := fReleaseResource(@RefPB);

    REM_Close := 0;
END;

FUNCTION REM_Write;
{    REM_WRITE updates the resource if it has changed.

    The REM_CLOSE parameter block is used by REM_WRITE too.}

VAR
    txtData:    handle;
    resData:    handle;
    txtLength:  longint;
    winP:       GrafPortPtr;
    private:    privateDataHndl;
    retErr:     integer;

BEGIN

    winP := GrafPortPtr(p^.windowPtr);
    txtLength := TEGetText($0018, @txtData, 0, 0, 0, GetCtlHandleFromID(p^.windowPtr, 1));
    private := getPrivate(winP);
    resData := private^^.data;
    checkData(p^.resID, resData, txtData);
    REM_Write := 0;
END;


FUNCTION REM_Event;
VAR
    retErr:     integer;
    title:      handle;
    private:    privateDataHndl;
    winP:       GrafPortPtr;

BEGIN

    winP := grafPortPtr(fEventPtr^.taskData);

    if p^.taskCode = app3Evt THEN BEGIN
        SetWTitle(getTitle(rPString, p^.resID), winP);
     END;

    REM_Event := 0;
END;



FUNCTION REM_Activate;
VAR
    txtData:    handle;
    resData:    handle;
    txtLength:  longint;
    private:    privateDataHndl;
    winP:       GrafPortPtr;

BEGIN
	IF ((p^.fFlag & F_ACTIVATE) <> 0) THEN
		p^.fFlag := p^.fFlag | (F_CLIP + F_CLEAR + F_MENUAPPLY)
    ELSE BEGIN
        winP := GrafPortPtr (fEventPtr^.taskData);
        txtLength := TEGetText($0018, @txtData, 0, 0, 0, GetCtlHandleFromID(winP, 1));
        private := getPrivate(winP);
        checkData(p^.resID, private^^.data, txtData);
    END;
    REM_Activate := 0;
END;

{	========================================================================
    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.
}

FUNCTION REM_GetLink;

BEGIN
    REM_GetLink := 0;
END;

{	========================================================================
    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.
}

FUNCTION REM_VidMode;
VAR
    winP:       grafPortPtr;
    ctlH:       ctlRecHndl;
    txtLength:  longint;
    winH:       paramListHndl;
    txtData:    handle;
    private:     privateDataHndl;

BEGIN
    winP := grafPortPtr(p^.windowPtr);
    private := getPrivate(winP);
    ctlH := GetCtlHandleFromID(winP, 1);
    txtLength := TEGetText($0018, @txtData, 0, 0, 0, ctlH);
    HideControl(ctlH);
    DisposeControl(ctlH);
    ctlH := NewControl2(winP, 1, private^^.ctlH);


    TESetText($0008, txtData, 0, 0, 0, ctlH);

    SetFrameColor(p^.wColorPtr, p^.windowPtr);
    REM_VidMode := 0;
END;

{	========================================================================
    REM_Print must be supported entirely by the editor.  Why would we want
    to print a Pascal String?  We don't.
}

FUNCTION REM_Print;

BEGIN
    REM_Print := 0;
END;

{	========================================================================
    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!
}

FUNCTION REM_STARTUP;
BEGIN
    fUserID := p^.edUserID;
    edResFileID := p^.edResFileID;
    shResFileID := p^.shResFileID;
    fdResFileID := p^.fdResFileID;

    remHelpID := 1; {stuff a rText ID into the editors header help field }

    REM_STARTUP := 0;
END;

{	========================================================================
    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.
}

FUNCTION REM_SHUTDOWN;

BEGIN
	DisposeAll(fUserID);	{ before assuming that this simplicity is 	}
    REM_SHUTDOWN := 0;      { all you need, read TBR-1, page 12-23!	}
END;
END.
