/*
 *                            COPYRIGHT
 *
 *  pcb-rnd, interactive printed circuit board design
 *  Copyright (C) 2022 Tibor 'Igor2' Palinkas
 *
 *  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., 31 Milk Street, # 960789 Boston, MA 02196 USA.
 *
 *  Contact:
 *    Project page: http://www.repo.hu/projects/librnd
 *    lead developer: http://www.repo.hu/projects/librnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

#include <librnd/rnd_config.h>
#include <librnd/core/event.h>
#include <librnd/core/compat_misc.h>
#include <librnd/hid/hid_dad.h>
#include <libmbtk/libmbtk.h>
#include <libmbtk/widget.h>
#include <libmbtk/draw_atext.h>
#include <libmbtk/callback_imp.h>
#include <librnd/plugins/lib_hid_common/dad_markup.h>

#include "lib_mbtk_conf.h"
#include "topwin.h"
#include "mbtk_common.h"

#include "attr_dlg.h"

typedef struct attr_dlg_s attr_dlg_t;

typedef struct attr_widget_s { /* closure for a attribute-widget so that callbacks can trace back full context */
	attr_dlg_t *actx;
	long idx;              /* attribute index and widget index (within actx->aw[]) */
	mbtk_widget_t *w;      /* the actual widget on the bottom (holds content, modify the content of this one) */
	mbtk_widget_t *wtop;   /* either same as ->w or the top wrapper widget for frame/scroll */
} attr_widget_t;

struct attr_dlg_s {
	/* config */
	rnd_mbtk_t *mctx; /* also includes ->hildib */
	rnd_hid_attribute_t *attrs;
	int n_attrs;
	void *caller_data;
	const char *id;
	void (*close_cb)(void *caller_data, rnd_hid_attr_ev_t ev);
	rnd_hid_attr_val_t property[RND_HATP_max];
	unsigned close_cb_called:1;
	unsigned modal:1;
	unsigned init_wpos:1;
	unsigned init_wdim:1;

	/* state */
	long x0, y0;
	mbtk_window_t *win;
	attr_widget_t *aw; /* allocated to n_attrs */
	gdl_elem_t link; /* in mctx->dad_dialogs  */

	/* temporary, used during build */
	char **tablab;    /* pointer into the array of tabbed labels; points to the next label to use; may be NULL if there are no labels; content NULL means no more labels */
	int panei;        /* next pane index to use */
};

typedef struct rnd_mbtk_attr_tb_s {
	enum {
		TB_TABLE,
		TB_TABBED,
		TB_PANE
	} type;
	union {
		struct {
			int cols, rows;
			int col, row;
		} table;
		struct {
			const char **tablab;
		} tabbed;
		struct {
			int next;
		} pane;
	} val;
} rnd_mbtk_attr_tb_t;


int rnd_mbtk_winplace_cfg(rnd_design_t *hidlib, mbtk_window_t *win, void *ctx, const char *id)
{
	TODO("x;y are relative coords, we need screen coords");
	rnd_event(hidlib, RND_EVENT_DAD_NEW_GEO, "psiiii", ctx, id,
		(int)win->x, (int)win->y, (int)win->w, (int)win->h);
	return 0;
}

static void rnd_mbtk_initial_wstates(attr_dlg_t *actx)
{
	int n;
	for(n = 0; n < actx->n_attrs; n++)
		if (actx->attrs[n].rnd_hatt_flags & RND_HATF_HIDE)
			mbtk_widget_hide(actx->aw[n].w, 0);
}

static mbtk_event_handled_t attr_dlg_event(mbtk_widget_t *w, mbtk_kw_t id, mbtk_event_t *ev, void *user_data)
{
	attr_dlg_t *actx = user_data;
	switch(ev->type) {
		case MBTK_EV_WIN_MOVE:
			actx->init_wpos = 1;
			goto cfgd;
		case MBTK_EV_WIN_RESIZE:
			actx->init_wdim = 1;
			cfgd:;
			if (actx->init_wpos && actx->init_wdim)
				rnd_mbtk_winplace_cfg(actx->mctx->hidlib, actx->win, actx, actx->id);
			return MBTK_EVENT_HANDLED;

		case MBTK_EV_WIN_CLOSE:
			TODO("handle close if needed");
			break;

		default:
			break;
	}
	return MBTK_EVENT_NOT_HANDLED;
}

#define change_cb(actx, dst) \
	do { \
		if (actx->property[RND_HATP_GLOBAL_CALLBACK].func != NULL) \
			actx->property[RND_HATP_GLOBAL_CALLBACK].func(actx, actx->caller_data, dst); \
		if (dst->change_cb != NULL) \
			dst->change_cb(actx, actx->caller_data, dst); \
	} while(0) \

#define right_cb(actx, dst) \
	do { \
		if (dst->right_cb != NULL) \
			dst->right_cb(actx, actx->caller_data, dst); \
	} while(0) \

#define enter_cb(actx, dst) \
	do { \
		if (dst->enter_cb != NULL) \
			dst->enter_cb(actx, actx->caller_data, dst); \
	} while(0) \

static mbtk_event_handled_t label_click_cb(mbtk_widget_t *w, mbtk_kw_t id, void *user_data)
{
	attr_widget_t *aw = user_data;
	rnd_hid_attribute_t *dst = &aw->actx->attrs[aw->idx];

	dst->changed = 1;
	change_cb(aw->actx, dst);
	return 0;
}

static mbtk_event_handled_t label_right_click_cb(mbtk_widget_t *w, mbtk_kw_t id, void *user_data)
{
	attr_widget_t *aw = user_data;
	rnd_hid_attribute_t *dst = &aw->actx->attrs[aw->idx];

	right_cb(aw->actx, dst);
	return 0;
}

static mbtk_event_handled_t button_click_cb(mbtk_widget_t *w, mbtk_kw_t id, void *user_data)
{
	attr_widget_t *aw = user_data;
	rnd_hid_attribute_t *dst = &aw->actx->attrs[aw->idx];

	dst->changed = 1;
	change_cb(aw->actx, dst);
	return 0;
}


static mbtk_event_handled_t entry_enter_cb(mbtk_widget_t *w, mbtk_kw_t id, void *user_data)
{
	attr_widget_t *aw = user_data;
	rnd_hid_attribute_t *dst = &aw->actx->attrs[aw->idx];
	enter_cb(aw->actx, dst);
	return 0;
}

static mbtk_event_handled_t entry_change_cb(mbtk_widget_t *w, mbtk_kw_t id, void *user_data)
{
	attr_widget_t *aw = user_data;
	rnd_hid_attribute_t *dst = &aw->actx->attrs[aw->idx];

	free((char *)dst->val.str);
	dst->val.str = rnd_strdup(mbtk_textedit_single_line_get_str((mbtk_textedit_t *)aw->w));
	dst->changed = 1;
	change_cb(aw->actx, dst);
	return 0;
}

static mbtk_event_handled_t tabbed_change_cb(mbtk_widget_t *w, mbtk_kw_t id, void *user_data)
{
	attr_widget_t *aw = user_data;
	rnd_hid_attribute_t *dst = &aw->actx->attrs[aw->idx];

	dst->val.lng = mbtk_tabbed_get_active((mbtk_tabbed_t *)aw->w);
	dst->changed = 1;
	change_cb(aw->actx, dst);
	return 0;
}

static mbtk_event_handled_t combo_change_cb(mbtk_widget_t *w, mbtk_kw_t id, void *user_data)
{
	attr_widget_t *aw = user_data;
	rnd_hid_attribute_t *dst = &aw->actx->attrs[aw->idx];

	dst->val.lng = mbtk_combo_get_idx((mbtk_combo_t *)aw->w);
	dst->changed = 1;
	change_cb(aw->actx, dst);
	return 0;
}

static mbtk_event_handled_t pane_moved_cb(mbtk_widget_t *w, mbtk_kw_t id, void *user_data)
{
	attr_widget_t *aw = user_data;
	rnd_hid_attribute_t *dst = &aw->actx->attrs[aw->idx];
	double pos;

	if (aw->actx->attrs[aw->idx].name == NULL)
		return 0; /* do not remember unnamed panes, they are not saved */

	TODO("do this behind a timer to avoid flooding - but maybe mbtk should have the timer?");
	pos = mbtk_pane_get_percent((mbtk_pane_t *)aw->w);
	if (pos >= 0)
		rnd_event(aw->actx->mctx->hidlib, RND_EVENT_DAD_PANE_GEO_CHG, "ssd", aw->actx->id, aw->actx->attrs[aw->idx].name, pos);

	return 0;
}


/* Add (append) w under parent, whatever parent is (box, table, etc) */
static void any_add_widget(attr_dlg_t *actx,mbtk_widget_t *parent, mbtk_widget_t *w)
{
	mbtk_kw_t kw_hbox, kw_vbox, kw_tbl, kw_tabbed, kw_pane;

	mbtk_kw_cache(kw_hbox, "hbox");
	mbtk_kw_cache(kw_vbox, "vbox");
	mbtk_kw_cache(kw_tbl, "tbl");
	mbtk_kw_cache(kw_tabbed, "tabbed");
	mbtk_kw_cache(kw_tabbed, "tabbed");
	mbtk_kw_cache(kw_pane, "pane");

	if ((parent->type == kw_hbox) || (parent->type == kw_vbox)) {
		mbtk_box_add_widget((mbtk_box_t *)parent, w, 0);
		return;
	}

	if (parent->type == kw_tbl) {
		mbtk_tbl_add_widget((mbtk_tbl_t *)parent, w, 0);
		return;
	}

	if (parent->type == kw_tabbed) {
		mbtk_tabbed_t *tabbed = (mbtk_tabbed_t *)parent;
		const char *lab;
		char tmp[64];

		if (actx->tablab != NULL) {
			lab = *actx->tablab;
			if (lab == NULL) {
				sprintf(tmp, "Page %d", mbtk_tabbed_get_num_pages(tabbed)+1);
				lab = tmp;
			}
			else
				actx->tablab++;
		}
		mbtk_tabbed_append_page_with_label(tabbed, lab, w);
		return;
	}

	if (parent->type == kw_pane) {
		mbtk_pane_t *pane = (mbtk_pane_t *)parent;

		mbtk_pane_set_child(pane, actx->panei, w);
		actx->panei++;
		return;
	}


	fprintf(stderr, "Internal error: any_add_widget() tries to append to '%s'\n", mbtk_kw_search(parent->type));
	abort();
}

/* Create a wrapper box around inner if needed (for frame or scroll) and
   add it under parent. If inner is NULL, only the box is created. If inner
   is not NULL and no box got created, inner is added under parent directly. */
static mbtk_widget_t *frame_scroll(attr_dlg_t *actx,mbtk_widget_t *parent, rnd_hatt_compflags_t flags, mbtk_widget_t **wltop, mbtk_widget_t *inner, int horiz)
{
	mbtk_box_t *fr;
	mbtk_widget_t *res = NULL;
	int expfill = (flags & RND_HATF_EXPFILL);
	int topped = 0;

	if (flags & RND_HATF_FRAME) {

		if (horiz)
			fr = mbtk_hbox_new(NULL);
		else
			fr = mbtk_vbox_new(NULL);
		mbtk_box_set_align(fr, HVBOX_LEFT_TOP);
		mbtk_box_set_framed(fr, 1);
		if (expfill) mbtk_widget_set_span(&fr->w, HVBOX_FILL);
		any_add_widget(actx, parent, &fr->w);

		if (wltop != NULL) {
			*wltop = (mbtk_widget_t *)fr;
			topped = 1; /* remember the outmost parent */
		}
		parent = res = &fr->w;
	}
	if (flags & RND_HATF_SCROLL) {
		mbtk_scrolled_wb_t *sc = mbtk_scrolled_wb_new(NULL, 1, 1);
		mbtk_scrolled_wb_set_span(sc, HVBOX_FILL);

		any_add_widget(actx, parent, &sc->w);
		if (inner == NULL) {
			if (horiz)
				res = (mbtk_widget_t *)mbtk_hbox_new(NULL);
			else
				res = (mbtk_widget_t *)mbtk_vbox_new(NULL);
		}
		else
			res = inner;

		mbtk_widget_set_align(res, HVBOX_LEFT_TOP);
		mbtk_scrolled_wb_set_root_widget(sc, res);

		if ((wltop != NULL) && (!topped)) {
			*wltop = (mbtk_widget_t *)sc;
			topped = 1;
		}
	}

	if ((inner != NULL) && !topped) {
		/* there's an inner and no frame and no scroll - place it in parent */
		any_add_widget(actx, parent, inner);
		if (expfill) mbtk_widget_set_span(inner, HVBOX_FILL);
		*wltop = inner;
		res = inner;
	}

	if (res == NULL) {
		if (horiz)
			res = (mbtk_widget_t *)mbtk_hbox_new(NULL);
		else
			res = (mbtk_widget_t *)mbtk_vbox_new(NULL);
		mbtk_widget_set_align(res, HVBOX_LEFT_TOP);
		any_add_widget(actx, parent, res);
	}

	return res;
}

static int rnd_mbtk_attr_dlg_add(attr_dlg_t *actx, mbtk_widget_t *parent, rnd_mbtk_attr_tb_t *tb_st, int start_from);

#include "attr_w_txt.c"
#include "attr_w_tree.c"
#include "attr_w_misc.c"

static int rnd_mbtk_attr_dlg_add(attr_dlg_t *actx, mbtk_widget_t *parent, rnd_mbtk_attr_tb_t *tb_st, int start_from)
{
	int j, nextj, expfill;
	mbtk_widget_t *w, *bparent;

	for(j = start_from; j < actx->n_attrs; j = nextj) {
		if (actx->attrs[j].type == RND_HATT_END) {
			j++;
			break;
		}

		expfill = (actx->attrs[j].rnd_hatt_flags & RND_HATF_EXPFILL);
		actx->aw[j].idx = j;
		actx->aw[j].actx = actx;
		nextj = j+1;

		switch (actx->attrs[j].type) {
			case RND_HATT_BEGIN_COMPOUND:
				nextj = rnd_mbtk_attr_dlg_add(actx, parent, NULL, j+1);
				break;
			case RND_HATT_BEGIN_HBOX:
				actx->aw[j].w = w = (mbtk_widget_t *)frame_scroll(actx, parent, actx->attrs[j].rnd_hatt_flags, &actx->aw[j].wtop, NULL, 1);
				nextj = rnd_mbtk_attr_dlg_add(actx, w, NULL, j+1);
				break;
			case RND_HATT_BEGIN_VBOX:
				actx->aw[j].w = w = (mbtk_widget_t *)frame_scroll(actx, parent, actx->attrs[j].rnd_hatt_flags, &actx->aw[j].wtop, NULL, 0);
				nextj = rnd_mbtk_attr_dlg_add(actx, w, NULL, j+1);
				break;
			case RND_HATT_BEGIN_TABLE:
				actx->aw[j].w = w = (mbtk_widget_t *)mbtk_tbl_new(NULL, actx->attrs[j].rnd_hatt_table_cols);
				/* wrap in a box if needed */
				frame_scroll(actx, parent, actx->attrs[j].rnd_hatt_flags, &actx->aw[j].wtop, w, 1);
				nextj = rnd_mbtk_attr_dlg_add(actx, w, NULL, j+1);
				break;
			case RND_HATT_BEGIN_TABBED:
				{
					char **tablab_save;
					mbtk_tabbed_layout_t lyo = (actx->attrs[j].rnd_hatt_flags & RND_HATF_LEFT_TAB) ? MBTK_TABBED_LEFT : MBTK_TABBED_TOP;
					actx->aw[j].w = w = (mbtk_widget_t *)mbtk_tabbed_new(NULL, lyo);

					tablab_save = actx->tablab;
					actx->tablab = actx->attrs[j].wdata;
					nextj = rnd_mbtk_attr_dlg_add(actx, w, NULL, j+1);
					actx->tablab = tablab_save;

					mbtk_tabbed_callback_changed((mbtk_tabbed_t *)w, tabbed_change_cb, &actx->aw[j]);
					any_add_widget(actx, parent, w);
					mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				}
				break;
			case RND_HATT_BEGIN_HPANE:
			case RND_HATT_BEGIN_VPANE:
				{
					int panei_save;

					actx->aw[j].w = w = (mbtk_widget_t *)mbtk_pane_new(NULL, (actx->attrs[j].type == RND_HATT_BEGIN_HPANE));

					panei_save = actx->panei;
					actx->panei = 0;
					nextj = rnd_mbtk_attr_dlg_add(actx, w, NULL, j+1);
					actx->panei = panei_save;

					mbtk_pane_callback_range_moved((mbtk_pane_t *)w, pane_moved_cb, &actx->aw[j]);

					any_add_widget(actx, parent, w);
					mbtk_widget_set_span(w, HVBOX_FILL); /* ugly: HPANE and VPANE imply exp+fill (librnd API) */
					mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				}
				break;

			case RND_HATT_SUBDIALOG:
				{
					mbtk_box_t *subbox = mbtk_hbox_new(NULL);
					rnd_hid_dad_subdialog_t *sub = actx->attrs[j].wdata;

					sub->dlg_hid_ctx = rnd_mbtk_attr_sub_new(actx->mctx, subbox, sub->dlg, sub->dlg_len, sub);
					actx->aw[j].w = w = &subbox->w;
					any_add_widget(actx, parent, w);
				}
				break;

			case RND_HATT_LABEL:
				actx->aw[j].w = w = (mbtk_widget_t *)mbtk_label_new(NULL, actx->attrs[j].name);

				if (actx->attrs[j].rnd_hatt_flags & RND_HATF_TEXT_TRUNCATED)
					mbtk_label_set_auto_trunc((mbtk_label_t *)w, 1);
				if (actx->attrs[j].rnd_hatt_flags & RND_HATF_TEXT_VERTICAL)
					mbtk_label_set_rot90((mbtk_label_t *)w, 1);

				mbtk_handler_pushb_install(w);
				mbtk_handler_pushb_callback_invoked(w, label_click_cb, &actx->aw[j]);
				mbtk_handler_pushb_callback_right_click(w, label_right_click_cb, &actx->aw[j]);
				any_add_widget(actx, parent, w);
				mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				break;

			case RND_HATT_BUTTON:
				if (actx->attrs[j].rnd_hatt_flags & RND_HATF_TOGGLE)
					actx->aw[j].w = w = (mbtk_widget_t *)mbtk_tbutton_new_with_label(actx->attrs[j].val.str);
				else
					actx->aw[j].w = w = (mbtk_widget_t *)mbtk_button_new_with_label(actx->attrs[j].val.str);
				mbtk_handler_pushb_callback_invoked(w, button_click_cb, &actx->aw[j]);
				any_add_widget(actx, parent, w);
				mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				break;

				actx->aw[j].w = w = (mbtk_widget_t *)mbtk_label_new(NULL, actx->attrs[j].name);
				mbtk_handler_pushb_install(w);
				mbtk_handler_pushb_callback_invoked(w, label_click_cb, &actx->aw[j]);
				mbtk_handler_pushb_callback_right_click(w, label_right_click_cb, &actx->aw[j]);
				any_add_widget(actx, parent, w);
				mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				break;

			case RND_HATT_STRING:
				actx->aw[j].w = w = (mbtk_widget_t *)mbtk_textedit_new(NULL, actx->attrs[j].val.str, 1);
				any_add_widget(actx, parent, w);
				mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);

				mbtk_textedit_callback_changed((mbtk_textedit_t *)w, entry_change_cb, &actx->aw[j]);
				mbtk_textedit_callback_keypress_enter((mbtk_textedit_t *)w, entry_enter_cb, &actx->aw[j]);

				if (actx->attrs[j].hatt_flags & RND_HATF_HEIGHT_CHR)
					mbtk_widget_set_minsize(w, actx->attrs[j].geo_width * 16, 0);
				else
					mbtk_widget_set_minsize(w, 150, 0);
				break;

			case RND_HATT_TEXT:
				actx->aw[j].w = w = (mbtk_widget_t *)rnd_mbtk_text_create(actx, &actx->attrs[j]);
				any_add_widget(actx, parent, w);
				mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				break;

			case RND_HATT_TREE:
				actx->aw[j].w = w = (mbtk_widget_t *)rnd_mbtk_tree_create(actx, &actx->attrs[j]);
				any_add_widget(actx, parent, w);
				mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				break;

			case RND_HATT_BOOL:
				rnd_mbtk_chk_btn_new(&w, &actx->aw[j].wtop, actx->attrs[j].val.lng, rnd_mbtk_chkbox_cb, &actx->aw[j], NULL);
				any_add_widget(actx, parent, w);
				mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				break;

			case RND_HATT_PROGRESS:
				{
					mbtk_progbar_t *p = mbtk_progbar_new(NULL);

					mbtk_progbar_set_minsize(p, 32, 12);
					actx->aw[j].w = w = (mbtk_widget_t *)p;
					any_add_widget(actx, parent, w);
					mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				}
				break;

			case RND_HATT_ENUM:
				{
					const char **vals = actx->attrs[j].wdata;
					mbtk_combo_t *cmb = mbtk_combo_new(NULL);

					mbtk_combo_append_strs(cmb, vals);
					mbtk_combo_set_val_idx(cmb, actx->attrs[j].val.lng);
					mbtk_combo_callback_changed(cmb, combo_change_cb, &actx->aw[j]);

					actx->aw[j].w = w = (mbtk_widget_t *)cmb;
					any_add_widget(actx, parent, w);
					mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				}
				break;

			case RND_HATT_PICTURE:
				{
					mbtk_picture_xpm_t *p = mbtk_picture_xpm_new(NULL);
					mbtk_picture_xpm_set_xpm(p, actx->attrs[j].wdata);

					actx->aw[j].w = w = (mbtk_widget_t *)p;
					any_add_widget(actx, parent, w);
					mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				}
				break;

			case RND_HATT_PICBUTTON:
				{
					mbtk_picture_xpm_t *p = mbtk_picture_xpm_new(NULL);
					mbtk_picture_xpm_set_xpm(p, actx->attrs[j].wdata);

					if (actx->attrs[j].rnd_hatt_flags & RND_HATF_TOGGLE)
						actx->aw[j].w = w = (mbtk_widget_t *)mbtk_tbutton_new_with_widget(NULL, &p->w);
					else
						actx->aw[j].w = w = (mbtk_widget_t *)mbtk_button_new_with_widget(NULL, &p->w);

					mbtk_handler_pushb_callback_invoked(w, button_click_cb, &actx->aw[j]);
					any_add_widget(actx, parent, w);
					mbtk_widget_set_tooltip(w, actx->attrs[j].help_text);
				}
				break;

			case RND_HATT_PREVIEW:
			case RND_HATT_COLOR:
				TODO("implement these");
				actx->aw[j].w = w = (mbtk_widget_t *)mbtk_label_new(NULL, "[TODO]");
				any_add_widget(actx, parent, w);
				break;


			case RND_HATT_INTEGER:
				actx->aw[j].w = w = (mbtk_widget_t *)mbtk_label_new(NULL, "ERROR: INTEGER entry");
				any_add_widget(actx, parent, w);
				break;

			case RND_HATT_REAL:
				actx->aw[j].w = w = (mbtk_widget_t *)mbtk_label_new(NULL, "ERROR: REAL entry");
				any_add_widget(actx, parent, w);
				break;

			case RND_HATT_COORD:
				actx->aw[j].w = w = (mbtk_widget_t *)mbtk_label_new(NULL, "ERROR: COORD entry");
				any_add_widget(actx, parent, w);
				break;

			case RND_HATT_UNIT:
				actx->aw[j].w = w = (mbtk_widget_t *)mbtk_label_new(NULL, "ERROR: UNIT entry");
				any_add_widget(actx, parent, w);
				break;

			case RND_HATT_END:
				abort();  /* can't get here, handled before the switch */
		}

		if (actx->aw[j].wtop == NULL)
			actx->aw[j].wtop = actx->aw[j].w;

		if (expfill)
			mbtk_widget_set_span(w, HVBOX_FILL);
	}
	return j;
}

void rnd_mbtk_attr_dlg_new(rnd_hid_t *hid, rnd_mbtk_t *mctx, const char *id, rnd_hid_attribute_t *attrs, int n_attrs, const char *title, void *caller_data, rnd_bool modal, void (*button_cb)(void *caller_data, rnd_hid_attr_ev_t ev), int defx, int defy, int minx, int miny, void **hid_ctx_out)
{
	mbtk_box_t *main_vbox;
	attr_dlg_t *actx;
	int plc[4] = {-1, -1, -1, -1};
	mbtk_window_type_t wtype;

	plc[2] = defx;
	plc[3] = defy;

	actx = calloc(sizeof(attr_dlg_t), 1);
	*hid_ctx_out = actx;
	actx->mctx = mctx;
	actx->attrs = attrs;
	actx->n_attrs = n_attrs;
	actx->aw = calloc(sizeof(attr_widget_t), n_attrs);
	actx->caller_data = caller_data;
	actx->close_cb_called = 0;
	actx->close_cb = button_cb;
	actx->id = rnd_strdup(id);
	actx->modal = modal;
	gdl_append(&mctx->dad_dialogs, actx, link);

	rnd_event(mctx->hidlib, RND_EVENT_DAD_NEW_DIALOG, "psp", actx, actx->id, plc);

	wtype = (modal ? MBTK_WNTY_MODAL : MBTK_WNTY_NORMAL);

	if (plc[0] < 0) plc[0] = 0;
	if (plc[1] < 0) plc[1] = 0;
	if (plc[2] <= 0) plc[2] = 32;
	if (plc[3] <= 0) plc[3] = 32;
	actx->win = mbtk_window_new(&mctx->disp, wtype, title, plc[0], plc[1], plc[2], plc[3]);
	if ((modal && rnd_mbtk_conf.plugins.hid_mbtk.dialog.transient_modal) || (!modal && rnd_mbtk_conf.plugins.hid_mbtk.dialog.transient_modeless))
		mbtk_window_transient4(actx->win, mctx->topwin->win);

	main_vbox = mbtk_vbox_new(NULL);
	mbtk_box_set_align(main_vbox, HVBOX_LEFT_TOP);

	mbtk_window_set_root_widget(actx->win, &main_vbox->w);
	rnd_mbtk_attr_dlg_add(actx, &main_vbox->w, NULL, 0);


	mbtk_callback_set_event(&main_vbox->w, mbtk_kw("event"), attr_dlg_event, actx);

	rnd_mbtk_initial_wstates(actx);

	if (rnd_mbtk_conf.plugins.hid_mbtk.dialog.auto_present)
		mbtk_window_raise(actx->win);
}

int rnd_mbtk_attr_dlg_run(void *hid_ctx)
{
	attr_dlg_t *actx = hid_ctx;
	mbtk_run_window(actx->win);
}

void rnd_mbtk_attr_dlg_raise(void *hid_ctx)
{
	attr_dlg_t *actx = hid_ctx;
	mbtk_window_raise(actx->win);
}

void rnd_mbtk_attr_dlg_close(void *hid_ctx)
{
	attr_dlg_t *actx = hid_ctx;
	mbtk_window_close(actx->win);
}

void rnd_mbtk_attr_dlg_free(void *hid_ctx)
{
	attr_dlg_t *actx = hid_ctx;
	rnd_mbtk_t *mctx = actx->mctx;

	mbtk_window_close(actx->win);
	gdl_remove(&mctx->dad_dialogs, actx, link);

	free(actx->aw);
	free((char *)actx->id);
	free(actx);
}

void rnd_mbtk_attr_dlg_property(void *hid_ctx, rnd_hat_property_t prop, const rnd_hid_attr_val_t *val)
{
	TODO("implement");
}

int rnd_mbtk_attr_dlg_widget_state(void *hid_ctx, int idx, int enabled)
{
	TODO("implement");
}

int rnd_mbtk_attr_dlg_widget_hide(void *hid_ctx, int idx, rnd_bool hide)
{
	attr_dlg_t *actx = hid_ctx;

	if ((idx < 0) || (idx >= actx->n_attrs))
		return -1;

	if (actx->attrs[idx].type == RND_HATT_BEGIN_COMPOUND)
		return -1;

	if (actx->attrs[idx].type == RND_HATT_END) {
		rnd_hid_compound_t *cmp = actx->attrs[idx].wdata;
		if ((cmp != NULL) && (cmp->widget_state != NULL))
			return cmp->widget_hide(&actx->attrs[idx], actx, idx, hide);
		else
			return -1;
	}

	if (hide)
		mbtk_widget_hide(actx->aw[idx].w, 0);
	else
		mbtk_widget_show(actx->aw[idx].w, 0);

	return 0;
}

int rnd_mbtk_attr_dlg_widget_poke(void *hid_ctx, int idx, int argc, fgw_arg_t argv[])
{

}

int rnd_mbtk_attr_dlg_widget_focus(void *hid_ctx, int idx)
{

}

int rnd_mbtk_attr_dlg_set_value(void *hid_ctx, int idx, const rnd_hid_attr_val_t *val)
{

}

void rnd_mbtk_attr_dlg_set_help(void *hid_ctx, int idx, const char *val)
{

}


rnd_design_t *rnd_mbtk_attr_get_dad_hidlib(void *hid_ctx)
{
	attr_dlg_t *actx = hid_ctx;
	return actx->mctx->hidlib;
}

void rnd_mbtk_attr_dlg_free_all(rnd_mbtk_t *gctx)
{

}


