/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - GUI
 *  Copyright (C) 2022 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 PET Fund in 2022)
 *
 *  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
 */

/* pen selection dialog */

#include <libcschem/config.h>

#include <string.h>
#include <genht/htsp.h>
#include <genht/hash.h>
#include <genlist/gendlist.h>

#include <librnd/core/event.h>
#include <librnd/core/actions.h>
#include <librnd/core/hidlib.h>
#include <librnd/core/rnd_conf.h>
#include <librnd/hid/hid_dad.h>
#include <librnd/hid/hid_dad_tree.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/compat_fs.h>
#include <librnd/core/globalconst.h>

#include <libcschem/search.h>
#include <libcschem/cnc_loop.h>
#include <libcschem/cnc_pen.h>
#include <libcschem/cnc_grp.h>
#include <libcschem/cnc_any_obj.h>
#include <libcschem/operation.h>
#include <libcschem/actions_csch.h>
#include <sch-rnd/draw.h>
#include <sch-rnd/search.h>
#include <sch-rnd/funchash_core.h>
#include <sch-rnd/crosshair.h>
#include <sch-rnd/dad.h>


/* This doesn't make a plugin dep as only inlines and macros and structs are used */
#include <sch-rnd/conf_core.h>
#define RND_TIMED_CHG_TIMEOUT conf_core.editor.edit_time
#include <librnd/plugins/lib_hid_common/timed_chg.h>

typedef struct pendlg_ctx_s pendlg_ctx_t;

struct pendlg_ctx_s {
	RND_DAD_DECL_NOINIT(dlg)
	csch_sheet_t *sheet;
	int wlist, wname, wsize, wshape, wcolor, wfontheight, wfontfamily, wfontstyle, wdel;
	csch_cgrp_t *grp;
	int recursive, is_modal;
	htsp_t pens; /* key: pen's ->name; value: (csch_cpen_t *) */
	csch_cpen_t *last_pen; /* last selected pen */

	/* timed changes */
	struct {
		rnd_timed_chg_t hdr;

		csch_cpen_t *pen; /* pen to apply these changes to */

		/* remember what to change */
		unsigned height:1;
		unsigned family:1;
		unsigned style:1;
	} font;

	struct {
		rnd_timed_chg_t hdr;

		csch_cpen_t *pen; /* pen to apply these changes to */

		/* remember what to change */
		unsigned name:1;
		unsigned shape:1;
		unsigned size:1;
		unsigned color:1;
	} tip;

	gdl_elem_t link;
};

static gdl_list_t pendlgs; /* list of all non-modal pen dialogs */

static void pens_map(pendlg_ctx_t *ctx);

/* finalize all pending operations */
static void pendlg_finalize(pendlg_ctx_t *ctx)
{
	rnd_timed_chg_finalize(&ctx->font.hdr);
	rnd_timed_chg_finalize(&ctx->tip.hdr);
}

static void pendlg_close_cb(void *caller_data, rnd_hid_attr_ev_t ev)
{
	pendlg_ctx_t *ctx = caller_data;

	pendlg_finalize(ctx);

	htsp_uninit(&ctx->pens);
	RND_DAD_FREE(ctx->dlg);
	if (!ctx->is_modal) {
		gdl_remove(&pendlgs, ctx, link);
		free(ctx); /* need to leave this to after RUN() returned because we want to pick up the result */
	}
}

static int pen_cmp(const void *p1_, const void *p2_)
{
	const csch_cpen_t **p1 = (const csch_cpen_t **)p1_, **p2 = (const csch_cpen_t **)p2_;
	return strcmp((*p1)->name.str, (*p2)->name.str); /* never returns 0 because these are coming from a hash table by name */
}

static void pens2dlg_load_tree(pendlg_ctx_t *ctx, rnd_hid_attribute_t *attr)
{
	vtp0_t sorted = {0};
	htsp_entry_t *e;
	char *cell[3];
	const csch_cpen_t **pen;
	long n;

	/* sort items */
	vtp0_enlarge(&sorted, ctx->pens.used);
	sorted.used = 0;
	for(e = htsp_first(&ctx->pens); e != NULL; e = htsp_next(&ctx->pens, e))
		vtp0_append(&sorted, e->value);
	qsort(sorted.array, sorted.used, sizeof(csch_cpen_t *), pen_cmp);

	/* add to table */
	cell[2] = NULL;
	for(n = 0, pen = (const csch_cpen_t **)sorted.array; n < sorted.used; n++, pen++) {
		rnd_hid_row_t *row;

		cell[0] = rnd_strdup((*pen)->name.str);
		cell[1] = rnd_strdup((*pen)->hdr.parent == ctx->grp ? "local" : "inherited");
		row = rnd_dad_tree_append(attr, NULL, cell);
		row->user_data = (void *)(*pen);
	}

	vtp0_uninit(&sorted);
}

static void pen_dlg_select_by_name(pendlg_ctx_t *ctx, const char *pen_name)
{
	rnd_hid_attr_val_t hv;
	hv.str = pen_name;
	rnd_gui->attr_dlg_set_value(ctx->dlg_hid_ctx, ctx->wlist, &hv);
}

static void pens2dlg(pendlg_ctx_t *ctx)
{
	rnd_hid_attribute_t *attr = &ctx->dlg[ctx->wlist];
	rnd_hid_tree_t *tree = attr->wdata;
	rnd_hid_row_t *r;
	char *cursor_path = NULL;

	/* remember cursor */
	r = rnd_dad_tree_get_selected(attr);
	if (r != NULL)
		cursor_path = rnd_strdup(r->cell[0]);

	/* remove existing items */
	rnd_dad_tree_clear(tree);

	/* add all items */
	pens2dlg_load_tree(ctx, attr);

	/* restore cursor */
	if (cursor_path != NULL) {
		pen_dlg_select_by_name(ctx, cursor_path);
		free(cursor_path);
	}
}

static void pen2dlg(pendlg_ctx_t *ctx, csch_cpen_t *pen)
{
	int enab;

	if (pen != NULL) {
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wname, str, pen->name.str);
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wsize, crd, C2P(pen->size));
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wshape, lng, pen->shape);
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wcolor, clr, pen->color);
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wfontheight, crd, C2P(pen->font_height));
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wfontfamily, str, RND_EMPTY(pen->font_family));
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wfontstyle, str,  RND_EMPTY(pen->font_style));
		enab = 1;
	}
	else {
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wname, str, "");
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wsize, crd, 0);
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wshape, lng, -1);
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wcolor, str, "");
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wfontheight, crd, 0);
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wfontfamily, str, "");
		RND_DAD_SET_VALUE(ctx->dlg_hid_ctx, ctx->wfontstyle, str, "");
		enab = 0;
	}

	rnd_gui->attr_dlg_widget_state(ctx->dlg_hid_ctx, ctx->wname, enab);
	rnd_gui->attr_dlg_widget_state(ctx->dlg_hid_ctx, ctx->wsize, enab);
	rnd_gui->attr_dlg_widget_state(ctx->dlg_hid_ctx, ctx->wshape, enab);
	rnd_gui->attr_dlg_widget_state(ctx->dlg_hid_ctx, ctx->wcolor, enab);
	rnd_gui->attr_dlg_widget_state(ctx->dlg_hid_ctx, ctx->wfontheight, enab);
	rnd_gui->attr_dlg_widget_state(ctx->dlg_hid_ctx, ctx->wfontfamily, enab);
	rnd_gui->attr_dlg_widget_state(ctx->dlg_hid_ctx, ctx->wfontstyle, enab);
}

static void pen_select_cb(rnd_hid_attribute_t *attrib, void *hid_ctx, rnd_hid_row_t *row)
{
	rnd_hid_tree_t *tree = attrib->wdata;
	pendlg_ctx_t *ctx = tree->user_ctx;
	int is_local = 0;

	if (row != NULL) {
		ctx->last_pen = (csch_cpen_t *)row->user_data;
		is_local = (row->cell[1][0] == 'l');
	}
	else
		ctx->last_pen = NULL;

	pendlg_finalize(ctx);
	pen2dlg(ctx, ctx->last_pen);
	rnd_gui->attr_dlg_widget_state(ctx->dlg_hid_ctx, ctx->wdel, is_local); /* allow deleting local pens only */
}

static csch_cpen_t *get_dlg_pen(pendlg_ctx_t *ctx)
{
	rnd_hid_attribute_t *attr = &ctx->dlg[ctx->wlist];
	rnd_hid_row_t *r = rnd_dad_tree_get_selected(attr);
	if (r == NULL)
		return NULL;
	return r->user_data;
}

static void pen_dlg_remap_redraw(pendlg_ctx_t *ctx)
{
	pens_map(ctx);
	pens2dlg(ctx);
	pen2dlg(ctx, NULL);
	rnd_gui->invalidate_all(rnd_gui);
}

static void pen_new_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	csch_cpen_t *pen;
	pendlg_ctx_t *ctx = caller_data;

	pendlg_finalize(ctx);

	uundo_freeze_serial(&ctx->sheet->undo);
	pen = (csch_cpen_t *)csch_op_create(ctx->sheet, ctx->grp, CSCH_CTYPE_PEN);
	if (pen != NULL) {
		rnd_color_t clr;
		csch_coord_t sz = 1000, tsz=3000;
		char *font_family = "sans";

		memcpy(&clr, rnd_color_red, sizeof(rnd_color_t));
		csch_pen_modify_tip(ctx->sheet, pen, NULL, &sz, &clr, NULL, NULL, NULL, 1);
		csch_pen_modify_font(ctx->sheet, pen, &tsz, &font_family, NULL, 1);
		pen_dlg_remap_redraw(ctx);
		ctx->last_pen = pen;
		pen_dlg_select_by_name(ctx, pen->name.str);
		pen2dlg(ctx, pen);
	}
	uundo_unfreeze_serial(&ctx->sheet->undo);
	uundo_inc_serial(&ctx->sheet->undo);
}

static void pen_del_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	pendlg_ctx_t *ctx = caller_data;
	csch_cpen_t *pen = get_dlg_pen(ctx);

	if (pen->hdr.parent != ctx->grp) {
		rnd_message(RND_MSG_ERROR, "Can not remove inherited pen;\nfind the pen in a parent group and remove there!\n");
		return;
	}

	pendlg_finalize(ctx);
	csch_op_remove(ctx->sheet, &pen->hdr);
	pen_dlg_remap_redraw(ctx);
}

static void set_pen_tip_timed(void *ctx_)
{
	pendlg_ctx_t *ctx = ctx_;
	char *tmp_name, **new_name = NULL;
	csch_pen_shape_t tmp_shape, *new_shape = NULL;
	csch_coord_t *new_size = NULL, crdtmp;
	rnd_color_t *new_color = NULL;
	int remap = 0;

	if (ctx->tip.name) {
		rnd_hid_attribute_t *attr = &ctx->dlg[ctx->wlist];
		rnd_hid_row_t *r;

		tmp_name = rnd_strdup(ctx->dlg[ctx->wname].val.str);
		new_name = &tmp_name;
		ctx->font.style = 0;
		remap = 0;

		/* update name in the list-of-pens */
		r = rnd_dad_tree_get_selected(attr);
		if (r != NULL)
			rnd_dad_tree_modify_cell(attr, r, 0, rnd_strdup(tmp_name));
	}

	if (ctx->tip.shape) {
		tmp_shape = ctx->dlg[ctx->wshape].val.lng;
		new_shape = &tmp_shape;
		ctx->tip.shape = 0;
	}

	if (ctx->tip.size) {
		crdtmp = P2C(ctx->dlg[ctx->wsize].val.crd);
		new_size = &crdtmp;
		ctx->tip.size = 0;
	}

	if (ctx->tip.color) {
		new_color = &ctx->dlg[ctx->wcolor].val.clr;
		ctx->tip.color = 0;
	}

	csch_pen_modify_tip(ctx->sheet, ctx->tip.pen, new_shape, new_size, new_color, new_name, NULL, NULL, 1);

	if (remap)
		pen_dlg_remap_redraw(ctx);
	rnd_gui->invalidate_all(rnd_gui);
}

static void set_pen_name_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	pendlg_ctx_t *ctx = caller_data;
	csch_cpen_t *pen = get_dlg_pen(ctx);

	if ((pen == NULL) || (strcmp(attr->val.str, pen->name.str) == 0))
		return;

	ctx->tip.pen = pen;
	ctx->tip.name = 1;
	rnd_timed_chg_schedule(&ctx->tip.hdr);
}

static void set_pen_shape_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	pendlg_ctx_t *ctx = caller_data;
	csch_cpen_t *pen = get_dlg_pen(ctx);

	if (pen == NULL)
		return;

	ctx->tip.pen = pen;
	ctx->tip.shape = 1;
	rnd_timed_chg_schedule(&ctx->tip.hdr);
}

static void set_pen_size_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	pendlg_ctx_t *ctx = caller_data;
	csch_cpen_t *pen = get_dlg_pen(ctx);

	if (pen == NULL)
		return;

	ctx->tip.pen = pen;
	ctx->tip.size = 1;
	rnd_timed_chg_schedule(&ctx->tip.hdr);
}

static void set_pen_color_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	pendlg_ctx_t *ctx = caller_data;
	csch_cpen_t *pen = get_dlg_pen(ctx);

	if (pen == NULL)
		return;

	ctx->tip.pen = pen;
	ctx->tip.color = 1;
	rnd_timed_chg_schedule(&ctx->tip.hdr);

}

static void set_pen_font_timed(void *ctx_)
{
	pendlg_ctx_t *ctx = ctx_;
	char *tmp_style, **new_style = NULL, *tmp_family, **new_family = NULL;
	csch_coord_t *new_height = NULL, ctmp;

	if (ctx->font.height) {
		ctmp = P2C(ctx->dlg[ctx->wfontheight].val.crd);
		new_height = &ctmp;
		ctx->font.height = 0;
	}

	if (ctx->font.family) {
		tmp_family = rnd_strdup(ctx->dlg[ctx->wfontfamily].val.str);
		new_family = &tmp_family;
		ctx->font.family = 0;
	}

	if (ctx->font.style) {
		tmp_style = rnd_strdup(ctx->dlg[ctx->wfontstyle].val.str);
		new_style = &tmp_style;
		ctx->font.style = 0;
	}

	csch_pen_modify_font(ctx->sheet, ctx->font.pen, new_height, new_family, new_style, 1);
	rnd_gui->invalidate_all(rnd_gui);
}

static void set_pen_fontheight_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	pendlg_ctx_t *ctx = caller_data;
	csch_cpen_t *pen = get_dlg_pen(ctx);
	csch_coord_t newval = P2C(ctx->dlg[ctx->wfontheight].val.crd);

	if ((pen == NULL) || (newval == pen->font_height))
		return;

	ctx->font.pen = pen;
	ctx->font.height = 1;
	rnd_timed_chg_schedule(&ctx->font.hdr);
}

static void set_pen_fontfamily_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	pendlg_ctx_t *ctx = caller_data;
	csch_cpen_t *pen = get_dlg_pen(ctx);

	if ((pen == NULL) || ((pen->font_family != NULL) && (strcmp(attr->val.str, pen->font_family) == 0)))
		return;

	ctx->font.pen = pen;
	ctx->font.family = 1;
	rnd_timed_chg_schedule(&ctx->font.hdr);
}

static void set_pen_fontstyle_cb(void *hid_ctx, void *caller_data, rnd_hid_attribute_t *attr)
{
	pendlg_ctx_t *ctx = caller_data;
	csch_cpen_t *pen = get_dlg_pen(ctx);

	if ((pen == NULL) || ((pen->font_style != NULL) && (strcmp(attr->val.str, pen->font_style) == 0)))
		return;

	ctx->font.pen = pen;
	ctx->font.style = 1;
	rnd_timed_chg_schedule(&ctx->font.hdr);
}



static void pens_map_(pendlg_ctx_t *ctx, const csch_cgrp_t *grp)
{
	csch_chdr_t *h;
	csch_grpo_iter_t it;

	for(h = csch_grpo_first(&it, ctx->sheet, grp); h != NULL; h = csch_grpo_next(&it)) {
		if (h->type == CSCH_CTYPE_PEN) {
			csch_cpen_t *pen = (csch_cpen_t *)h;
			if (!htsp_has(&ctx->pens, pen->name.str))
				htsp_set(&ctx->pens, (char *)pen->name.str, pen);
		}
	}

	if (ctx->recursive && (grp->hdr.parent != NULL))
		pens_map_(ctx, grp->hdr.parent);
}

static void pens_map(pendlg_ctx_t *ctx)
{
	htsp_clear(&ctx->pens);
	pens_map_(ctx, ctx->grp);
}

const char *sch_rnd_pen_dlg(csch_sheet_t *sheet, csch_cgrp_t *grp, const char *pen_name, int modal, int recursive)
{
	pendlg_ctx_t *ctx = calloc(sizeof(pendlg_ctx_t), 1);
	rnd_hid_dad_buttons_t clbtn_modal[] = {{"Cancel", 0}, {"Use selected", 1}, {NULL, 0}};
	rnd_hid_dad_buttons_t clbtn_normal[] = {{"Close", 0}, {NULL, 0}};
	const char *hdr[] = {"pen name", "scope", NULL};
	char *shapes[] = {"round", "square", NULL};

	ctx->sheet = sheet;
	ctx->grp = grp;
	ctx->recursive = recursive;
	ctx->is_modal = modal;
	htsp_init(&ctx->pens, strhash, strkeyeq);
	pens_map(ctx);

	rnd_timed_chg_init(&ctx->font.hdr, set_pen_font_timed, ctx);
	rnd_timed_chg_init(&ctx->tip.hdr,  set_pen_tip_timed, ctx);

	if (!modal)
		gdl_append(&pendlgs, ctx, link);

	RND_DAD_BEGIN_VBOX(ctx->dlg);
		RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
		RND_DAD_BEGIN_HPANE(ctx->dlg, "left-right");
			RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);

			RND_DAD_BEGIN_VBOX(ctx->dlg); /* left */
				RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
				RND_DAD_TREE(ctx->dlg, 2, 0, hdr); /* top left: tree */
					RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);
					RND_DAD_TREE_SET_CB(ctx->dlg, selected_cb, pen_select_cb);
					RND_DAD_TREE_SET_CB(ctx->dlg, ctx, ctx);
					ctx->wlist = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_BEGIN_HBOX(ctx->dlg); /* bottom left: buttons */
					RND_DAD_BUTTON(ctx->dlg, "new");
						RND_DAD_CHANGE_CB(ctx->dlg, pen_new_cb);
					RND_DAD_BUTTON(ctx->dlg, "del");
						RND_DAD_CHANGE_CB(ctx->dlg, pen_del_cb);
						ctx->wdel = RND_DAD_CURRENT(ctx->dlg);
				RND_DAD_END(ctx->dlg);
			RND_DAD_END(ctx->dlg);

			RND_DAD_BEGIN_VBOX(ctx->dlg); /* right */
				RND_DAD_COMPFLAG(ctx->dlg, RND_HATF_EXPFILL);

				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_LABEL(ctx->dlg, "Pen name:");
					RND_DAD_STRING(ctx->dlg);
						ctx->wname = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_CHANGE_CB(ctx->dlg, set_pen_name_cb);
				RND_DAD_END(ctx->dlg);

				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_LABEL(ctx->dlg, "Shape:");
					RND_DAD_ENUM(ctx->dlg, shapes);
						ctx->wshape = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_CHANGE_CB(ctx->dlg, set_pen_shape_cb);
				RND_DAD_END(ctx->dlg);

				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_LABEL(ctx->dlg, "Size:");
					RND_DAD_CSCH_COORD(ctx->dlg);
						ctx->wsize = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_CHANGE_CB(ctx->dlg, set_pen_size_cb);
				RND_DAD_END(ctx->dlg);

				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_LABEL(ctx->dlg, "Color:");
					RND_DAD_COLOR(ctx->dlg);
						ctx->wcolor = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_CHANGE_CB(ctx->dlg, set_pen_color_cb);
				RND_DAD_END(ctx->dlg);

				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_LABEL(ctx->dlg, "Font height:");
					RND_DAD_CSCH_COORD(ctx->dlg);
						ctx->wfontheight = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_CHANGE_CB(ctx->dlg, set_pen_fontheight_cb);
				RND_DAD_END(ctx->dlg);

				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_LABEL(ctx->dlg, "Font family:");
					RND_DAD_STRING(ctx->dlg);
						ctx->wfontfamily = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_CHANGE_CB(ctx->dlg, set_pen_fontfamily_cb);
				RND_DAD_END(ctx->dlg);

				RND_DAD_BEGIN_HBOX(ctx->dlg);
					RND_DAD_LABEL(ctx->dlg, "Font style:");
					RND_DAD_STRING(ctx->dlg);
						ctx->wfontstyle = RND_DAD_CURRENT(ctx->dlg);
						RND_DAD_CHANGE_CB(ctx->dlg, set_pen_fontstyle_cb);
				RND_DAD_END(ctx->dlg);

				RND_DAD_TIMING(ctx->dlg, &ctx->font.hdr, "font");
				RND_DAD_TIMING(ctx->dlg, &ctx->tip.hdr, "tip");

			RND_DAD_END(ctx->dlg);
		RND_DAD_END(ctx->dlg);


		RND_DAD_BUTTON_CLOSES(ctx->dlg, (modal ? clbtn_modal : clbtn_normal));
	RND_DAD_END(ctx->dlg);

	RND_DAD_DEFSIZE(ctx->dlg, 200, 300);
	RND_DAD_NEW("PenDialog", ctx->dlg, "Pen edit", ctx, modal, pendlg_close_cb); /* type=local */

	rnd_timed_chg_timing_init(&ctx->font.hdr, ctx->dlg_hid_ctx);
	rnd_timed_chg_timing_init(&ctx->tip.hdr, ctx->dlg_hid_ctx);

	pens2dlg(ctx);
	if (pen_name != NULL) {
		pen_dlg_select_by_name(ctx, pen_name);
		pen2dlg(ctx, htsp_get(&ctx->pens, pen_name));
	}
	else
		pen2dlg(ctx, NULL);

	if (modal) {
		const char *pn = NULL;

		if (RND_DAD_RUN(ctx->dlg)) {
			if (ctx->last_pen != NULL)
				pn = ctx->last_pen->name.str;
		}

		RND_DAD_FREE(ctx->dlg);
		free(ctx);
		return pn;
	}

	return NULL;
}


extern csch_chdr_t *csch_obj_clicked;

const char csch_acts_PenDialog[] = "PenDialog([object[=idpath]], [non-modal], [recursive], [ret_name], [init_pen_name])";
const char csch_acth_PenDialog[] = "Bring up a modal pen selecton dialog. If idpath is specified, list pens starting from the group addressed by idpath. If second argument is non-modal, the dialog is not modal; else it is modal and the user selected pen is returned (as idpath). If recursive is set, map pens of parents as well. If ret_name is set, return the name of the selected pen (or NULL on no selection) instead of the usual integer return. If init_pen_name is supplied, the cursor is set to that pen.\n";
fgw_error_t csch_act_PenDialog(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	rnd_design_t *hidlib = RND_ACT_DESIGN;
	csch_sheet_t *sheet = (csch_sheet_t *)hidlib;
	int cmd, is_modal = 1, recursive = 0, ret_name = 0;
	const char *modals = NULL, *recursives = NULL, *ret_names = NULL;
	const char *init_pen_name = NULL;
	csch_chdr_t *obj;

	CSCH_ACT_CONVARG_OBJ(1, PenDialog, cmd, obj);
	RND_ACT_MAY_CONVARG(2, FGW_STR, PenDialog, modals = argv[2].val.cstr);
	RND_ACT_MAY_CONVARG(3, FGW_STR, PenDialog, recursives = argv[3].val.cstr);
	RND_ACT_MAY_CONVARG(4, FGW_STR, PenDialog, ret_names = argv[4].val.cstr);
	RND_ACT_MAY_CONVARG(5, FGW_STR, PenDialog, init_pen_name = argv[5].val.cstr);

	if ((modals != NULL) && (rnd_strcasecmp(modals, "non-modal") == 0))
		is_modal = 0;

	if ((recursives != NULL) && (rnd_strcasecmp(recursives, "recursive") == 0))
		recursive = 1;

	if ((ret_names != NULL) && (rnd_strcasecmp(ret_names, "ret_name") == 0))
		ret_name = 1;

	RND_ACT_IRES(-1);

	switch(cmd) {
		case F_Object:
			{
				csch_coord_t x, y;

				if (obj == NULL) {
					if (sch_rnd_get_coords("Click on object to edit pens of", &x, &y, 0) != 0)
						break;

					obj = sch_rnd_search_obj_at(sheet, x, y, sch_rnd_slop);
					if (obj == NULL) {
						rnd_message(RND_MSG_ERROR, "PenDialog(): no object under cursor\n");
						break;
					}
				}

				if (obj != NULL) {
					csch_comm_str_t comms;
					int is_grp = csch_obj_is_grp(obj);
					csch_cgrp_t *grp = is_grp ? (csch_cgrp_t *)obj : obj->parent;
					const char *pen_name;

					if (init_pen_name == NULL)
						init_pen_name = obj->stroke_name.str;

					pen_name = sch_rnd_pen_dlg(sheet, grp, init_pen_name, is_modal, recursive);
					if (pen_name != NULL) {
						if (!is_grp) {
							comms = csch_comm_str(sheet, pen_name, 1);
							csch_chdr_pen_name_modify(sheet, obj, &comms, NULL, 1);
						}
						if (ret_name) {
							res->type = FGW_STR;
							res->val.cstr = pen_name;
							return 0;
						}
						RND_ACT_IRES(0);
					}
				}
				else
					rnd_message(RND_MSG_ERROR, "PenDialog(): no object\n");
			}
			break;
		default:
			rnd_message(RND_MSG_ERROR, "PenDialog(): invalid first argument\n");
	}

	if (ret_name) {
		res->type = FGW_STR;
		res->val.cstr = NULL;
	}
	return 0;
}

/* close all non-modal pen dialogs open for this sheet */
void csch_dlg_pen_preunload(csch_sheet_t *sheet)
{
	pendlg_ctx_t *n, *next;
	rnd_dad_retovr_t retovr = {0};

	for(n = gdl_first(&pendlgs); n != NULL; n = next) {
		next = gdl_next(&pendlgs, n);
		if (n->sheet == sheet)
			rnd_hid_dad_close(n->dlg_hid_ctx, &retovr, 0);
	}
}
