{ $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

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

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;         {the master TEControl Handle}
        end;

    privateDataPtr = ^privateData;
    privateDataHndl = ^privateDataPtr;

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

    paramListHndl = ^paramListPtr;

VAR
    edResFileID :   integer;	{editor resource file}
    shResFileID :   integer;	{shell resource file}
    fdResFileID :   integer;	{users Foundation.Data resource file}


{ 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: fActivateRecPtr): 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 }


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

{  --------------------------------------
    SaveData - writes a pstring resource whether its needed or not - this is
    used by REM_Close, REM_Write and REM_Activate.
}

PROCEDURE SaveData(winP: GrafPortPtr; resID: longint);

VAR
    size:               longInt;
    resData, txtData:   handle;
    private:            privateDataHndl;

BEGIN

    private := privateDataHndl(fGetPrivateData2(winP, longintPtr(0), integerPtr(0)));
    resData := private^^.data;
    HLock(resData);

    size := TEGetText($0018, @txtData, 0, 0, 0, GetCtlHandleFromID(winP, 1));
    HLock(txtData);

    size := GetHandleSize(txtData);
    HUnlock(resData);
    SetHandleSize(size, resData);
    HLock(resData);
    HandToHand(txtData, resData, size);
    fWriteResource2(rPString, resID, 0);

    HUnlock(resData);
    DisposeHandle(txtData);
    END;


PROCEDURE setTitle (winP: GrafPortPtr; ID: longInt);

VAR
    title:  handle;

BEGIN

	title := NewHandle(128, fUserID, $8018, ptr(0));
    fGetWindowTitle2 (rPString, ID, pStringPtr(title^));
    SetHandleSize((title^^ & $FF), title);
    SetWTitle(handle(longint(title) | $80000000), winP);

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
    ctlH :      handle;
    resData :   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

        IF (p^.fFlag & F_OPENDATA) <> 0 THEN  { data is provided - handle is ours }
            resData := p^.Data
        ELSE BEGIN              { no data, load default from editor }
            resData := fLoadResource2 (rPString, 1, integerPtr(0), edResFileID, pStringPtr(0));
            fDetachResource2 (rPString, 1, edResFileID);
        END;
        p^.resID := fAddResource2 (rPString, p^.resID, resData, 0, 0, pStringPtr(0));
        fReleaseResource2 (rPString, p^.resID, 0);


        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)));
    tempH := paramListHndl(fSpecialMagic2 (rWindParam1, 1, Handle(0), 0, edResFileID));
    tempH := paramListHndl(fSpecialMagic2 (rWindParam1, 0, Handle(tempH), 1, edResFileID));
    private^^.ctlH := handle(tempH^^.wStorage);

    winP := NewWindow2(Pointer(0), 0, @DrawProc, ptr(0), 1, tempH, rWindParam1);
    SetTitle(winP, p^.resID);
    SetPort(winP);
    fAddPrivateData2 (winP, 0, 0, Handle(private), $0020, rPString, p^.resID);
    private^^.data := fLoadResource2(rPString, p^.resID, integerPtr(0), 0, pStringPtr(0));


{ 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, private^^.data, 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;

BEGIN
    SaveData(GrafPortPtr(p^.windowPtr), p^.resID);
    DisposeHandle(fGetPrivateData2(p^.windowPtr, longintPtr(0), integerPtr(0)));
    fRelPrivateData2(GrafPortPtr(p^.windowPtr));
    CloseWindow(p^.windowPtr);
    fReleaseResource2(rPString, p^.resID, 0);
    REM_Close := 0;
END;

{	========================================================================
    REM_WRITE updates the resource if it has changed.

    The REM_CLOSE parameter block is used by REM_WRITE too:

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

        fCloseRecPtr = ^fCloseRec;
 }

FUNCTION REM_Write;

BEGIN
    SaveData(GrafPortPtr(p^.windowPtr), p^.resID);
    REM_Write := 0;
END;

{	========================================================================
    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:
        fEventRec = record
            resType:    integer;    - rPString
            resID:      longint;    - $00000001 ... $07ffffff
            fFlag:      integer;    - no input
            taskCode:   integer;    - Event task code
            end;

        fEventRecPtr = ^fEventRec;
}

FUNCTION REM_Event;
VAR
    winP:       GrafPortPtr;

BEGIN

    winP := grafPortPtr(fEventPtr^.taskData);

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

    REM_Event := 0;
END;


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

        fActivateRec = record
            resType:    integer;    - rPString
            resID:      longint;    - $00000001 ... $07ffffff
            fFlag:      integer;    - $8000 bit is activate
            linkHand:   handle;     - null, used by native selectors only
        end;

    fActivateRecPtr = ^fActivateRec;
}

FUNCTION REM_Activate;
VAR
    winP:       GrafPortPtr;


BEGIN
    winP := GetPort;        {  GrafPortPtr(fEventPtr^.TaskData);  }
	IF ((p^.fFlag & F_ACTIVATE) <> 0) THEN
		p^.fFlag := p^.fFlag | (F_CLIP + F_CLEAR + F_MENUAPPLY)
    ELSE
        SaveData(winP, p^.resID);
    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.

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

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.

    The REM_VIDMODE parameter block looks like this:

        fVidModeRec = record
            resType:    integer;    - rPString
            resID:      longint;    - $00000001 ... $07ffffff
            fFlag:      integer;    - $4000 bit is when entering 640 mode
            windowPtr:  ptr;        - the editor's GrafPortPtr
            wColorPtr:  ptr;        - new mode's standard window color table
            end;

    fVidModeRecPtr = ^fVidModeRec;
}

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

BEGIN
    winP := grafPortPtr(p^.windowPtr);
    ctlH := GetCtlHandleFromID(winP, 1);
    txtLength := TEGetText($0018, @txtData, 0, 0, 0, ctlH);
    HideControl(ctlH);
    DisposeControl(ctlH);
    private := privateDataHndl(fGetPrivateData2 (winP, longintPtr(0), integerPtr(0)));
    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.

    The REM_CLOSE parameter block is used by REM_PRINT too:

    fCloseRec = record
        resType:    integer;    rPString
        resID:      longint;    $00000001 ... $07ffffff
        fFlag:      integer;    PageSetup request if bit 123 ($2000) is set
        windowPtr:  pointer;    editor's GrafPortPtr
        END;

    fCloseRecPtr = ^fCloseRec;
}

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!

    The REM_STARTUP parameter block looks like this:

        fStartStopRec = record
            edUserID:       integer;    -mem manager ID assigned to editor
            edResFileID:    integer;    -editor resource fork ID
            shResFileID:    integer;    -shell resource fork ID
            fdResFileID:    integer;    -Foundation.Data resource fork ID
            end;

    fStartStopRecPtr = ^fStartStopRec;
}

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.

    The REM_SHUTDOWN parameter block looks like this:

        fStartStopRec = record
            edUserID:       integer;    - mem manager ID assigned to editor
            edResFileID:    integer;    - editor resource fork ID
            shResFileID:    integer;    - null
            fdResFileID:    integer;    - null
            end;
    fStartStopRecPtr = ^fStartStopRec;
}

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.
