/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - sch-rnd (executable)
 *  Copyright (C) 2022, 2023 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 PET Fund in 2022, Entrust in 2023)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/sch-rnd
 *    contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html
 *    mailing list: http://www.repo.hu/projects/sch-rnd/contact.html
 */

/* Generic actions usually related to the currently open file(s) */

#include <libcschem/config.h>

#include <liblihata/tree.h>

#include <libcschem/concrete.h>
#include <libcschem/plug_library.h>
#include <libcschem/event.h>
#include <libcschem/cnc_grp.h>
#include <libcschem/operation.h>
#include <libcschem/project.h>
#include <libcschem/plug_io.h>
#include <librnd/core/actions.h>
#include <librnd/hid/hid.h>
#include <librnd/hid/hid_export.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/compat_fs.h>
#include <librnd/core/compat_lrealpath.h>
#include <librnd/core/safe_fs.h>

#include "sheet.h"
#include "conf_core.h"
#include "build_run.h"
#include "multi.h"
#include "project.h"


static const char csch_acts_Quit[] = "Quit()";
static const char csch_acth_Quit[] = "Quits sch-rnd after confirming.";
static fgw_error_t csch_act_Quit(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	csch_sheet_t *sheet = CSCH_ACT_SHEET;
	const char *force = NULL;
	RND_ACT_MAY_CONVARG(1, FGW_STR, Quit, force = argv[1].val.str);

	if ((force != NULL) && (rnd_strcasecmp(force, "force") == 0))
		exit(0);

TODO("need to check all sheets, not just the current one")
	if (!sheet->changed || (rnd_hid_message_box(RND_ACT_DESIGN, "warning", "Close: lose data", "Exiting sch-rnd will lose unsaved sheet modifications.", "cancel", 0, "ok", 1, NULL) == 1))
		sch_rnd_quit_app();

	RND_ACT_IRES(-1);
	return 0;
}

static const char csch_acts_SymlibRehash[] = "SymlibRehash()";
static const char csch_acth_SymlibRehash[] = "Rebuild the in-memory tree of symbol libraries";
static fgw_error_t csch_act_SymlibRehash(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	csch_sheet_t *sheet = CSCH_ACT_SHEET;
	csch_lib_master_t *master = csch_lib_get_master("symbol", 1);

	csch_lib_clear_sheet_lib(sheet, master->uid);
	csch_lib_add_all(sheet, master, &conf_core.rc.library_search_paths, 1);
	rnd_event(&sheet->hidlib, CSCH_EVENT_LIBRARY_CHANGED, NULL);
	RND_ACT_IRES(0);
	return 0;
}

static const char csch_acts_SymlibReloadAllLocal[] = "SymlibReloadAllLocal()";
static const char csch_acth_SymlibReloadAllLocal[] = "Replace all local symbols from a newer version of the same symbol from the symbol library";
static fgw_error_t csch_act_SymlibReloadAllLocal(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	csch_sheet_t *sheet = CSCH_ACT_SHEET;
	long cnt = 0, n, i;
	csch_lib_root_t *libroot;
	csch_lib_master_t *master = csch_lib_get_master("symbol", 0);

	libroot = sheet->local_libs.array[master->uid];
	for(n = 0; n < libroot->roots.used; n++) {
		csch_lib_t *root = libroot->roots.array[n];
		if (strcmp(root->name, "<local>") == 0) {
			for(i = root->children.used-1; i >= 0; i--) {
				csch_lib_t *l = root->children.array[i];
				if ((l != NULL) && (l->backend != NULL) && (l->backend->loc_refresh_from_ext != NULL)) {
					if (l->backend->loc_refresh_from_ext(sheet, l) == 0)
						cnt++;
				}
			}
			break;
		}
	}

	rnd_message(RND_MSG_INFO, "Reloaded %ld local lib symbol(s) from external libs\n", cnt);

	rnd_event(&sheet->hidlib, CSCH_EVENT_LIBRARY_CHANGED, NULL);
	RND_ACT_IRES(0);
	return 0;
}


static const char csch_acts_Revert[] = "Revert([sheet|project])";
static const char csch_acth_Revert[] = "Revert to on-disk version of file(s). Default target is sheet.";
fgw_error_t csch_act_Revert(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *op = "sheet";
	csch_sheet_t *sheet = CSCH_ACT_SHEET;

	RND_ACT_MAY_CONVARG(1, FGW_STR, Revert, op = argv[1].val.str);

	if (rnd_strcasecmp(op, "sheet") == 0) {
		csch_revert_sheet(sheet, sch_rnd_sheet_new4revert);
	}
	else if (rnd_strcasecmp(op, "project") == 0) {
		rnd_message(RND_MSG_ERROR, "Revert(project,...) not yet implemented\n");
	}
	else
		RND_ACT_FAIL(Revert);

	RND_ACT_IRES(0);
	return 0;
}

static void prj_flush(csch_sheet_t *sheet, const char *listpath)
{
	const char *prjpath = NULL;

	rnd_conf_makedirty(RND_CFR_PROJECT);
	rnd_conf_update(listpath, -1);

	if (sheet->hidlib.project != NULL)
		prjpath = sheet->hidlib.project->fullpath;

	rnd_conf_save_file(&sheet->hidlib, prjpath, sheet->hidlib.fullpath, RND_CFR_PROJECT, NULL);
}

/* Does NOT increment num_root_sheets, caller needs to do that */
static void prj_append_sheet(csch_project_t *prj, csch_sheet_t *sheet, const char *listpath)
{
	lht_node_t *nd;

	if (prj->hdr.fullpath == NULL)
		sch_rnd_project_create_file_for_sheet_gui(sheet);

	if (prj->hdr.fullpath == NULL) /* cancel - no project file created */
		sheet->stype = CSCH_SHTY_UNLISTED;


	nd = rnd_conf_lht_get_at(RND_CFR_PROJECT, listpath, 1);
	if (nd != NULL) {
		char *val = NULL;

		if (prj->hdr.fullpath == NULL)
			val = rnd_strdup(rnd_basename(sheet->hidlib.fullpath));
		else if ((sheet->hidlib.fullpath != NULL) && (*sheet->hidlib.fullpath != '<'))
			val = rnd_relative_path_files(sheet->hidlib.fullpath, prj->hdr.fullpath);
		else
			rnd_message(RND_MSG_ERROR, "Failed to append sheet to project file's %s;\ntry to save the sheet first so it gets a file name!\n", listpath);

		if (val != NULL) {
			lht_node_t *n = lht_dom_node_alloc(LHT_TEXT, NULL);
			lht_dom_list_append(nd, n);
			n->data.text.value = val;
	
			prj_flush(sheet, listpath);
		}
	}
	else
		rnd_message(RND_MSG_ERROR, "Failed to append sheet to project file's %s\n", listpath);
}

static const char csch_acts_New[] = "New([scope, [root|aux|unlisted]])";
static const char csch_acth_New[] = "Create a new sheet. Scope is a project path or @ for current project.";
fgw_error_t csch_act_New(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *scope = "@", *type = "root";
	char *fn, *path, *dn;
	csch_sheet_t *ns, *curr = (csch_sheet_t *)RND_ACT_DESIGN;
	csch_project_t *prj;

	RND_ACT_MAY_CONVARG(1, FGW_STR, New, scope = argv[1].val.str);
	RND_ACT_MAY_CONVARG(2, FGW_STR, New, type = argv[2].val.str);

	if ((strcmp(type, "root") != 0) && (strcmp(type, "aux") != 0) && (strcmp(type, "unlisted") != 0)) {
		rnd_message(RND_MSG_ERROR, "New(): invalid sheet type '%s'\n", type);
		return FGW_ERR_ARG_CONV;
	}

	if ((scope[0] != '@') || (scope[1] != '\0')) {
		rnd_message(RND_MSG_ERROR, "New(): only current project scope (@) is supported at the moment\n");
		return FGW_ERR_ARG_CONV;
	}

	dn = rnd_dirname(curr->hidlib.fullpath);
	if (dn == NULL) {
		if ((curr->hidlib.project != NULL) && (curr->hidlib.project->fullpath != NULL))
			dn = rnd_dirname(curr->hidlib.project->fullpath);
		if (dn == NULL) {
			rnd_message(RND_MSG_ERROR, "New(): project does not exist\n(Try saving current sheet first)\n");
			return FGW_ERR_ARG_CONV;
		}
	}


	RND_ACT_IRES(0);
	fn = rnd_hid_prompt_for(RND_ACT_DESIGN, "Please enter a file name of the new sheet.\nFile path is assumed to be the same as the project's path.\n(as a convention, it is recommended that the file name ends in .rs)", "new.rs", "File name of the new sheet");
	if (fn == NULL) /* cancel */
		return 0;

	path = rnd_concat(dn, "/", fn, NULL);
	if (rnd_file_readable(RND_ACT_DESIGN, path)) {
		int go_for_it;
		char *msg;
		
		msg = rnd_concat("File ", path, " already exists.\nSaving the new sheet would overwrite it.\nAre you sure you want the create a new sheet on this name?", NULL);
		go_for_it = rnd_hid_message_box(RND_ACT_DESIGN, "question", "New sheet: file already exists", msg, "yes", 1, "no/cancel", 0, NULL);
		free(msg);

		if (!go_for_it) {
			free(path);
			return 0;
		}
	}

	rnd_multi_switch_to(NULL); /* empty conf so we can overwrite */

	ns = sch_rnd_multi_new_empty(path);
	free(path);

	ns->changed = 1; /* sheet is unsaved when created */

	prj = (csch_project_t *)ns->hidlib.project;
	switch(*type) {
		case 'r':
			ns->stype = CSCH_SHTY_ROOT;
			prj->num_root_sheets++;
			rnd_multi_switch_to_(&ns->hidlib); /* activate new, before project file write */
			prj_append_sheet(prj, ns, "prj/root_sheets");
			if (prj->hdr.fullpath == NULL) {
				/* undo the counting if the user didn't create the project file */
				prj->num_root_sheets--;
				ns->stype = CSCH_SHTY_UNLISTED;
			}
			rnd_event(&ns->hidlib, RND_EVENT_DESIGN_FN_CHANGED, NULL); /* get the final type displayed */
			break;
		case 'a':
			ns->stype = CSCH_SHTY_AUX;
			prj->num_aux_sheets++;
			rnd_multi_switch_to_(&ns->hidlib); /* activate new, before project file write */
			prj_append_sheet(prj, ns, "prj/aux_sheets");
			if (prj->hdr.fullpath == NULL) {
				/* undo the counting if the user didn't create the project file */
				prj->num_aux_sheets--;
				ns->stype = CSCH_SHTY_UNLISTED;
			}
			rnd_event(&ns->hidlib, RND_EVENT_DESIGN_FN_CHANGED, NULL); /* get the final type displayed */
			break;
		case 'u':
			ns->stype = CSCH_SHTY_UNLISTED;
			rnd_multi_switch_to_(&ns->hidlib); /* activate new */
			break;
	}

	return 0;
}

static const char csch_acts_ProjectNew[] = "ProjectNew([path])";
static const char csch_acth_ProjectNew[] = "Create a new project";
fgw_error_t csch_act_ProjectNew(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *path = NULL;
	char *freeme = NULL;
	int ret = 1;
	csch_project_t *prj;

	RND_ACT_MAY_CONVARG(1, FGW_STR, ProjectNew, path = argv[1].val.str);

	if (path == NULL) {
		path = freeme = rnd_hid_fileselect(rnd_gui, "New project", "select the path/project.lht of the new project", "project.lht", NULL, NULL, "ProjectNew", 0, NULL);
		if (path == NULL)
			goto quit;
	}

	if (rnd_file_readable(RND_ACT_DESIGN, path)) {
		rnd_message(RND_MSG_ERROR, "Can't create project: project file already exists\n");
		goto quit;
	}

	ret = sch_rnd_project_create_file(RND_ACT_DESIGN, path, &prj);
	if ((ret == 0) && (prj != NULL)) {
		csch_sheet_t *ns;
		char *nam, *bn;

		rnd_multi_switch_to(NULL); /* empty conf so we can overwrite */
		ns = sch_rnd_multi_new_empty_in_prj(prj);
		ns->changed = 1; /* sheet is unsaved when created */

		nam = rnd_strdup(prj->hdr.fullpath);
		bn = nam + strlen(nam) - 11; /* nam ends in project.lht */
		strcpy(bn, "<none>");
		ns->hidlib.loadname = nam; /* sinde there's no full_path in hidlib, save_as will trigger on save */
		ns->stype = CSCH_SHTY_UNLISTED;
		rnd_multi_switch_to_(&ns->hidlib);
	}

	quit:;
	free(freeme);
	RND_ACT_IRES(ret);
	return 0;
}

static const char csch_acts_ProjectLoadPartial[] = "ProjectLoadPartial([@])";
static const char csch_acth_ProjectLoadPartial[] = "Load sheet's missing files (or in other words, load the project file of the sheet)";
fgw_error_t csch_act_ProjectLoadPartial(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *scope = "@";
	csch_sheet_t *sheet = (csch_sheet_t *)RND_ACT_DESIGN;
	int ret;
	csch_project_t *prj;

	RND_ACT_CONVARG(1, FGW_STR, ProjectLoadPartial, scope = argv[1].val.str);

	RND_ACT_IRES(1);

	if ((scope[0] != '@') || (scope[1] != '\0')) {
		rnd_message(RND_MSG_ERROR, "ProjectLoadPartial(): only current project scope (@) is supported at the moment\n");
		return FGW_ERR_ARG_CONV;
	}

	prj = (csch_project_t *)sheet->hidlib.project;
	assert(prj != NULL);

	ret = rnd_actionva(&sheet->hidlib, "LoadFrom", "sheet", prj->hdr.fullpath, NULL);

	RND_ACT_IRES(ret);
	return 0;
}

static void prj_remove_sheet(csch_project_t *prj, const char *listpath, long pidx)
{
	lht_node_t *nd;

	if ((listpath == NULL) || (pidx < 0))
		return;

	nd = rnd_conf_lht_get_at(RND_CFR_PROJECT, listpath, 1);
	if (nd != NULL) {
		if (lht_tree_list_del_nth(nd, pidx) == LHTE_SUCCESS) {
			if (listpath[4] == 'r') /* removed from prj/root_sheets */
				prj->num_root_sheets--;
			if (listpath[4] == 'a') /* removed from prj/aux_sheets */
				prj->num_aux_sheets--;
		}
	}
}

static long prj_find_sheet_in_(const char *prjdir, const rnd_conflist_t *lst, const char *fpath)
{
	int idx;
	rnd_conf_listitem_t *item_li;
	const char *item_str;

	rnd_conf_loop_list_str(lst, item_li, item_str, idx) {
		char *sfp;
		const char *sfn = item_str;
		gds_t tmp = {0};

		if (!rnd_is_path_abs(sfn)) { /* relative sheet file name is always relative to the project */
			gds_append_str(&tmp, prjdir);
			gds_append(&tmp, '/');
			gds_append_str(&tmp, sfn);
			sfn = tmp.array;
		}

		sfp = rnd_lrealpath(sfn);
		gds_uninit(&tmp);

		if (sfp != NULL) {
			int res = strcmp(sfp, fpath);
			free(sfp);
			if (res == 0)
				return idx;
		}
	}
	return -1;
}

/* convert each item of lst into a realpath and check if it matches fpath;
   if it does, return its index on the list */
static long prj_find_sheet_in(csch_project_t *prj, const rnd_conflist_t *lst, const char *fpath)
{
	long res;
	char *bn;

	bn = rnd_dirname(prj->hdr.fullpath);
	res = prj_find_sheet_in_(bn, lst, fpath);
	free(bn);

	return res;
}



static const char csch_acts_ProjectSheetType[] = "ProjectSheetType(@, sheet_fullpath, root|aux|unlisted|unload)";
static const char csch_acth_ProjectSheetType[] = "Change the type of a sheet (addressed by its full path) in a project, making the necessary modifications in the project file";
fgw_error_t csch_act_ProjectSheetType(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *scope, *fpath, *type, *listpath = NULL;
	int also_unload = 0;
	long n, pidx = -1;
	csch_sheet_t *sheet = NULL, *old = NULL, *curr = (csch_sheet_t *)RND_ACT_DESIGN;
	csch_project_t *prj;

	RND_ACT_CONVARG(1, FGW_STR, ProjectSheetType, scope = argv[1].val.str);
	RND_ACT_CONVARG(2, FGW_STR, ProjectSheetType, fpath = argv[2].val.str);
	RND_ACT_CONVARG(3, FGW_STR, ProjectSheetType, type = argv[3].val.str);

	RND_ACT_IRES(1);

	if ((scope[0] != '@') || (scope[1] != '\0')) {
		rnd_message(RND_MSG_ERROR, "ProjectSheetType(): only current project scope (@) is supported at the moment\n");
		return FGW_ERR_ARG_CONV;
	}

	prj = (csch_project_t *)curr->hidlib.project;
	assert(prj != NULL);

	/* find the sheet */
	for(n = 0; n < prj->hdr.designs.used; n++) {
		csch_sheet_t *sh = prj->hdr.designs.array[n];
		if (strcmp(sh->hidlib.fullpath, fpath) == 0) {
			sheet = sh;
			break;
		}
	}

	if (sheet == NULL) {
		rnd_message(RND_MSG_ERROR, "ProjectSheetType(): sheet '%s' not found\n", fpath);
		return 0;
	}

	if ((sheet->hidlib.fullpath == NULL) || (*sheet->hidlib.fullpath == '<')) {
		rnd_message(RND_MSG_ERROR, "sheet '%s' has no valid file name, try saving it first\n", fpath);
		return 0;
	}

	if (prj->hdr.fullpath == NULL)
		sch_rnd_project_create_file_for_sheet_gui(curr);

	if (prj->hdr.fullpath == NULL) /* cancel - no project file created */
		return 0;

	/* figure index-on-config-array of target sheet, if there's a list to look into */
	switch(sheet->stype) {
		case CSCH_SHTY_ROOT: listpath = "prj/root_sheets"; pidx = prj_find_sheet_in(prj, &conf_core.prj.root_sheets, fpath); break;
		case CSCH_SHTY_AUX:  listpath = "prj/aux_sheets"; pidx = prj_find_sheet_in(prj, &conf_core.prj.aux_sheets, fpath); break;
		default: /* no list to remove from */;
	}

	/* need to switch current sheet to get the project config */
	if (listpath != NULL)
		old = sch_rnd_multi_switch_to(sheet);

	if (strcmp(type, "root") == 0) {
		if (sheet->stype != CSCH_SHTY_ROOT) {
			prj_remove_sheet(prj, listpath, pidx);
			prj_append_sheet(prj, sheet, "prj/root_sheets");
			sheet->stype = CSCH_SHTY_ROOT;
			prj->num_root_sheets++;
		}
	}
	else if (strcmp(type, "aux") == 0) {
		if (sheet->stype != CSCH_SHTY_AUX) {
			prj_remove_sheet(prj, listpath, pidx);
			prj_append_sheet(prj, sheet, "prj/aux_sheets");
			sheet->stype = CSCH_SHTY_AUX;
			prj->num_aux_sheets++;
		}
	}
	else if (strcmp(type, "unlisted") == 0) {
		if (sheet->stype != CSCH_SHTY_UNLISTED) {
			prj_remove_sheet(prj, listpath, pidx);
			sheet->stype = CSCH_SHTY_UNLISTED;
			if (type[3] == 'o')
				also_unload = 1;
		}
	}
	else if (strcmp(type, "unload") == 0) {
		if (listpath != NULL) {
			prj_remove_sheet(prj, listpath, pidx);
			prj_flush(sheet, listpath);
		}

		sheet->stype = CSCH_SHTY_UNLISTED;
		also_unload = 1;
	}
	else {
		rnd_message(RND_MSG_ERROR, "ProjectSheetType(): invalid sheet type '%s'\n", type);
		goto error;
	}

	rnd_event(&sheet->hidlib, RND_EVENT_DESIGN_FN_CHANGED, NULL); /* get the final type displayed */

	if (old != NULL)
		sch_rnd_multi_switch_to(old);

	if (also_unload) {
		csch_sheet_t *next = (csch_sheet_t *)rnd_multi_neighbour_sheet(NULL);
		if (next != NULL) {
			sch_rnd_multi_unload(sheet);
			if (old == sheet)
				sch_rnd_multi_switch_to(next);
			rnd_event(&next->hidlib, CSCH_EVENT_SHEET_POSTUNLOAD, NULL);
		}
		else
			rnd_message(RND_MSG_ERROR, "ProjectSheetType(): can not unload the only sheet open\n");
	}

	RND_ACT_IRES(0);
	return 0;

	error:
	if (old != NULL)
		sch_rnd_multi_switch_to(old);
	return 0;
}

static const char csch_acts_SaveAll[] = "SaveAll([currprj])";
static const char csch_acth_SaveAll[] = "Save all unsaved sheets in current project (currprj)";
fgw_error_t csch_act_SaveAll(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *scope = "currprj";
	csch_sheet_t *sheet = (csch_sheet_t *)RND_ACT_DESIGN;
	long n;
	csch_project_t *prj;

	RND_ACT_MAY_CONVARG(1, FGW_STR, SaveAll, scope = argv[1].val.str);

	RND_ACT_IRES(1);

	if (strcmp(scope, "currprj") != 0) {
		rnd_message(RND_MSG_ERROR, "ProjectSaveAll(): only current project scope (currprj) is supported at the moment\n");
		return FGW_ERR_ARG_CONV;
	}

	prj = (csch_project_t *)sheet->hidlib.project;
	assert(prj != NULL);

	RND_ACT_IRES(0);

	for(n = 0; n < prj->hdr.designs.used; n++) {
		csch_sheet_t *sheet = prj->hdr.designs.array[n];
		if (sheet->changed) {
			if (csch_save_sheet(sheet, sheet->hidlib.fullpath, "lihata") != 0) {
				rnd_message(RND_MSG_ERROR, "Can not save file '%s'\n", sheet->hidlib.fullpath);
				RND_ACT_IRES(-1);
			}
			else
				rnd_message(RND_MSG_INFO, "SaveAll: saved '%s'\n", sheet->hidlib.fullpath);
		}
	}

	return 0;
}

static rnd_action_t file_action_list[] = {
	{"Quit", csch_act_Quit, csch_acth_Quit, csch_acts_Quit},
	{"SymlibRehash", csch_act_SymlibRehash, csch_acth_SymlibRehash, csch_acts_SymlibRehash},
	{"SymlibReloadAllLocal", csch_act_SymlibReloadAllLocal, csch_acth_SymlibReloadAllLocal, csch_acts_SymlibReloadAllLocal},
	{"Revert", csch_act_Revert, csch_acth_Revert, csch_acts_Revert},
	{"New", csch_act_New, csch_acth_New, csch_acts_New},
	{"Export", rnd_act_Export, rnd_acth_Export, rnd_acts_Export},
	{"ProjectNew", csch_act_ProjectNew, csch_acth_ProjectNew, csch_acts_ProjectNew},
	{"ProjectLoadPartial", csch_act_ProjectLoadPartial, csch_acth_ProjectLoadPartial, csch_acts_ProjectLoadPartial},
	{"ProjectSheetType", csch_act_ProjectSheetType, csch_acth_ProjectSheetType, csch_acts_ProjectSheetType},
	{"SaveAll", csch_act_SaveAll, csch_acth_SaveAll, csch_acts_SaveAll},
};

void sch_rnd_file_act_init2(void)
{
	RND_REGISTER_ACTIONS(file_action_list, NULL);
}
