/*************************************************************************
  *                                                                      *
  *                            Preliminary                               *
  *                        Amiga AppShell (tm)                           *
  *                                                                      *
  *  Copyright (c) 1990,1991 Commodore-Amiga, Inc. All Rights Reserved.  *
  *                                                                      *
  *   This software and information is proprietary, preliminary, and     *
  *   subject to change without notice.                                  *
  *                                                                      *
  *                            DISCLAIMER                                *
  *                                                                      *
  *   THIS SOFTWARE IS PROVIDED "AS IS".                                 *
  *   NO REPRESENTATIONS OR WARRANTIES ARE MADE WITH RESPECT TO THE      *
  *   ACCURACY, RELIABILITY, PERFORMANCE, CURRENTNESS, OR OPERATION      *
  *   OF THIS SOFTWARE, AND ALL USE IS AT YOUR OWN RISK.                 *
  *   NEITHER COMMODORE NOR THE AUTHORS ASSUME ANY RESPONSIBILITY OR     *
  *   LIABILITY WHATSOEVER WITH RESPECT TO YOUR USE OF THIS SOFTWARE.    *
  *                                                                      *
  *                          Non-Disclosure                              *
  *                                                                      *
  *   This information is not to be disclosed to any other company,      *
  *   individual or party.  Discussion is to be restricted to CBM        *
  *   approved discussion areas, such as the closed conferences on bix;  *
  *   amiga.cert, amiga.com, amiga.beta/appshell.                        *
  *                                                                      *
  ************************************************************************
  */

#include "multiproj.h"

#define	DI(x)	;

void kprintf (void *, ...);

/* Project function table */
struct Funcs PTable[] =
{
 /* Function with an argument template */
    {"New",  PNewFunc,  NewID,},
    {"Open", POpenFunc, OpenID, "FILENAME,FORCE/S", 2L, },
    {"Save", PSaveFunc, SaveID, ",", 0L, },
    {"SaveAs",PSaveAsFunc, SaveAsID, "NAME", 1L, },
    {"About",PAboutFunc, AboutID, },
    {"Quit", PQuitFunc, QuitID, "FORCE/S,ALL/S", 2L, },
    {"Change", ChangeFunc, ChangeID, },

 /* These function can not be accessed by the user */
    {"PInit", PInitFunc, PInitID, NULL, NULL, APSHF_PRIVATE},
    {"OpenMain", OpenMainFunc, OpenMainID, NULL, NULL, APSHF_PRIVATE},
    {"OpenAbout", OpenAboutFunc, OpenAboutID, NULL, NULL, APSHF_PRIVATE},
    {"DropIcon",  DropIconFunc,  DropIconID, NULL, NULL, APSHF_PRIVATE,},
    {"PExit", PExitFunc, PExitID, NULL, NULL, APSHF_PRIVATE},

 /* Marks the end of the array */
    {NULL, NO_FUNCTION,}
};

/* All text used in the application must be defined in a text array.  Then
 * referred to by numeric ID. */
STRPTR Def_Text[] =
{
 /* Padding */
    "",
    "Loading...",

 /* Main window menus */
    " TITLE PROJECT  LABEL Project",
    "  ITEM NEW      LABEL New                  KEY N CMD New",
    "  ITEM OPEN     LABEL \"Open...\"          KEY O CMD Open",
    "   BAR",
    "  ITEM SAVE     LABEL Save                 KEY S CMD Save",
    "  ITEM SAVEAS   LABEL \"Save As...\"       KEY A CMD SaveAs",
    "   BAR",
    "  ITEM CLOSE    LABEL Close                      CMD Quit",
    "   BAR",
    "  ITEM ABOUT    LABEL \"About...\"               CMD About",
    "   BAR",
    "  ITEM QUIT     LABEL \"Quit MultiProj\"   KEY Q CMD \"Quit All\"",

    " TITLE EXTRAS   LABEL Extras",
    "  ITEM CHANGE   LABEL \"Change Project\"   KEY / CMD Change",
    "  ITEM CMDSHELL LABEL \"Command Shell...\" KEY . CMD CmdShell",
    "   END",

    "Aborted by user",
#define	PERR_USER_ABORT	18

    "No name provided",
#define	PERR_NO_NAME	19

    "Couldn't load %s",
#define	PERR_CANT_LOAD	20

    "Requires project",
#define	PERR_NO_PROJECT	21

    "Couldn't create %s",
#define	PERR_CANT_CREATE 22

    "Couldn't write to %s",
#define	PERR_CANT_WRITE	23

 /* NULL termination is required */
    NULL
};

/* The following tag array consists of GadTool tags for Window Environment
 * objects. */
struct TagItem td_tags[] =
{
    {APSH_GTFlags, PLACETEXT_IN},
    {TAG_DONE,},
};

/* These are the objects that are going to be within our main window.
 * Note that the window itself is an object, and receives the SAME name
 * as in the WindowEnvironment tag list, APSH_NameTag.  If you want
 * to specify any window tags, attach them to the window object. */
struct Object objects[] =
{
    {&objects[1], 0, 0, OBJ_Window, NULL, NULL, NULL, "Main", 1L,
     {0, 0, 320, 0}, },

    {NULL,        0, 0, OBJ_Text,   NULL, NULL, NULL, "Name", 0L,
     {8, 4, 350, 10}, td_tags, },
};

/* This is a Window Environment tag list.  It describes a window. */
struct TagItem mainenv[] =
{
    {APSH_NameTag, (ULONG) "Main"},
    {APSH_Objects, (ULONG) objects},
    {APSH_TTMenu, 2L},
    {APSH_WinAOpen, OpenMainID},
    {APSH_RefreshData, OpenMainID},
    {TAG_DONE,}
};

 /* Command Shell user interface environment specification array */
struct TagItem Handle_DOS[] =
{
 /* Presence of this tag, makes the Command Shell to stay closed until
  * the user sends a CMDSHELL OPEN command. */
    {APSH_Status, APSHP_INACTIVE},
    {APSH_Rating, APSH_REQUIRED},
    {TAG_DONE,}
};

/* These tags describe the Intuition user interface. */
struct TagItem Handle_IDCMP[] =
{
    {APSH_Rating, APSH_REQUIRED},
    {TAG_DONE,}
};

 /* ARexx user interface environment specification array */
static struct TagItem Handle_AREXX[] =
{
    {APSH_Extens, (ULONG) "skel"},
    {APSH_Rating, APSH_OPTIONAL},
    {TAG_DONE,}
};

/* These tags describe the Simple IPC user interface. */
static struct TagItem Handle_SIPC[] =
{
    {APSH_Rating, APSH_REQUIRED},
    {TAG_DONE,}
};

struct TagItem appwinenv[] =
{
    {APSH_NameTag, (ULONG) "MAIN"},	/* Window to make into AppWindow */
    {APSH_AppDDrop, DropIconID},	/* function after drop */
    {APSH_CmdFlags, APSH_WBF_NOLIST},	/* Don't add the icons to any list */
    {TAG_DONE,}
};

/* These tags describe the Workbench user interface. */
struct TagItem Handle_WB[] =
{
    {APSH_AppWindowEnv, (ULONG) appwinenv},
    {APSH_Rating, APSH_OPTIONAL},
    {TAG_DONE,}
};

/* Application Environment:
 * Tell about our slave project application */
struct TagItem Project_App[] =
{
 /* About the application */
    {APSH_AppName, (ULONG) APPNAME},
    {APSH_AppVersion, (ULONG) APPVERS},
    {APSH_AppCopyright, (ULONG) APPCOPY},
    {APSH_AppAuthor, (ULONG) APPAUTH},

 /* Specify the application function table */
    {APSH_FuncTable, (ULONG) PTable},

 /* Specify the application text table */
    {APSH_DefText, (ULONG) Def_Text},

 /* Tell how memory we need for our own data */
    {APSH_UserDataSize, sizeof (struct AppData)},

 /* Must always specify the SIPC user interface */
    {APSH_AddSIPC_UI, (ULONG) Handle_SIPC},

 /* Add an ARexx user interface */
    {APSH_AddARexx_UI, (ULONG) Handle_AREXX},

 /* Add a Command Shell user interface */
    {APSH_AddCmdShell_UI, (ULONG) Handle_DOS},

 /* Add an Intuition user interface */
    {APSH_AddIntui_UI, (ULONG) Handle_IDCMP},

 /* Add a Workbench user interface */
    {APSH_AddWB_UI, (ULONG) Handle_WB},

 /* Specify a custom initialization routine */
    {APSH_AppInit, PInitID},
    {APSH_AppExit, PExitID},

    {TAG_DONE,}
};

/* local functions */
LONG save_project (struct AppInfo *ai, STRPTR name);
LONG load_project (struct AppInfo *ai, STRPTR name);
SHORT myEasyRequest (struct Window *, UBYTE *, ULONG *, UBYTE *, UBYTE *, int, ...);
BOOL CheckForChanges (struct AppInfo * ai, struct ProjNode * pn);
LONG FixFileAndPath (struct FileRequester *rf, STRPTR name);

 /* Sample initialization function */
VOID PInitFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct Project *p = &(ai->ai_Project);
    struct TagItem *attrs = af->af_Attrs;
    extern struct TagItem mainenv[];
    struct ProjNode *pn;
    WORD i;

    /* Get a pointer to our project node */
    DI (kprintf ("PInitFunc enter\n"));
    if (pn = (struct ProjNode *) GetTagData (APSH_ProjInfo, NULL, attrs))
    {
	/* Remember the project node */
	p->p_CurProj = ad->ad_PN = pn;

	/* Get a pointer to our Master's AppInfo */
	ad->ad_MAI = (struct AppInfo *) GetTagData (APSH_AppHandle, NULL, attrs);

	/* Allocate the file requesters */
	for (i = 0; i < FR_MAX; i++)
	{
	    ad->ad_FR[i] = AllocAslRequest (ASL_FileRequest, NULL);
	}

	/* Unset the file requester positioning */
	ad->ad_Width = (-1);

	/* Initialize the file requester */
	FixFileAndPath (ad->ad_FR[FR_PROJ], pn->pn_ProjPath);

	/* Open the Main window */
	DI (kprintf ("Open Main window\n"));
	HandlerFunc (ai,
		     APSH_Handler, "IDCMP",
		     APSH_Command, APSH_MH_OPEN,
		     APSH_WindowEnv, (ULONG) mainenv,
		     TAG_DONE);
    }
    else
    {
	/* We require a project node */
	ai->ai_Pri_Ret = RETURN_ERROR;
	ai->ai_Sec_Ret = PERR_NO_PROJECT;
	ai->ai_TextRtn =
	  PrepText (ai, APSH_USER_ID, ai->ai_Sec_Ret, NULL);
    }
    DI (kprintf ("PInitFunc exit\n"));
}

/* This function gets called after the main event processing loop exits,
 * and before deallocating all the user interfaces.  It is specified by the
 * APSH_AppExit tag in the Application Description tag array.  Note that the
 * AppData structure which has been allocated for us, is deallocated by
 * AppShell after freeing the user interfaces. */

VOID
PExitFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    WORD i;

    if (ad)
    {
	/* Free the file requesters */
	for (i = 0; i < FR_MAX; i++)
	{
	    /* Do we have a file requester? */
	    if (ad->ad_FR[i])
	    {
		/* Free the file requester */
		FreeFileRequest (ad->ad_FR[i]);
		ad->ad_FR[i] = NULL;
	    }
	}
    }
}

/* This function is called every time the Main window is opened.  It
 * was specified by using the APSH_WinAOpen tag in the Main Window
 * Environemt tag array.  I'm also 'cheating' and calling it everytime
 * I want to refresh the window and graphics. */

VOID OpenMainFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct Window *win;
    struct Gadget *gad;

    /* Update the mode gadget */
    if (APSHGetGadgetInfo (ai, "Main", "Name", (ULONG *) & win, (ULONG *) & gad))
    {
	/* Update the string gadget */
	GT_SetGadgetAttrs (gad, win, NULL,
			   GTTX_Text, ad->ad_PN->pn_ProjPath,
			   TAG_DONE);
    }

    /* Set the window title */
    sprintf (ad->ad_Tmp, "window title \"%s\"", ad->ad_PN->pn_Name);
    PerfFunc (ai, NULL, ad->ad_Tmp, NULL);
}

 /* Create a new project work area */
VOID PNewFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct MsgHandler *mh;

    /* Make sure we have a master */
    if (ad->ad_MAI)
    {
	/* See if they have a SIPC port */
	if (mh = HandlerData (ad->ad_MAI, APSH_Handler, "SIPC", TAG_DONE))
	{
	    /* Tell our master to open a new application */
	    HandlerFunc (ai,
			 APSH_Handler, "SIPC",
			 APSH_Command, AH_SENDCMD,
			 APSH_NameTag, mh->mh_PortName,
			 APSH_CmdString, (ULONG) "New",
			 TAG_DONE);
	}
    }
}

 /* Open a project into the existing work area */
VOID POpenFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct FileRequester *fr = ad->ad_FR[FR_PROJ];
    struct TagItem *attrs = af->af_Attrs;
    struct Funcs *f;
    STRPTR name = NULL;
    BOOL cancel = FALSE;
    BOOL force = FALSE;

    /* set the wait pointer */
    APSHSetWaitPointer (ai, NULL);

    /* See if we have a parsed command */
    if (f = af->af_FE)
    {
	/* See if they passed a file name */
	name = (STRPTR) f->fe_Options[0];

	/* Did they set the force switch? */
	force = (BOOL) f->fe_Options[1];
    }
    else
    {
	/* See if they passed a name with a tag */
	name = (STRPTR) GetTagData (APSH_NameTag, NULL, attrs);
    }

    /* Is there an existing project? */
    if (!force)
    {
	BOOL cancel = FALSE;

	/* Prompt user */
	if (cancel = CheckForChanges (ai, ad->ad_PN))
	{
	    return;
	}
    }

    /* Do we need to prompt for a name? */
    if (!(name))
    {
	/* Do we have a file requester? */
	if (fr)
	{
	    struct TagItem tg[10];
	    struct Window *win = NULL;

	    /* Get the window pointer */
	    APSHGetWindowInfo (ai, "Main", (ULONG *) & win);

	    /* Initialize the file requester position */
	    if (ad->ad_Width == (-1))
	    {
		ad->ad_LeftEdge = (win) ? (win->BorderLeft + 1) : 0;
		ad->ad_TopEdge = (win) ? win->BorderTop : 0;
		ad->ad_Width = 320;
		ad->ad_Height = MAX (((win) ? (win->Height - (win->BorderTop + win->BorderBottom)) : 150), 140);
	    }

	    /* Set the file requester position */
	    tg[4].ti_Tag = ASL_LeftEdge;
	    tg[4].ti_Data = (ULONG) (win) ? (win->LeftEdge + ad->ad_LeftEdge) : ad->ad_LeftEdge;
	    tg[5].ti_Tag = ASL_TopEdge;
	    tg[5].ti_Data = (ULONG) (win) ? (win->TopEdge + ad->ad_TopEdge) : ad->ad_TopEdge;
	    tg[6].ti_Tag = ASL_Width;
	    tg[6].ti_Data = (ULONG) ad->ad_Width;
	    tg[7].ti_Tag = ASL_Height;
	    tg[7].ti_Data = (ULONG) ad->ad_Height;

	    tg[0].ti_Tag = ASL_Hail;
	    tg[0].ti_Data = (ULONG) "Select File to Open";
	    tg[1].ti_Tag = ASL_FuncFlags;
	    tg[1].ti_Data = NULL;
	    tg[2].ti_Tag = ASL_OKText;
	    tg[2].ti_Data = (ULONG) "Open";
	    tg[3].ti_Tag = ASL_Window;
	    tg[3].ti_Data = (ULONG) win;
	    tg[8].ti_Tag = ASL_Pattern;
	    tg[8].ti_Data = (ULONG) "~(#?.info)";
	    tg[9].ti_Tag = TAG_DONE;

	    /* Request the name */
	    if (AslRequest (fr, tg))
	    {
		/* Fix the entries */
		FixFileAndPath (fr, NULL);
		strcpy (ad->ad_Name, fr->rf_Dir);
		AddPart (ad->ad_Name, fr->rf_File, 510);

		/* See if a name was entered */
		if (FixFileAndPath (fr, ad->ad_Name))
		{
		    name = ad->ad_Name;
		}
	    }
	    else
	    {
		/* User aborted requester */
		ai->ai_Pri_Ret = RETURN_WARN;
		ai->ai_Sec_Ret = PERR_USER_ABORT;
		ai->ai_TextRtn =
		  PrepText (ai, APSH_USER_ID, PERR_USER_ABORT, NULL);

		/* Canceled */
		cancel = TRUE;
	    }

	    /* Remember where they moved it */
	    ad->ad_LeftEdge = fr->rf_LeftEdge - ((win) ? win->LeftEdge : 0);
	    ad->ad_TopEdge = fr->rf_TopEdge - ((win) ? win->TopEdge : 0);
	    ad->ad_Width = fr->rf_Width;
	    ad->ad_Height = fr->rf_Height;
	}
    }

    /* Do we have a file name */
    if (!cancel && name)
    {
	/* LOAD THE PROJECT HERE */
	if (load_project (ai, name))
	{
	    UBYTE node[128];
	    BPTR lock;

	    /* Parse the name */
	    stcgfp (ad->ad_Tmp, name);
	    stcgfn (node, name);

	    /* Get a lock on the directory */
	    if (lock = Lock (ad->ad_Tmp, ACCESS_READ))
	    {
		struct WBArg wbarg;

		/* Fill in the WBArg */
		wbarg.wa_Lock = lock;
		wbarg.wa_Name = node;

		/* Update the project node */
		UpdateProject (ai, ad->ad_PN, &wbarg);

		/* Unlock our temporary lock */
		UnLock (lock);
	    }

	    /* Clear the change flag */
	    ad->ad_PN->pn_Changed = FALSE;

	    /* Refresh the display */
	    OpenMainFunc (h, ai, af);
	}
    }
    else if (!cancel)
    {
	/* No name given */
	ai->ai_Pri_Ret = RETURN_ERROR;
	ai->ai_Sec_Ret = PERR_NO_NAME;
	ai->ai_TextRtn =
	  PrepText (ai, APSH_USER_ID, PERR_NO_NAME, NULL);
    }

    /* clear the wait pointer */
    APSHClearPointer (ai, NULL);
}

/* This gets called when a Workbench icon is dropped on the main window. */
VOID DropIconFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct TagItem *attrs = af->af_Attrs;
    struct WBArg *wbarg;
    BOOL cancel = FALSE;

    /* Has there been changes? */
    if (cancel = CheckForChanges (ai, ad->ad_PN))
    {
	return;
    }

    if (wbarg = (struct WBArg *) GetTagData (APSH_WBArg, NULL, attrs))
    {
	BPTR lock;

	/* Go to the directory represented by the icon */
	if (lock = CurrentDir (wbarg->wa_Lock))
	{
	    /* Get the name of the directory */
	    NameFromLock (wbarg->wa_Lock, ad->ad_Name, 510);
	    AddPart (ad->ad_Name, wbarg->wa_Name, 510);

	    /* LOAD THE PROJECT HERE */
	    if (load_project (ai, ad->ad_Name))
	    {
		/* Update the project node */
		UpdateProject (ai, ad->ad_PN, wbarg);

		/* Clear the change flag */
		ad->ad_PN->pn_Changed = FALSE;

		/* Refresh the display */
		OpenMainFunc (h, ai, af);
	    }

	    /* Go back home */
	    CurrentDir (lock);
	}
    }
}

 /* Save the current project */
VOID PSaveFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;

    /* Not named yet */
    if ((ad->ad_PN->pn_ProjName == NULL) ||
	(strlen (ad->ad_PN->pn_ProjName) == 0L))
    {
	PSaveAsFunc (h, ai, af);
    }
    /* Is there an existing project? */
    else
    {
	/* Save the file */
	if (save_project (ai, ad->ad_PN->pn_ProjPath))
	{
	    /* Clear the change flag */
	    ad->ad_PN->pn_Changed = FALSE;
	}
    }
}

 /* Save the current project */
VOID PSaveAsFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct FileRequester *fr = ad->ad_FR[FR_PROJ];
    struct TagItem *attrs = af->af_Attrs;
    struct Funcs *f;
    STRPTR name = NULL;
    BOOL cancel = FALSE;

    /* set the wait pointer */
    APSHSetWaitPointer (ai, NULL);

    /* See if we have a parsed command */
    if (f = af->af_FE)
    {
	/* See if they passed a file name */
	name = (STRPTR) f->fe_Options[0];
    }
    else
    {
	/* See if they passed a name with a tag */
	name = (STRPTR) GetTagData (APSH_NameTag, NULL, attrs);
    }

    /* Do we need to prompt for a name? */
    if (!(name))
    {
	/* Do we have a file requester? */
	if (fr)
	{
	    struct TagItem tg[10];
	    struct Window *win = NULL;

	    /* Get the window pointer */
	    APSHGetWindowInfo (ai, "Main", (ULONG *) & win);

	    /* Initialize the file requester position */
	    if (ad->ad_Width == (-1))
	    {
		ad->ad_LeftEdge = (win) ? (win->BorderLeft + 1) : 0;
		ad->ad_TopEdge = (win) ? win->BorderTop : 0;
		ad->ad_Width = 320;
		ad->ad_Height = MAX (((win) ? (win->Height - (win->BorderTop + win->BorderBottom)) : 150), 160);
	    }

	    /* Set the file requester position */
	    tg[4].ti_Tag = ASL_LeftEdge;
	    tg[4].ti_Data = (ULONG) (win) ? (win->LeftEdge + ad->ad_LeftEdge) : ad->ad_LeftEdge;
	    tg[5].ti_Tag = ASL_TopEdge;
	    tg[5].ti_Data = (ULONG) (win) ? (win->TopEdge + ad->ad_TopEdge) : ad->ad_TopEdge;
	    tg[6].ti_Tag = ASL_Width;
	    tg[6].ti_Data = (ULONG) ad->ad_Width;
	    tg[7].ti_Tag = ASL_Height;
	    tg[7].ti_Data = (ULONG) ad->ad_Height;

	    tg[0].ti_Tag = ASL_Hail;
	    tg[0].ti_Data = (ULONG) "Select File to Save";
	    tg[1].ti_Tag = ASL_FuncFlags;
	    tg[1].ti_Data = (FILF_SAVE | FILF_PATGAD);
	    tg[2].ti_Tag = ASL_OKText;
	    tg[2].ti_Data = (ULONG) "Save";
	    tg[3].ti_Tag = ASL_Window;
	    tg[3].ti_Data = (ULONG) win;
	    tg[8].ti_Tag = ASL_Pattern;
	    tg[8].ti_Data = (ULONG) "~(#?.info)";
	    tg[9].ti_Tag = TAG_DONE;

	    /* Request the name */
	    if (AslRequest (fr, tg))
	    {
		/* Fix the entries */
		FixFileAndPath (fr, NULL);
		strcpy (ad->ad_Name, fr->rf_Dir);
		AddPart (ad->ad_Name, fr->rf_File, 510);

		/* See if a name was entered */
		if (FixFileAndPath (fr, ad->ad_Name))
		{
		    name = ad->ad_Name;
		}
	    }
	    else
	    {
		/* User aborted requester */
		ai->ai_Pri_Ret = RETURN_WARN;
		ai->ai_Sec_Ret = PERR_USER_ABORT;
		ai->ai_TextRtn =
		  PrepText (ai, APSH_USER_ID, ai->ai_Sec_Ret, NULL);

		/* Canceled */
		cancel = TRUE;
	    }

	    /* Remember where they moved it */
	    ad->ad_LeftEdge = fr->rf_LeftEdge - ((win) ? win->LeftEdge : 0);
	    ad->ad_TopEdge = fr->rf_TopEdge - ((win) ? win->TopEdge : 0);
	    ad->ad_Width = fr->rf_Width;
	    ad->ad_Height = fr->rf_Height;
	}
    }

    /* Do we have a file name */
    if (!cancel && name)
    {
	/* Save the file */
	if (save_project (ai, name))
	{
	    UBYTE node[128];
	    BPTR lock;

	    /* Parse the name */
	    stcgfp (ad->ad_Tmp, name);
	    stcgfn (node, name);

	    /* Get a lock on the directory */
	    if (lock = Lock (ad->ad_Tmp, ACCESS_READ))
	    {
		struct WBArg wbarg;

		/* Fill in the WBArg */
		wbarg.wa_Lock = lock;
		wbarg.wa_Name = node;

		/* Update the project node */
		UpdateProject (ai, ad->ad_PN, &wbarg);

		/* Unlock our temporary lock */
		UnLock (lock);
	    }

	    /* Set the real file name */
	    FixFileAndPath (fr, ad->ad_PN->pn_ProjPath);

	    /* Clear the change flag */
	    ad->ad_PN->pn_Changed = FALSE;
	}

	/* Refresh the display */
	OpenMainFunc (h, ai, af);
    }
    else if (!cancel)
    {
	/* No name given */
	ai->ai_Pri_Ret = RETURN_ERROR;
	ai->ai_Sec_Ret = PERR_NO_NAME;

	ai->ai_TextRtn =
	  PrepText (ai, APSH_USER_ID, ai->ai_Sec_Ret, NULL);
    }

    /* clear the wait pointer */
    APSHClearPointer (ai, NULL);
}

 /* Shutdown routine */
VOID PQuitFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct MsgHandler *mh;
    ULONG FuncID = CloseID;
    BOOL cancel = FALSE;
    BOOL force = FALSE;
    BOOL all = FALSE;
    struct Funcs *f;

    /* set the wait pointer */
    APSHSetWaitPointer (ai, NULL);

    /* See if we have a parsed command */
    if (f = af->af_FE)
    {
	/* Did they set the force switch? */
	force = (BOOL) f->fe_Options[0];

	/* Close all windows? */
	if (all = (BOOL) f->fe_Options[1])
	{
	    FuncID = QuitID;
	}
    }

    /* Is there an existing project? */
    if (!force)
    {
	/* Prompt user */
	if (cancel = CheckForChanges (ai, ad->ad_PN))
	{
	    return;
	}
    }

    /* Make sure we have a master */
    if (ad->ad_MAI)
    {
	/* Get a handle on our master's SIPC port */
	if (mh = HandlerData (ad->ad_MAI, APSH_Handler, "SIPC", TAG_DONE))
	{
	    /* Prepare our Tags */
	    ad->ad_TL[0].ti_Tag = APSH_ProjInfo;
	    ad->ad_TL[0].ti_Data = (ULONG) ad->ad_PN;
	    ad->ad_TL[1].ti_Tag = TAG_DONE;

	    /* Tell our master to close this project work area */
	    HandlerFunc (ai,
			 APSH_Handler, "SIPC",
			 APSH_Command, AH_SENDCMD,
			 APSH_NameTag, (ULONG) mh->mh_PortName,
			 APSH_CmdID,   FuncID,
			 APSH_CmdData, (ULONG) ad->ad_TL,
			 TAG_DONE);
	}
    }

    /* Tell the AppShell that we're all done now. */
    ai->ai_Done = TRUE;

    /* set the wait pointer */
    APSHClearPointer (ai, NULL);
}

 /* Simulate a changed project */
VOID ChangeFunc (struct Hook *h, struct AppInfo *ai, struct AppFunction *af)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;

    /* Indicate a change */
    ad->ad_PN->pn_Changed = TRUE;
}

#define	REALLY_SAVE	FALSE

LONG save_project (struct AppInfo *ai, STRPTR name)
{
    LONG retval = FALSE;

#if REALLY_SAVE
    BPTR fh;

    /* Create a new file */
    if (fh = Open (name, MODE_NEWFILE))
    {
	/* Write something to the file here */
	if (Write (fh, name, strlen (name)) >= 0)
	{
	    /* Show that we couldn't write to the project */
	    ai->ai_Pri_Ret = RETURN_FAIL;
	    ai->ai_Sec_Ret = PERR_CANT_WRITE;
	    ai->ai_TextRtn =
		PrepText (ai, APSH_USER_ID, ai->ai_Sec_Ret, (int)name);
	}

	/* Close the file */
	Close (fh);

#else

    /* Make sure they gave us a name */
    if (1)
    {

#endif
	/* Indicate that we were able to save the project */
	retval = TRUE;
    }
    else
    {
	/* Show that we couldn't create the project */
	ai->ai_Pri_Ret = RETURN_FAIL;
	ai->ai_Sec_Ret = PERR_CANT_CREATE;
	ai->ai_TextRtn =
	    PrepText (ai, APSH_USER_ID, ai->ai_Sec_Ret, (int)name);
    }

    return (retval);
}

LONG load_project (struct AppInfo *ai, STRPTR name)
{
    struct AppData *ad = (struct AppData *) ai->ai_UserData;
    struct FileRequester *fr = ad->ad_FR[FR_PROJ];
    LONG retval = FALSE;
    BPTR fh;

    /* Open an existing file */
    if (fh = Open (name, MODE_OLDFILE))
    {
	/* Close the file */
	Close (fh);

	/* Set the real file name */
	FixFileAndPath (fr, name);

	/* Indicate that we were able to open the project */
	retval = TRUE;
    }
    else
    {
	/* Show that we couldn't load the project */
	ai->ai_Pri_Ret = RETURN_FAIL;
	ai->ai_Sec_Ret = PERR_CANT_LOAD;
	ai->ai_TextRtn =
	    PrepText (ai, APSH_USER_ID, ai->ai_Sec_Ret, (int)name);
    }

    return (retval);
}

/* Error handling routine */
SHORT myEasyRequest (struct Window *win, UBYTE *title, ULONG *idcmp,
		     UBYTE *fpat, UBYTE *fbuts, int arg1, ...)
{
    struct EasyStruct myEs = {NULL};

    myEs.es_StructSize = sizeof (struct EasyStruct);
    myEs.es_Title = title;
    myEs.es_TextFormat = fpat;
    myEs.es_GadgetFormat = fbuts;
    return ((SHORT) (EasyRequestArgs (win, &myEs, idcmp, &arg1)));
}

BOOL CheckForChanges (struct AppInfo * ai, struct ProjNode * pn)
{
    BOOL cancel = FALSE;

    if (pn->pn_Changed)
    {
	UBYTE pname[255];
	SHORT button;

	strcpy (pname, pn->pn_Name);

	if (strlen (pname) < 1)
	{
	    strcpy (pname, "<UnNamed>");
	}

	/* Project has been changed, do you want to save first? */
	button = myEasyRequest (ai->ai_Window, ai->ai_AppName, NULL,
		"%s\nhas been changed.", "Continue|Save First|Cancel",
		(int)pname);

	switch (button)
	{
	    case 1:		/* Nope, don't save */
		break;

	    case 2:		/* Yes, please save */
		PSaveFunc (NULL, ai, NULL);
		break;

	    case 0:		/* Woops! Cancel */
		/* clear the wait pointer */
		APSHClearPointer (ai, NULL);

		cancel = TRUE;
		break;
	}
    }

    return (cancel);
}

/* Get the file requester set up properly.  Returns 0 if no file node
 * present, returns > 0 if there is a file node.  */
LONG FixFileAndPath (struct FileRequester *rf, STRPTR name)
{
    LONG count = 0L;

    if (rf)
    {
	if (name)
	{
	    stcgfp (rf->rf_Dir, name);
	    count = (LONG) stcgfn (rf->rf_File, name);
	}
	else
	{
	    WORD dlen = (strlen (rf->rf_Dir) - 1);

	    if ((dlen > 0) && (rf->rf_Dir[dlen] == '/'))
	    {
		rf->rf_Dir[dlen] = 0;
	    }
	}
    }

    return (count);
}
