
/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2023  Ladislav Vaiz <ok1zia@nagano.cz>
    and authors of web browser Links 0.96

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include "header.h"

#include "bfu.h"
//#include "charsets.h"
#include "kbdbind.h"
#include "kbd.h"
//#include "language2.h"
#include "main.h"
#include "menu.h"
#include "rc.h"
#include "session.h"
#include "terminal.h"
#include "zosk.h"

struct memory_list *getml(void *p, ...)
{
    struct memory_list *ml;
    va_list ap;
    int n = 0;
    void *q = p;
    va_start(ap, p);
    while (q) n++, q = va_arg(ap, void *);
    va_end(ap);
    if (!(ml = (struct memory_list*)g_malloc(sizeof(struct memory_list) + n * sizeof(void *)))) return NULL;
    ml->n = n;
    n = 0;
    q = p;
    va_start(ap, p);
    while (q) ml->p[n++] = q, q = va_arg(ap, void *);
    va_end(ap);
    return ml;
}

void add_to_ml(struct memory_list **ml, ...)
{
    struct memory_list *nml;
    va_list ap;
    int n = 0;
    void *q;
    if (!*ml) {
        if (!(*ml = (struct memory_list*)g_malloc(sizeof(struct memory_list)))) return;
        (*ml)->n = 0;
    }
    va_start(ap, ml);
    while ((q = va_arg(ap, void *))) n++;
    if (!(nml = (struct memory_list*)g_realloc(*ml, sizeof(struct memory_list) + (n + (*ml)->n) * sizeof(void *))))
        return;
    va_start(ap, ml);
    while ((q = va_arg(ap, void *))) nml->p[nml->n++] = q;
    *ml = nml;
}

void freeml(struct memory_list *ml)
{
    int i;
    if (!ml) return;
    for (i = 0; i < ml->n; i++) g_free(ml->p[i]);
    g_free(ml);
}

char m_bar = 0;


void do_menu_selected(struct menu_item *items, void *data, int selected)
{
    struct menu *menu;
    if ((menu = g_new0(struct menu, 1))) {
        menu->selected = selected;
        menu->view = 0;
        menu->items = items;
        menu->data = data;
        add_window(menu_func, menu);
    } else if (items->free_i) {
        int i;
        for (i = 0; items[i].text; i++) {
            if (items[i].free_i & 2) 
				g_free(items[i].text);
            if (items[i].free_i & 4) 
				g_free(items[i].rtext);
        }
        g_free(items);
    }
}

void do_menu(struct menu_item *items, void *data)
{
    do_menu_selected(items, data, 0);
}

static void select_menu(struct menu *menu)
{
    /*int x = menu->x + 4;
    int y = menu->y + menu->selected - menu->view + 2;*/
    struct menu_item *it = &menu->items[menu->selected];
    void (*func)(void *, void *) = it->func;
	void *arg1, *arg2;
	int dofunc = 1;
    
    if (menu->selected < 0 || menu->selected >= menu->ni || it->hotkey == M_BAR) return;
	if (menu->items[menu->selected].funcall){
		menu->items[menu->selected].funcall(menu);
		dofunc = 0;
	}
	arg1 = it->data;
	arg2 = menu->data;
    if (!it->in_m) {
        struct window *win, *win1;
        for (win = term->windows.next; 
			 (void *)win != &term->windows && (win->handler == menu_func || win->handler == mainmenu_func); ){
			win1 = win->next;
			delete_window(win);
			win = win1;
		}

    }
	if (dofunc){
	    func(arg1, arg2);
	}
}

static void count_menu_size(struct terminal *term, struct menu *menu)
{
    int sx = term->x;
    int sy = term->y;
    int mx = 4;
    int my;
	int s;
    for (my = 0; menu->items[my].text; my++) {
        s = strlen(_(menu->items[my].text)) + strlen(_(menu->items[my].rtext)) + MENU_HOTKEY_SPACE * (_(menu->items[my].rtext)[0] != 0) + 4;
		if (menu->items[my].checkbox) s += 4;
        if (s > mx) mx = s;
    }
	if (menu->items[my].rtext){
		menu->title = menu->items[my].rtext;
		s = strlen(menu->title) + 4;
        if (s > mx) mx = s;
	}
    menu->ni = my;
    my += 2;
    if (mx > sx) mx = sx;
    if (my > sy) my = sy;
    menu->xw = mx;
    menu->yw = my;
    if ((menu->x = menu->xp) < 0) menu->x = 0;
    if ((menu->y = menu->yp) < 0) menu->y = 0;
    if (menu->x + mx > sx) menu->x = sx - mx;
    if (menu->y + my > sy) menu->y = sy - my;
}

static void scroll_menu(struct menu *menu, int d)
{
    int c = 0;
    int w = menu->yw - 2;
    int scr_i = SCROLL_ITEMS > (w-1)/2 ? (w-1)/2 : SCROLL_ITEMS;
    if (scr_i < 0) scr_i = 0;
    if (w < 0) w = 0;
    menu->selected += d;
    while (1) {
        if (c++ > menu->ni) {
            menu->selected = -1;
            menu->view = 0;
            return;
        }
        if (menu->selected < 0) menu->selected = 0;
        if (menu->selected >= menu->ni) menu->selected = menu->ni - 1;
        /*if (menu->selected < 0) menu->selected = menu->ni - 1;
        if (menu->selected >= menu->ni) menu->selected = 0;*/
        if (menu->ni && menu->items[menu->selected].hotkey != M_BAR) break;
        menu->selected += d;
    }
    /*debug("1:%d %d %d %d", menu->ni, w, menu->view, menu->selected);*/
    if (menu->selected < menu->view + scr_i) menu->view = menu->selected - scr_i;
    if (menu->selected >= menu->view + w - scr_i - 1) menu->view = menu->selected - w + scr_i + 1;
    if (menu->view > menu->ni - w) menu->view = menu->ni - w;
    if (menu->view < 0) menu->view = 0;
    /*debug("2:%d %d %d %d", menu->ni, w, menu->view, menu->selected);*/
}

static void display_menu(struct menu *menu)
{
    int p, s;
    fill_area(menu->x+1, menu->y+1, menu->xw-2, menu->yw-2, COLOR_MENU1);
    draw_frame(menu->x, menu->y, menu->xw, menu->yw, COLOR_MENU_FRAME, 1);
	if (menu->title) {
		set_char(menu->x + 1, menu->y, COLOR_MENU_FRAME | ' ');
		print_text(menu->x + 2, menu->y, strlen(menu->title), menu->title, COLOR_MENU_FRAME);
		set_char(menu->x + 2 + strlen(menu->title), menu->y, COLOR_MENU_FRAME | ' ');
	}
    for (p = menu->view, s = menu->y + 1; p < menu->ni && p < menu->view + menu->yw - 2; p++, s++) {
        int x;
        int h = 0;
        unsigned char c;
        char *tmptext = _(menu->items[p].text);
        int co = p == menu->selected ? h = 1, COLOR_MENU_SELECTED : COLOR_MENU1;
        if (h) {
            set_cursor(menu->x+1, s, term->x - 1, term->y - 1);
            /*set_window_ptr(menu->win, menu->x+3, s+1);*/
            set_window_ptr(menu->win, menu->x+menu->xw, s);
            fill_area(menu->x+1, s, menu->xw-2, 1, co);
        }
        if (menu->items[p].hotkey != M_BAR || (tmptext && tmptext[0])) {
			int xc = 0;
            int l = strlen(_(menu->items[p].rtext));
            for (x = l - 1; x >= 0 && menu->xw - 4 >= l - x && (c = _(menu->items[p].rtext)[x]); x--)
                set_char(menu->x + menu->xw - 2 - l + x, s, c | co);
			if (menu->items[p].checkbox) {
                set_char(menu->x + x + 2 + 1, s, '[' | co);
				if (menu->items[p].checked) set_char(menu->x + x + 2 + 2, s, 'X' | co);
                set_char(menu->x + x + 2 + 3, s, ']' | co);
				xc = 4;
			}
            for (x = 0; x < menu->xw - 4 && (c = tmptext[x]); x++)
                set_char(menu->x + x + 2 + xc, s, !h && strchr(_(menu->items[p].hotkey), z_char_uc(c)) ? h = 1 , COLOR_MENU_HOTKEY | c : co | c);
        } else {
            set_char(menu->x, s, COLOR_MENU_FRAME | ATTR_FRAME | 0xc3);
            fill_area(menu->x+1, s, menu->xw-2, 1, COLOR_MENU_FRAME | ATTR_FRAME | 0xc4);
            set_char(menu->x+menu->xw-1, s, COLOR_MENU_FRAME | ATTR_FRAME | 0xb4);
        }
    }
    redraw_from_window(menu->win);
}

void menu_func(struct window *win, struct event *ev, int fwd)
{
    //dbg("menu_func");
    int s = 0;
    struct menu *menu = (struct menu*)win->data;
    struct window *w1;
    menu->win = win;
    switch (ev->ev) {
        case EV_INIT:
        case EV_RESIZE:
        case EV_REDRAW:
            get_parent_ptr(win, &menu->xp, &menu->yp);
            count_menu_size(win->term, menu);
            menu->selected--;
            scroll_menu(menu, 1);
        /*case EV_REDRAW:*/
            display_menu(menu);
            break;
        case EV_MOUSE:
            if (ev->b & B_MOVE) break;
            if (ev->x < menu->x || ev->x >= menu->x+menu->xw || ev->y < menu->y || ev->y >= menu->y+menu->yw) {
                if ((ev->b & BM_ACT) == B_DOWN) del:delete_window_ev(win, ev);
                else for (w1 = win; (void *)w1 != &win->term->windows; w1 = w1->next) {
                    struct menu *m1;
                    if (w1->handler == mainmenu_func) {
                        if (!ev->y) goto del;
                        break;
                    }
                    if (w1->handler != menu_func) break;
                    m1 = (struct menu*)w1->data;
                    if (ev->x > m1->x && ev->x < m1->x+m1->xw-1 && ev->y > m1->y && ev->y < m1->y+m1->yw-1) goto del;
                }
            } else {
                if (!(ev->x < menu->x || ev->x >= menu->x+menu->xw || ev->y < menu->y/*+1*/ || ev->y >= menu->y+menu->yw/*-1*/)) {
                    int s = ev->y - menu->y-1 + menu->view;
					if (menu->items[s].hotkey == M_BAR){
#ifdef Z_HAVE_SDL
						if (zsdl){
							double m = fmod((double)ev->my, (double)zsdl->font_h);
							if (m < zsdl->font_h / 2) s--;
							else s++;
						}else
#endif
							 {
							if (menu->selected < 0) break;
							s = menu->selected;
						}
					}
					if (s < 0) s = 0;
					if (s >= menu->ni) s = menu->ni - 1;

                    if (s >= 0 && s < menu->ni) {
                        menu->selected = s;
                        scroll_menu(menu, 0);
                        display_menu(menu);
                        if ((ev->b & BM_ACT) == B_UP /*|| menu->items[s].in_m*/) {
							struct menu_item *it = &menu->items[s];
							int relx = ev->x - menu->x;
							if (it->checkbox && relx < 6) {
								it->checked = !it->checked;
								redraw_later();
							}else{
								select_menu(menu);
							}
						}
                    }
                }
            }
            break;
        case EV_KBD:
            switch (kbd_action(KM_MENU, ev)) {
                case ACT_LEFT:
                case ACT_RIGHT:
                    if ((void *)win->next != &win->term->windows && win->next->handler == mainmenu_func) goto mm;
                    /*for (w1 = win; (void *)w1 != &win->term->windows; w1 = w1->next) {
                        if (w1->handler == mainmenu_func) goto mm;
                        if (w1->handler != menu_func) break;
                    }*/
                    if (kbd_action(KM_MENU, ev) == ACT_RIGHT) goto enter;
                    delete_window(win);
                    goto break2;
                case ACT_UP: scroll_menu(menu, -1); break;
                case ACT_DOWN: scroll_menu(menu, 1); break;
                case ACT_HOME: menu->selected = -1, scroll_menu(menu, 1); break;
                case ACT_END: menu->selected = menu->ni, scroll_menu(menu, -1); break;
                case ACT_PAGE_UP:
                    if ((menu->selected -= menu->yw - 3) < -1) menu->selected = -1;
                    if ((menu->view -= menu->yw - 2) < 0) menu->view = 0;
                    scroll_menu(menu, -1);
                    break;
                case ACT_PAGE_DOWN:
                    if ((menu->selected += menu->yw - 3) > menu->ni) menu->selected = menu->ni;
                    if ((menu->view += menu->yw - 2) >= menu->ni - menu->yw + 2) menu->view = menu->ni - menu->yw + 2;
                    scroll_menu(menu, 1);
                    break;
                default:
                    if ((ev->x >= KBD_F1 && ev->x <= KBD_F12) || ev->y == KBD_ALT) {
                        mm:
                        delete_window_ev(win, ev);
                        goto break2;
                    }
                    if (ev->x == KBD_ESC) {
                        delete_window_ev(win, (void *)win->next != &win->term->windows && win->next->handler == mainmenu_func ? ev : NULL);
                        goto break2;
                    }
                    if (ev->x > ' ' && ev->x < 256) {
                        int i;
                        for (i = 0; i < menu->ni; i++)
                            if (strchr(_(menu->items[i].hotkey), z_char_uc(ev->x))) {
                                menu->selected = i;
                                scroll_menu(menu, 0);
                                s = 1;
                            }
                    }
                    break;
            }
			if (ev->x == ' '){
				menu->items[menu->selected].checked = !menu->items[menu->selected].checked;
				scroll_menu(menu, 1);
			}
            display_menu(menu);
            if (s || ev->x == KBD_ENTER) {
                enter:
                select_menu(menu);
            }
            break2:
            break;
        case EV_ABORT:
            if (menu->items->free_i) {
                int i;
                for (i = 0; menu->items[i].text; i++) {
                    if (menu->items[i].free_i & 2) 
						g_free(menu->items[i].text);
                    if (menu->items[i].free_i & 4) 
						g_free(menu->items[i].rtext);
                }
                g_free(menu->items);
            }
            break;
    }
}

void do_mainmenu(struct menu_item *items, void *data, int sel)
{
    struct mainmenu *menu;
    if ((menu = g_malloc(sizeof(struct mainmenu)))) {
        menu->selected = sel == -1 ? 0 : sel;
        menu->items = items;
        menu->data = data;
        add_window(mainmenu_func, menu);
        if (sel != -1) {
            struct event ev = {EV_KBD, KBD_ENTER, 0, 0};
            struct window *win = term->windows.next;
            win->handler(win, &ev, 0);
        }
    }
}

static void display_mainmenu(struct mainmenu *menu)
{
    int i;
    int p = 2;
    fill_area(0, 0, term->x, 1, COLOR_MAINMENU | ' ');
	set_char(0, 0, COLOR_MAINMENU | 0xad);
	for (i = 0; menu->items[i].text; i++) {
        int s = 0;
        int j;
        unsigned char c;
        char *tmptext = _(menu->items[i].text);
        int co = i == menu->selected ? s = 1, COLOR_MAINMENU_SELECTED : COLOR_MAINMENU;
        if (s) {
            fill_area(p, 0, 2, 1, co);
            fill_area(p+strlen(tmptext)+2, 0, 2, 1, co);
            menu->sp = p;
            set_cursor(p, 0, term->x - 1, term->y - 1);
            set_window_ptr(menu->win, p, 1);
        }
        p += 2;
        for (j = 0; (c = tmptext[j]); j++, p++)
            set_char(p, 0, (!s && strchr(_(menu->items[i].hotkey), z_char_uc(c)) ? s = 1, COLOR_MAINMENU_HOTKEY : co) | c);
        p += 2;
    }
    menu->ni = i;
    redraw_from_window(menu->win);
}

static void select_mainmenu(struct mainmenu *menu)
{
    struct menu_item *it = &menu->items[menu->selected];
    if (menu->selected < 0 || menu->selected >= menu->ni || it->hotkey == M_BAR) return;
    if (!it->in_m) {
        struct window *win;
        for (win = term->windows.next; (void *)win != &term->windows && (win->handler == menu_func || win->handler == mainmenu_func); delete_window(win)) ;
    }
    it->func(it->data, menu->data);
}

void mainmenu_func(struct window *win, struct event *ev, int fwd)
{
    //dbg("mainmenu_func");
    int s = 0;
    struct mainmenu *menu = (struct mainmenu *)win->data;
    menu->win = win;
    switch(ev->ev) {
        case EV_INIT:
        case EV_RESIZE:
        case EV_REDRAW:
            display_mainmenu(menu);
            break;
        case EV_MOUSE:
            if (ev->b & B_MOVE) break;
            if ((ev->b & BM_ACT) == B_DOWN && ev->y) delete_window_ev(win, ev);
            else if (!ev->y) {
                int i;
                int p = 2;
                for (i = 0; i < menu->ni; i++) {
                    int o = p;
                    char *tmptext = _(menu->items[i].text);
                    p += strlen(tmptext) + 4;
                    if (ev->x >= o && ev->x < p) {
                        menu->selected = i;
                        display_mainmenu(menu);
                        if ((ev->b & BM_ACT) == B_UP || menu->items[s].in_m) select_mainmenu(menu);
                        break;
                    }
                }
            }
            break;
        case EV_KBD:
            if (ev->x == ' ' || ev->x == KBD_ENTER || ev->x == KBD_DOWN || ev->x == KBD_UP || ev->x == KBD_PGDN || ev->x == KBD_PGUP) {
                select_mainmenu(menu);
                break;
            }
            if (ev->x == KBD_LEFT) {
                if (!menu->selected--) menu->selected = menu->ni - 1;
                s = 1;
            }
            if (ev->x == KBD_RIGHT) {
                if (++menu->selected >= menu->ni) menu->selected = 0;
                s = 1;
            }
            if ((ev->x == KBD_LEFT || ev->x == KBD_RIGHT) && fwd) {
                display_mainmenu(menu);
                select_mainmenu(menu);
                break;
            }
            if (ev->x > ' ' && ev->x < 256) {
                int i;
                s = 1;
                for (i = 0; i < menu->ni; i++)
                    if (strchr(_(menu->items[i].hotkey), z_char_uc(ev->x))) {
                        menu->selected = i;
                        s = 2;
                    }
            } else if (!s) {
                delete_window_ev(win, ev->x != KBD_ESC ? ev : NULL);
                break;
            }
            display_mainmenu(menu);
            if (s == 2) select_mainmenu(menu);
            break;
        case EV_ABORT:
            break;
    }
}

// free_i:
// 0x02 - free text
// 0x04 - free rtext
struct menu_item *new_menu(int free_i)
{
    struct menu_item *mi;
    if (!(mi = (struct menu_item*)g_malloc(sizeof(struct menu_item)))) return NULL;
    memset(mi, 0, sizeof(struct menu_item));
    mi->free_i = free_i;
    return mi;
}

struct menu_item *add_to_menu(struct menu_item **mi, char *text, char *rtext, char *hotkey, void (*func)(void *, void *), void *data, int in_m)
{
    struct menu_item *mii;
    int n;
    for (n = 0; (*mi)[n].text; n++) ;
    mii = (struct menu_item*)g_realloc(*mi, (n + 2) * sizeof(struct menu_item));
    *mi = mii;
    memcpy(mii + n + 1, mii + n, sizeof(struct menu_item));
    mii[n].text = text;
    mii[n].rtext = rtext;
    mii[n].hotkey = hotkey;
    mii[n].func = func;
    mii[n].data = data;
    mii[n].in_m = in_m;
    mii[n].checkbox = 0;
    mii[n].checked = 0;
	return mii + n;
}

void do_dialog(struct dialog *dlg, struct memory_list *ml)
{
    struct dialog_data *dd;
    struct dialog_item *d;
    int n = 0;
    for (d = dlg->items; d->type != D_END; d++) n++;
    if (!(dd = (struct dialog_data *)g_malloc(sizeof(struct dialog_data) + sizeof(struct dialog_item_data) * n))) return;
    dd->dlg = dlg;
    dd->n = n;
    dd->ml = ml;
    add_window(dialog_func, dd);
}

void display_dlg_item(struct dialog_data *dlg, struct dialog_item_data *di, int sel)
{
    switch(di->item->type) {
        int co;
        char *text;
        case D_CHECKBOX:
            if (di->checked) print_text(di->x, di->y, 3, "[X]", COLOR_DIALOG_CHECKBOX);
            else print_text(di->x, di->y, 3, "[ ]", COLOR_DIALOG_CHECKBOX);
            if (sel) {
                set_cursor(di->x + 1, di->y, di->x + 1, di->y);
                set_window_ptr(dlg->win, di->x, di->y);
            }
            break;
        case D_CHECKBOX3:
            if (di->checked==2) 
                print_text(di->x, di->y, 3, "[O]", COLOR_DIALOG_CHECKBOX);
            else if (di->checked==1) 
                print_text(di->x, di->y, 3, "[X]", COLOR_DIALOG_CHECKBOX);
            else 
                print_text(di->x, di->y, 3, "[ ]", COLOR_DIALOG_CHECKBOX);
            if (sel) {
                set_cursor(di->x + 1, di->y, di->x + 1, di->y);
                set_window_ptr(dlg->win, di->x, di->y);
            }
            break;
        case D_FIELD_PASS:
        case D_FIELD:
            if (di->vpos + di->l <= di->cpos) di->vpos = di->cpos - di->l + 1;
            if (di->vpos > di->cpos) di->vpos = di->cpos;
            if (di->vpos < 0) di->vpos = 0;
            fill_area(di->x, di->y, di->l, 1, COLOR_DIALOG_FIELD);
            if (di->item->type == D_FIELD) {
                print_text(di->x, di->y, strlen(di->cdata + di->vpos) <= di->l ? strlen(di->cdata + di->vpos) : di->l, di->cdata + di->vpos, COLOR_DIALOG_FIELD_TEXT);
            }
            else fill_area(di->x, di->y, strlen(di->cdata + di->vpos) <= di->l ? strlen(di->cdata + di->vpos) : di->l, 1, COLOR_DIALOG_FIELD_TEXT | '*');
            if (sel) {
                set_cursor(di->x + di->cpos - di->vpos, di->y, di->x + di->cpos - di->vpos, di->y);
                set_window_ptr(dlg->win, di->x, di->y);
            }
            break;
        case D_BUTTON:
            co = sel ? COLOR_DIALOG_BUTTON_SELECTED : COLOR_DIALOG_BUTTON;
            text = _(di->item->text);
            print_text(di->x, di->y, 2, "[ ", co);
            print_text(di->x + 2, di->y, strlen(text), text, co);
            print_text(di->x + 2 + strlen(text), di->y, 2, " ]", co);
            if (sel) {
                set_cursor(di->x + 2, di->y, di->x + 2, di->y);
                set_window_ptr(dlg->win, di->x, di->y);
            }
            break;
        case D_BOX:
            /* Draw a hierarchy box */
            show_dlg_item_box(dlg, di);
            break;
		case D_TEXT:
			break;
        default:
            dbg("Tried to draw unknown ");
    }
}

static void dlg_select_item(struct dialog_data *dlg, struct dialog_item_data *di)
{
    if (di->item->type == D_CHECKBOX) {
        if (!di->item->gid) di -> checked = *(int *)di->cdata = !*(int *)di->cdata;
        else {
            int i;
            for (i = 0; i < dlg->n; i++) {
                if (dlg->items[i].item->type == D_CHECKBOX && dlg->items[i].item->gid == di->item->gid) {
                    *(int *)dlg->items[i].cdata = di->item->gnum;
                    dlg->items[i].checked = 0;
                    display_dlg_item(dlg, &dlg->items[i], 0);
                }
            }
            di->checked = 1;
        }
        display_dlg_item(dlg, di, 1);
    }
    else if (di->item->type == D_CHECKBOX3) {
        if (!di->item->gid) {
            (*(int *)di->cdata)++;
            if (*(int *)di->cdata == 3) *(int *)di->cdata = 0;
            di -> checked = *(int *)di->cdata;
        }else{
            int i;
            for (i = 0; i < dlg->n; i++) {
                if (dlg->items[i].item->type == D_CHECKBOX3 && dlg->items[i].item->gid == di->item->gid) {
                    *(int *)dlg->items[i].cdata = di->item->gnum;
                    dlg->items[i].checked = 0;
                    display_dlg_item(dlg, &dlg->items[i], 0);
                }
            }
            di->checked = 1;
        }
        display_dlg_item(dlg, di, 1);
    }
    else if (di->item->type == D_BUTTON) di->item->fn(dlg, di);
}

static void dlg_set_history(struct dialog_item_data *di)
{
    char *s = "";
    int l;
    if ((void *)di->cur_hist != &di->history) s = di->cur_hist->d;
    if ((l = strlen(s)) > di->item->dlen) l = di->item->dlen - 1;
    memcpy(di->cdata, s, l);
    di->cdata[l] = 0;
    di->cpos = l;
}

static int dlg_mouse(struct dialog_data *dlg, struct dialog_item_data *di, struct event *ev)
{
    switch (di->item->type) {
        case D_BUTTON:
            if (ev->y != di->y || ev->x < di->x || ev->x >= di->x + strlen(_(di->item->text)) + 4) return 0;
			if (dlg->dlg->items[di - dlg->items].type == D_TEXT) return 0;
            display_dlg_item(dlg, &dlg->items[dlg->selected], 0);
            dlg->selected = di - dlg->items;
            display_dlg_item(dlg, di, 1);
            if ((ev->b & BM_ACT) == B_UP) dlg_select_item(dlg, di);
            return 1;
        case D_FIELD_PASS:
        case D_FIELD:
            if (ev->y != di->y || ev->x < di->x || ev->x >= di->x + di->l) return 0;
			if (dlg->dlg->items[di - dlg->items].type == D_TEXT) return 0;
			if (!cfg->usetouch){
				if ((di->cpos = di->vpos + ev->x - di->x) > strlen(di->cdata)) di->cpos = strlen(di->cdata);
			}
            display_dlg_item(dlg, &dlg->items[dlg->selected], 0);
            dlg->selected = di - dlg->items;
            display_dlg_item(dlg, di, 1);
#ifdef Z_HAVE_SDL
			if (gses && cfg->usetouch){
				if (gses->osk) zosk_free(gses->osk);
				gses->osk = zosk_init(sdl->screen, ZOSK_ENTERCLOSE, di->cdata);
				zosk_portrait(gses->osk);
			}
#endif

            return 1;
        case D_CHECKBOX:
        case D_CHECKBOX3:
            if (ev->y != di->y || ev->x < di->x || ev->x >= di->x + 3) return 0;
            display_dlg_item(dlg, &dlg->items[dlg->selected], 0);
            dlg->selected = di - dlg->items;
            display_dlg_item(dlg, di, 1);
            if ((ev->b & BM_ACT) == B_UP) dlg_select_item(dlg, di);
            return 1;
        case D_BOX:
            if ((ev->b & BM_ACT) == B_UP) {
                if ( (ev->y >= di->y)  && (ev->x >= di->x && ev->x <= di->l + di->x) ) {
                    /* Clicked in the box. */
                    int offset;
                    offset = ev->y - di->y;
                    box_sel_set_visible(di, offset);
                    display_dlg_item(dlg, di, 1);
                    return 1;
                }
            } /*else if ((ev->b & BM_ACT) == B_DRAG) {
                debug("drag");
            }*/
		case D_TEXT:

			break;
    }
    return 0;
}

static void redraw_dialog(struct dialog_data *dlg)
{
    int i;
    draw_frame(dlg->x + DIALOG_LEFT_BORDER, dlg->y + DIALOG_TOP_BORDER, dlg->xw - 2 * DIALOG_LEFT_BORDER, dlg->yw - 2 * DIALOG_TOP_BORDER, COLOR_DIALOG_FRAME, DIALOG_FRAME);
    i = strlen(_(dlg->dlg->title));
    print_text((dlg->xw - i) / 2 + dlg->x - 1, dlg->y + DIALOG_TOP_BORDER, 1, " ", COLOR_DIALOG_TITLE);
    print_text((dlg->xw - i) / 2 + dlg->x, dlg->y + DIALOG_TOP_BORDER, i, _(dlg->dlg->title), COLOR_DIALOG_TITLE);
    print_text((dlg->xw - i) / 2 + dlg->x + i, dlg->y + DIALOG_TOP_BORDER, 1, " ", COLOR_DIALOG_TITLE);
    for (i = 0; i < dlg->n; i++) display_dlg_item(dlg, &dlg->items[i], i == dlg->selected);
    redraw_from_window(dlg->win);
}


void dialog_func(struct window *win, struct event *ev, int fwd)
{
    int i;
    struct dialog_data *dlg = (struct dialog_data *)win->data;
    struct dialog_item_data *di;

    dlg->win = win;
    //dbg("dialog_func(%p,[%d,%d,%d,%d],%d)\n",win,ev->ev,ev->x,ev->y,ev->b,fwd);
    /* Use nonstandard event handlers */
    if (dlg->dlg->handle_event && ((dlg->dlg->handle_event)(dlg, ev) == EVENT_PROCESSED) ) {
        return;
    }
    
    switch (ev->ev) {
        case EV_INIT:
            for (i = 0; i < dlg->n; i++) {
                struct dialog_item_data *di = &dlg->items[i];
                memset(di, 0, sizeof(struct dialog_item_data));
                di->item = &dlg->dlg->items[i];
                if ((di->cdata = (char *)g_malloc(di->item->dlen)))
                    memcpy(di->cdata, di->item->data, di->item->dlen);
                if (di->item->type == D_CHECKBOX) {
                    if (di->item->gid) {
                        if (*(int *)di->cdata == di->item->gnum) di->checked = 1;
                    } else if (*(int *)di->cdata) di->checked = 1;
                } 
                if (di->item->type == D_CHECKBOX3) {
                    di->checked = *(int *)di->cdata;
                } 
                if (di->item->type == D_BOX) {
                    /* Freed in bookmark_dialog_abort_handler() */
                    di->cdata = g_malloc( sizeof(struct dlg_data_item_data_box) );
                    ((struct dlg_data_item_data_box*)di->cdata)->sel = -1;
                    ((struct dlg_data_item_data_box*)di->cdata)->box_top = 0;
                    ((struct dlg_data_item_data_box*)di->cdata)->list_len = -1;

                    init_list(((struct dlg_data_item_data_box*)di->cdata)->items);
                }
                init_list(di->history);
                if (di->item->type == D_FIELD || di->item->type == D_FIELD_PASS) {
                    /*if (di->item->history) {
                        struct history_item *j;
                        //int l = di->item->dlen;
                        foreach(j, di->item->history->items) {
                            struct history_item *hi;
                            if (!(hi = g_malloc(sizeof(struct history_item) + strlen(j->d) + 1))) continue;
                            strcpy(hi->d, j->d);
                            struct list_head *h = &di->history;
                            add_to_list(*h, hi);
                        }
                    } */
                    di->cpos = strlen(di->cdata);
                } 
            }
            dlg->selected = 0;
			while (dlg->dlg->items[dlg->selected].type == D_TEXT) dlg->selected ++;  // no focus for D_TEXT
        case EV_RESIZE:
        case EV_REDRAW:
            dlg->dlg->fn(dlg);
            redraw_dialog(dlg);
            break;
        case EV_MOUSE:
            if (ev->b & B_MOVE) break;

            for (i = 0; i < dlg->n; i++) if (dlg_mouse(dlg, &dlg->items[i], ev)) break;
            break;
        case EV_KBD:
            di = &dlg->items[dlg->selected];
            if (di->item->type == D_FIELD || di->item->type == D_FIELD_PASS) {
                
                if (di->item->tabcompl && ev->x == KBD_F2 && ev->y == 0){
                    di->item->tabi++;
                    do_tab_compl(di, di->item->tabi);
                    if (di->item->tabi == 2) di->item->tabi = 0;
                    goto dsp_f;
                }else{
                    di->item->tabi = 0;
                }
                switch (kbd_action(KM_EDIT, ev)) {
                    case ACT_UP:
                        if ((void *)di->cur_hist->prev != &di->history) {
                            di->cur_hist = di->cur_hist->prev;
                            dlg_set_history(di);
                            goto dsp_f;
                        }
                        break;
                    case ACT_DOWN:
                        if ((void *)di->cur_hist != &di->history) {
                            di->cur_hist = di->cur_hist->next;
                            dlg_set_history(di);
                            goto dsp_f;
                        }
                        break;
                    case ACT_RIGHT:
                        if (di->cpos < strlen(di->cdata)) di->cpos++;
                        goto dsp_f;
                    case ACT_LEFT:
                        if (di->cpos > 0) di->cpos--;
                        goto dsp_f;
                    case ACT_HOME:
                        di->cpos = 0;
                        goto dsp_f;
                    case ACT_END:
                        di->cpos = strlen(di->cdata);
                        goto dsp_f;
                    case ACT_BACKSPACE:
                        if (di->cpos) {
                            memmove(di->cdata + di->cpos - 1, di->cdata + di->cpos, strlen(di->cdata) - di->cpos + 1);
                            di->cpos--;
                        }
                        goto dsp_f;
                    case ACT_DELETE:
                        if (di->cpos < strlen(di->cdata))
                            memmove(di->cdata + di->cpos, di->cdata + di->cpos + 1, strlen(di->cdata) - di->cpos + 1);
                        goto dsp_f;
                    case ACT_KILL_TO_BOL:
                        memmove(di->cdata, di->cdata + di->cpos, strlen(di->cdata + di->cpos) + 1);
                        di->cpos = 0;
                        goto dsp_f;
                    case ACT_KILL_TO_EOL:
                        di->cdata[di->cpos] = 0;
                        goto dsp_f;
                        case ACT_COPY_CLIPBOARD:
                        /* Copy to clipboard */
                        set_clipboard_text(di->cdata);
                        break;  /* We don't need to redraw */
                    case ACT_CUT_CLIPBOARD:
                        /* Cut to clipboard */                      
                        set_clipboard_text(di->cdata);
                        di->cdata[0] = 0;
                        di->cpos = 0;
                        goto dsp_f;
                    case ACT_PASTE_CLIPBOARD: {
                        /* Paste from clipboard */
                        char * clipboard = get_clipboard_text();
                        strncpy(di->cdata , clipboard, di->item->dlen);
                        di->cdata[di->item->dlen - 1] = 0;
                        di->cpos = strlen(di->cdata);
                        g_free(clipboard);
                        goto dsp_f;
                    }
                    default:
                        if (ev->x >= ' ' && ev->x < 0x100 && !ev->y) {
                            if (strlen(di->cdata) < di->item->dlen - 1) {
                                memmove(di->cdata + di->cpos + 1, di->cdata + di->cpos, strlen(di->cdata) - di->cpos + 1);
                                di->cdata[di->cpos++] = ev->x;
                            }
                            goto dsp_f;
                        }
                }
                goto gh;
                dsp_f:
                display_dlg_item(dlg, di, 1);
                redraw_from_window(dlg->win);
                break;
            }
            if ((ev->x == KBD_ENTER && di->item->type == D_BUTTON) || ev->x == ' ') {
                dlg_select_item(dlg, di);
                break;
            }
            gh:
            if (ev->x > ' ' && ev->x < 0x100) for (i = 0; i < dlg->n; i++)
                if (dlg->dlg->items[i].type == D_BUTTON && z_char_uc(_(dlg->dlg->items[i].text)[0]) == z_char_uc(ev->x)) {
                    sel:
                    if (dlg->selected != i) {
                        display_dlg_item(dlg, &dlg->items[dlg->selected], 0);
                        display_dlg_item(dlg, &dlg->items[i], 1);
                        dlg->selected = i;
                    }
                    dlg_select_item(dlg, &dlg->items[i]);
                    goto bla;
            }
            if (ev->x == KBD_ENTER) for (i = 0; i < dlg->n; i++)
                if (dlg->dlg->items[i].type == D_BUTTON && dlg->dlg->items[i].gid & B_ENTER) goto sel;
            if (ev->x == KBD_ESC) for (i = 0; i < dlg->n; i++)
                if (dlg->dlg->items[i].type == D_BUTTON && dlg->dlg->items[i].gid & B_ESC) goto sel;
            if ((ev->x == KBD_TAB && !ev->y) || ev->x == KBD_DOWN || ev->x == KBD_RIGHT) {
                display_dlg_item(dlg, &dlg->items[dlg->selected], 0);
				do{
					if ((++dlg->selected) >= dlg->n) dlg->selected = 0;
				}while (dlg->dlg->items[dlg->selected].type == D_TEXT);
                display_dlg_item(dlg, &dlg->items[dlg->selected], 1);
                redraw_from_window(dlg->win);
                break;
            }
            if ((ev->x == KBD_TAB && ev->y) || ev->x == KBD_UP || ev->x == KBD_LEFT) {
                display_dlg_item(dlg, &dlg->items[dlg->selected], 0);
				do{ 
					if ((--dlg->selected) < 0) dlg->selected = dlg->n - 1;
				}while (dlg->dlg->items[dlg->selected].type == D_TEXT);
                display_dlg_item(dlg, &dlg->items[dlg->selected], 1);
                redraw_from_window(dlg->win);
                break;
            }
            break;
        case EV_ABORT:
            /* Moved this line up so that the dlg would have access to its 
                member vars before they get freed. */ 
            if (dlg->dlg->abort) dlg->dlg->abort(dlg);
            for (i = 0; i < dlg->n; i++) {
                struct dialog_item_data *di = &dlg->items[i];
                if (di->cdata) g_free(di->cdata);
                free_list(di->history);
            }
            freeml(dlg->ml);
    }
    bla:;
}

int check_number(struct dialog_data *dlg, struct dialog_item_data *di)
{
    char *end;
    long l = strtol(di->cdata, (char **)(void *)&end, 10);
    if (!*di->cdata || *end) {
        msg_box(NULL, VTEXT(T_BAD_NUMBER), AL_CENTER, VTEXT(T_NUMBER_EXPECTED), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
        return 1;
    }
    if (l < di->item->gid || l > di->item->gnum) {
        msg_box(NULL, VTEXT(T_BAD_NUMBER), AL_CENTER, VTEXT(T_NUMBER_OUT_OF_RANGE), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
        return 1;
    }
    return 0;
}

int check_hex(struct dialog_data *dlg, struct dialog_item_data *di)
{
    char *end;
    long l = strtol(di->cdata, (char **)(void *)&end, 16);
    if (!*di->cdata || *end) {
        msg_box(NULL, VTEXT(T_BAD_NUMBER), AL_CENTER, VTEXT(T_NUMBER_EXPECTED), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
        return 1;
    }
    if (l < di->item->gid || l > di->item->gnum) {
        msg_box(NULL, VTEXT(T_BAD_NUMBER), AL_CENTER, VTEXT(T_NUMBER_OUT_OF_RANGE), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
        return 1;
    }
    return 0;
}

int check_double(struct dialog_data *dlg, struct dialog_item_data *di)
{
    double d = atof(di->cdata);
    if (!*di->cdata) {
        msg_box(NULL, VTEXT(T_BAD_NUMBER), AL_CENTER, VTEXT(T_NUMBER_EXPECTED), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
        return 1;
    }
    if (d < (double)di->item->gid || d > (double)di->item->gnum) {
        msg_box(NULL, VTEXT(T_BAD_NUMBER), AL_CENTER, VTEXT(T_NUMBER_OUT_OF_RANGE), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
        return 1;
    }
    return 0;
}

int check_qrg(struct dialog_data *dlg, struct dialog_item_data *di)
{
    char *end;
    double d = strtod(di->cdata, (char **)(void *)&end);
    if (!*di->cdata || *end) {
        msg_box(NULL, VTEXT(T_BAD_NUMBER), AL_CENTER, VTEXT(T_NUMBER_EXPECTED), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
        return 1;
    }
    if (d < 0.0 || d > 300000000000.0) {
        msg_box(NULL, VTEXT(T_BAD_NUMBER), AL_CENTER, VTEXT(T_NUMBER_OUT_OF_RANGE), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
        return 1;
    }
    return 0;
}

/*int check_spowe(struct dialog_data *dlg, struct dialog_item_data *di)
{
	if (strlen(di->cdata) == 0) return 0;

	char *c;
	for (c = di->cdata; *c != '\0'; c++){
		if (*c >= '0' && *c <= '9') continue;
		if (*c == '.') continue;
		if (*c == ' ') continue;
		msg_box(NULL, VTEXT(T_BAD_NUMBER), AL_CENTER, TRANSLATE("Must be TX power in Watts without unit."), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
		return 1;
	}

	double d = atof(di->cdata);
	if (!*di->cdata) {
		msg_box(NULL, VTEXT(T_BAD_NUMBER), AL_CENTER, VTEXT(T_NUMBER_EXPECTED), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
		return 1;
	}

	return 0;
}	*/


int check_nonempty(struct dialog_data *dlg, struct dialog_item_data *di)
{
    char *p;
    for (p = di->cdata; *p; p++) if (*p > ' ') return 0;
    msg_box(NULL, VTEXT(T_BAD_STRING), AL_CENTER, VTEXT(T_EMPTY_STRING_NOT_ALLOWED), NULL, 1, VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
    return 1;
}

int cancel_dialog(struct dialog_data *dlg, struct dialog_item_data *di)
{
    /*struct dialog *dl = dlg->dlg;*/
    delete_window(dlg->win);
    /*g_free(dl);*/
    return 0;
}

static int check_dialog(struct dialog_data *dlg)
{
    int i;
    for (i = 0; i < dlg->n; i++)
        if (dlg->dlg->items[i].type == D_CHECKBOX || dlg->dlg->items[i].type == D_CHECKBOX3 || 
            dlg->dlg->items[i].type == D_FIELD || dlg->dlg->items[i].type == D_FIELD_PASS)
            if (dlg->dlg->items[i].fn && dlg->dlg->items[i].fn(dlg, &dlg->items[i])) {
                dlg->selected = i;
                redraw_dialog(dlg);
                return 1;
            }
    return 0;
}

int ok_dialog(struct dialog_data *dlg, struct dialog_item_data *di)
{
    int i;
    void (*fn)(void *) = dlg->dlg->refresh;
    void *data = dlg->dlg->refresh_data;
    if (check_dialog(dlg)) return 1;
    for (i = 0; i < dlg->n; i++)
        memcpy(dlg->dlg->items[i].data, dlg->items[i].cdata, dlg->dlg->items[i].dlen);
    if (fn) fn(data);
    i = cancel_dialog(dlg, di);
    return i;
}

void center_dlg(struct dialog_data *dlg)
{
    dlg->x = (dlg->win->term->x - dlg->xw) / 2;
    dlg->y = (dlg->win->term->y - dlg->yw) / 2;
}

void draw_dlg(struct dialog_data *dlg)
{
    fill_area(dlg->x, dlg->y, dlg->xw, dlg->yw, COLOR_DIALOG);
}

void max_text_width(struct terminal *term, char *text, int *width)
{
    text = _(text);
    do {
        int c = 0;
        while (*text && *text != '\n') text++, c++;
        if (c > *width) *width = c;
    } while (*(text++));
}

void min_text_width(struct terminal *term, char *text, int *width)
{
    text = _(text);
    do {
        int c = 0;
        while (*text && *text != '\n' && *text != ' ') text++, c++;
        if (c > *width) *width = c;
    } while (*(text++));
}

void dlg_format_text(struct terminal *term1, struct terminal *t2, char *atext, int x, int *y, int w, int *rw, int co, int align)
{
    unsigned char *text;
    text = (unsigned char *)_(atext);
    do {
        unsigned char *tx;
        unsigned char *tt = text;
        int s;
        int xx = x;
        do {
            while (*text && *text != '\n' && *text != ' ') {
                text++, xx++;
            }
            tx = ++text;
            xx++;
            if (*(text - 1) != ' ') break;
            while (*tx && *tx != '\n' && *tx != ' ') tx++;
        } while (tx - text + xx - x <= w);
        s = (align & AL_MASK) == AL_CENTER ? (w - (xx - 1 - x)) / 2 : 0;
        if (s < 0) s = 0;
        while (tt < text - 1) {
            if (s >= w) {
                s = 0, (*y)++;
                if (rw) *rw = w;
                rw = NULL;
            }
            if (term1) set_char(x + s, *y, co | *tt);
            s++, tt++;
        }
        if (rw && xx - 1 - x > *rw) *rw = xx - 1 - x;
        (*y)++;
    } while (*(text - 1));
}

void max_buttons_width(struct terminal *term, struct dialog_item_data *butt, int n, int *width)
{
    int w = -2;
    int i;
    for (i = 0; i < n; i++) w += strlen(_((butt++)->item->text)) + 6;
    if (w > *width) *width = w;
}

void min_buttons_width(struct terminal *term, struct dialog_item_data *butt, int n, int *width)
{
    int i;
    for (i = 0; i < n; i++) {
        int w = strlen(_((butt++)->item->text)) + 4;
        if (w > *width) *width = w;
    }
}

void dlg_format_buttons(struct terminal *term, struct terminal *t2, struct dialog_item_data *butt, int n, int x, int *y, int w, int *rw, int align)
{
    int i1 = 0;
    while (i1 < n) {
        int i2 = i1 + 1;
        int mw;
        while (i2 < n) {
            mw = 0;
            max_buttons_width(t2, butt + i1, i2 - i1 + 1, &mw);
            if (mw <= w) i2++;
            else break;
        }
        mw = 0;
        max_buttons_width(t2, butt + i1, i2 - i1, &mw);
        if (rw && mw > *rw) if ((*rw = mw) > w) *rw = w;
        if (term) {
            int i;
            int p = x + ((align & AL_MASK) == AL_CENTER ? (w - mw) / 2 : 0);
            for (i = i1; i < i2; i++) {
                butt[i].x = p;
                butt[i].y = *y;
                p += (butt[i].l = strlen(_(butt[i].item->text)) + 4) + 2;
            }
        }
        *y += 2;
        i1 = i2;
    }
}
void dlg_format_buttons1(struct terminal *term, struct terminal *t2, struct dialog_item_data *butt, int n, int x, int *y, int w, int *rw, int align)
{
    int i1 = 0;
    while (i1 < n) {
        int i2 = i1 + 1;
        int mw;
        while (i2 < n) {
            mw = 0;
            max_buttons_width(t2, butt + i1, i2 - i1 + 1, &mw);
            if (mw <= w) i2++;
            else break;
        }
        mw = 0;
        max_buttons_width(t2, butt + i1, i2 - i1, &mw);
        if (rw && mw > *rw) if ((*rw = mw) > w) *rw = w;
        if (term) {
            int i;
            int p = x + ((align & AL_MASK) == AL_CENTER ? (w - mw) / 2 : 0);
            for (i = i1; i < i2; i++) {
                butt[i].x = p;
                butt[i].y = *y;
                p += (butt[i].l = strlen(_(butt[i].item->text)) + 4) + 2;
            }
        }
        *y += 1;
        i1 = i2;
    }
}

void dlg_format_checkbox(struct terminal *term, struct terminal *t2, struct dialog_item_data *chkb, int x, int *y, int w, int *rw, char *text)
{
    if (term) {
        chkb->x = x;
        chkb->y = *y;
    }
    if (rw) *rw -= 4;
    dlg_format_text(term, t2, text, x + 4, y, w - 4, rw, COLOR_DIALOG_CHECKBOX_TEXT, AL_LEFT);
    if (rw) *rw += 4;
}

void dlg_format_checkboxes(struct terminal *term, struct terminal *t2, struct dialog_item_data *chkb, int n, int x, int *y, int w, int *rw, char **texts)
{
    while (n) {
        dlg_format_checkbox(term, t2, chkb, x, y, w, rw, _(texts[0]));
        texts++; chkb++; n--;
    }
}

void checkboxes_width(struct terminal *term, char **texts, int *w, void (*fn)(struct terminal *, char *, int *))
{
    while (texts[0]) {
        *w -= 4;
        fn(term, _(texts[0]), w);
        *w += 4;
        texts++;
    }
}

void dlg_format_field(struct terminal *term, struct terminal *t2, struct dialog_item_data *item, int x, int *y, int w, int *rw, int align)
{
    item->x = x;
    item->y = *y;
    item->l = w;
    /*if ((item->l = w) > item->item->dlen - 1) item->l = item->item->dlen - 1;*/
    if (rw && item->l > *rw) if ((*rw = item->l) > w) *rw = w;
    (*y)++;
}

/* Layout for generic boxes */
void dlg_format_box(struct terminal *term, struct terminal *t2, struct dialog_item_data *item, int x, int *y, int w, int *rw, int align) {
    item->x = x;
    item->y = *y;
    item->l = w;
    if (rw && item->l > *rw) if ((*rw = item->l) > w) *rw = w;
    (*y) += item->item->gid;
}

void max_group_width(struct terminal *term, char **texts, struct dialog_item_data *item, int n, int *w)
{
    int ww = 0;
    while (n--) {
        int wx = (item->item->type == D_CHECKBOX || item->item->type == D_CHECKBOX3) ? 4 : 
            item->item->type == D_BUTTON ?  strlen(_(item->item->text)) + 5 : item->item->dlen + 1;
        wx += strlen(_(texts[0]));
        if (n) wx++;
        ww += wx;
        texts++;
        item++;
    }
    if (ww > *w) *w = ww;
}

void min_group_width(struct terminal *term, char **texts, struct dialog_item_data *item, int n, int *w)
{
    while (n--) {
        int wx = (item->item->type == D_CHECKBOX || item->item->type == D_CHECKBOX3) ? 4 : 
            item->item->type == D_BUTTON ? strlen(_(item->item->text)) + 5 : item->item->dlen + 1;
        wx += strlen(_(texts[0]));
        if (wx > *w) *w = wx;
        texts++;
        item++;
    }
}

void dlg_format_group(struct terminal *term1, struct terminal *t2, 
                      char **texts, struct dialog_item_data *item, 
                      int n, int x, int *y, int w, int *rw)
{
    int nx = 0; /* now X coord */
/*    dbg("\ndlg_format_group (term=%p,n=%d,x=%d,*y=%d,w=%d,rw=%p)\n",term,n,x,*y,w,rw);*/
    //dbg("dlg_format_group item x=%d y=%d l=%d, vpos=%d, cpos=%d\n", item->x, item->y, item->l, item->vpos, item->cpos);
        
    while (n--) {
        int sl;
        int wx = (item->item->type == D_CHECKBOX || item->item->type == D_CHECKBOX3) ? 4 : 
            item->item->type == D_BUTTON ? 
            strlen(_(item->item->text)) + 5 : 
              item->item->maxl ? item->item->maxl+1 : item->item->dlen + 1;        /* widget width */
        if (_(texts[0])[0]) sl = strlen(_(texts[0]));
        else sl = -1;
        wx += sl;
        if (nx && nx + wx > w) {
            nx = 0;
            (*y) += 2;
        }
/*        dbg("sl=%d nx=%d wx=%d w=%d\n",sl,nx,wx,w);*/
        if (term1) {
            int stringlen;
            print_text(x + nx + 4 * (item->item->type == D_CHECKBOX || item->item->type == D_CHECKBOX3), *y, strlen(_(texts[0])), _(texts[0]), COLOR_DIALOG_TEXT);
            item->x = x + nx + (sl + 1) * (item->item->type != D_CHECKBOX && item->item->type != D_CHECKBOX3);
            item->y = *y;
/*            dbg("item->x=%d  item->y=%d\n",item->x,item->y);*/
            if (item->item->type == D_FIELD || item->item->type == D_FIELD_PASS) {
                  item->l = item->item->maxl ? item->item->maxl : item->item->dlen;
/*                dbg("setting item->l to %d  \n",item->l);
                dbg("remains w-nx=%d w-nx-wx=%d \n",w-nx,w-nx-wx);*/
                stringlen=strlen(_(texts[0]));    
                if (item->l > w-nx-stringlen) item->l = w-nx-stringlen;
                
/*                dbg("setting item->l to %d  \n",item->l);*/
            }
        }
        if (rw && nx + wx > *rw) if ((*rw = nx + wx) > w) *rw = w;
        nx += wx + 1;
        texts++;
        item++;
    }
    (*y)++;
}

void dlg_format_group1(struct terminal *term1, struct terminal *t2, 
                      char **texts, struct dialog_item_data *item, 
                      int n, int x, int *y, int w, int *rw)
{
    int nx = 0; /* now X coord */
/*    dbg("\ndlg_format_group (term=%p,n=%d,x=%d,*y=%d,w=%d,rw=%p)\n",term,n,x,*y,w,rw);*/
        
    while (n--) {
        int sl;
        int wx = (item->item->type == D_CHECKBOX || item->item->type == D_CHECKBOX3) ? 4 : 
            item->item->type == D_BUTTON ? 
            strlen(_(item->item->text)) + 5 : 
              item->item->maxl ? item->item->maxl+1 : item->item->dlen + 1;        /* widget width */
        if (_(texts[0])[0]) sl = strlen(_(texts[0]));
        else sl = -1;
        wx += sl;
        if (nx && nx + wx > w) {
            nx = 0;
            (*y) += 1;
        }
/*        dbg("sl=%d nx=%d wx=%d w=%d\n",sl,nx,wx,w);*/
        if (term1) {
            int stringlen;
            print_text(x + nx + 4 * (item->item->type == D_CHECKBOX || item->item->type == D_CHECKBOX3), *y, strlen(_(texts[0])), _(texts[0]), COLOR_DIALOG_TEXT);
            item->x = x + nx + (sl + 1) * (item->item->type != D_CHECKBOX && item->item->type != D_CHECKBOX3);
            item->y = *y;
/*            dbg("item->x=%d  item->y=%d\n",item->x,item->y);*/
            if (item->item->type == D_FIELD || item->item->type == D_FIELD_PASS) {
                  item->l = item->item->maxl ? item->item->maxl : item->item->dlen;
/*                dbg("setting item->l to %d  \n",item->l);
                dbg("remains w-nx=%d w-nx-wx=%d \n",w-nx,w-nx-wx);*/
                stringlen=strlen(_(texts[0]));    
                if (item->l > w-nx-stringlen) item->l = w-nx-stringlen;
                
/*                dbg("setting item->l to %d  \n",item->l);           */
            }
        }
        if (rw && nx + wx > *rw) if ((*rw = nx + wx) > w) *rw = w;
        nx += wx + 1;
        texts++;
        item++;
    }
    (*y)++;
}

void checkbox_list_fn(struct dialog_data *dlg)
{
    int max = 0, min = 0;
    int w, rw;
    int y = 0;
    checkboxes_width(term, dlg->dlg->udata, &max, max_text_width);
    checkboxes_width(term, dlg->dlg->udata, &min, min_text_width);
    max_buttons_width(term, dlg->items + dlg->n - 2, 2, &max);
    min_buttons_width(term, dlg->items + dlg->n - 2, 2, &min);
    w = term->x * 9 / 10 - 2 * DIALOG_LB;
    if (w > max) w = max;
    if (w < min) w = min;
    if (w > term->x - 2 * DIALOG_LB) w = term->x - 2 * DIALOG_LB;
    if (w < 5) w = 5;
    rw = 0;
    dlg_format_checkboxes(NULL, term, dlg->items, dlg->n - 2, 0, &y, w, &rw, dlg->dlg->udata);
    y++;
    dlg_format_buttons(NULL, term, dlg->items + dlg->n - 2, 2, 0, &y, w, &rw, AL_CENTER);
    w = rw;
    dlg->xw = rw + 2 * DIALOG_LB;
    dlg->yw = y + 2 * DIALOG_TB;
    center_dlg(dlg);
    draw_dlg(dlg);
    y = dlg->y + DIALOG_TB + 1;
    dlg_format_checkboxes(term, term, dlg->items, dlg->n - 2, dlg->x + DIALOG_LB, &y, w, NULL, dlg->dlg->udata);
    y++;
    dlg_format_buttons(term, term, dlg->items + dlg->n - 2, 2, dlg->x + DIALOG_LB, &y, w, &rw, AL_CENTER);
}

void group_fn(struct dialog_data *dlg)
{
    int max = 0, min = 0;
    int w, rw;
    int y = 0;
    max_group_width(term, dlg->dlg->udata, dlg->items, dlg->n - 2, &max);
    min_group_width(term, dlg->dlg->udata, dlg->items, dlg->n - 2, &min);
    max_buttons_width(term, dlg->items + dlg->n - 2, 2, &max);
    min_buttons_width(term, dlg->items + dlg->n - 2, 2, &min);
    w = term->x * 9 / 10 - 2 * DIALOG_LB;
    if (w > max) w = max;
    if (w < min) w = min;
    if (w > term->x - 2 * DIALOG_LB) w = term->x - 2 * DIALOG_LB;
    if (w < 1) w = 1;
    rw = 0;
    dlg_format_group(NULL, term, dlg->dlg->udata, dlg->items, dlg->n - 2, 0, &y, w, &rw);
    y++;
    dlg_format_buttons(NULL, term, dlg->items + dlg->n - 2, 2, 0, &y, w, &rw, AL_CENTER);
    w = rw;
    dlg->xw = rw + 2 * DIALOG_LB;
    dlg->yw = y + 2 * DIALOG_TB;
    center_dlg(dlg);
    draw_dlg(dlg);
    y = dlg->y + DIALOG_TB + 1;
    dlg_format_group(term, term, dlg->dlg->udata, dlg->items, dlg->n - 2, dlg->x + DIALOG_LB, &y, w, NULL);
    y++;
    dlg_format_buttons(term, term, dlg->items + dlg->n - 2, 2, dlg->x + DIALOG_LB, &y, w, &rw, AL_CENTER);
}

static void msg_box_fn(struct dialog_data *dlg)
{
    int max = 0, min = 0;
    int w, rw;
    int y = 0;
    char **ptr;
    //t textl2 = 0;
    GString *text = g_string_new("");

    for (ptr = dlg->dlg->udata; *ptr; ptr++) g_string_append(text, _(*ptr));
    max_text_width(term, text->str, &max);
    min_text_width(term, text->str, &min);
    max_buttons_width(term, dlg->items, dlg->n, &max);
    min_buttons_width(term, dlg->items, dlg->n, &min);
    w = term->x * 9 / 10 - 2 * DIALOG_LB;
    if (w > max) w = max;
    if (w < min) w = min;
    if (w > term->x - 2 * DIALOG_LB) w = term->x - 2 * DIALOG_LB;
    if (w < 1) w = 1;
    rw = 0;
    dlg_format_text(NULL, term, text->str, 0, &y, w, &rw, COLOR_DIALOG_TEXT, dlg->dlg->align);
    y++;
    dlg_format_buttons(NULL, term, dlg->items, dlg->n, 0, &y, w, &rw, AL_CENTER);
    w = rw;
    dlg->xw = rw + 2 * DIALOG_LB;
    dlg->yw = y + 2 * DIALOG_TB;
    center_dlg(dlg);
    draw_dlg(dlg);
    y = dlg->y + DIALOG_TB + 1;
    dlg_format_text(term, term, text->str, dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, dlg->dlg->align);
    y++;
    dlg_format_buttons(term, term, dlg->items, dlg->n, dlg->x + DIALOG_LB, &y, w, NULL, AL_CENTER);
    g_string_free(text, TRUE);
}

static int msg_box_button(struct dialog_data *dlg, struct dialog_item_data *di)
{
    void (*fn)(void *, struct dialog_item_data *) = (void (*)(void *, struct dialog_item_data *di))di->item->udata;
    void *data = dlg->dlg->udata2;
    /*struct dialog *dl = dlg->dlg;*/
    if (fn) fn(data, di);
    cancel_dialog(dlg, di);
    return 0;
}

void msg_box(struct memory_list *ml, char *title, 
        int align, /*char *text, void *data, int n,*/ ...)
{
    struct dialog *dlg;
    int i;
    int n;
    char *text;
    char **udata;
    void *udata2;
    int udatan;
    va_list ap;
    va_start(ap, align);
    udata = NULL;
    udatan = 0;
    do {
        char **udata_;
        text = va_arg(ap, char *);
        na_kovarne__to_je_narez:
        if (!(udata_ = g_realloc(udata, ++udatan * sizeof(char *)))) {
            g_free(udata);
            va_end(ap);
            return;
        }
        udata = udata_;
        udata[udatan - 1] = text;
        if (text && !(align & AL_EXTD_TEXT)) {
            text = NULL;
            goto na_kovarne__to_je_narez;
        }
    } while (text);
    udata2 = va_arg(ap, void *);
    n = va_arg(ap, int);
    if (!(dlg = g_malloc(sizeof(struct dialog) + (n + 1) * sizeof(struct dialog_item)))) {
        g_free(udata);
        va_end(ap);
        return;
    }
    memset(dlg, 0, sizeof(struct dialog) + (n + 1) * sizeof(struct dialog_item));
    dlg->title = title;
    dlg->fn = msg_box_fn;
    dlg->udata = udata;
    dlg->udata2 = udata2;
    dlg->align = align;
    for (i = 0; i < n; i++) {
        char *m;
        void (*fn)(void *);
        int flags;
        m = va_arg(ap, char *);
        fn = va_arg(ap, void *);
        flags = va_arg(ap, int);
        if (!m) {
            i--, n--;
            continue;
        }
        dlg->items[i].type = D_BUTTON;
        dlg->items[i].gid = flags;
        dlg->items[i].fn = msg_box_button;
        dlg->items[i].dlen = 0;
        dlg->items[i].text = m;
        dlg->items[i].udata = fn;
    }
    va_end(ap);
    dlg->items[i].type = D_END;
    add_to_ml(&ml, dlg, udata, NULL);
    do_dialog(dlg, ml);
}

void add_to_history(struct history *h, char *t)
{
/*    struct history_item *hi, *hs;
    int l;
    if (!h || !t || !*t) return;
    l = strlen(t) + 1;
    if (!(hi = g_malloc(sizeof(struct history_item) + l))) return;
    memcpy(hi->d, t, l);
    foreach(hs, h->items) if (!strcmp(hs->d, t)) {
        struct history_item *hd = hs;
        hs = hs->prev;
        del_from_list(hd);
        g_free(hd);
        h->n--;
    }
    add_to_list(*h, hi);
    h->n++;
    while (h->n > MAX_HISTORY_ITEMS) {
        struct history_item *hd = h->items.prev;
        if ((void *)hd == &h->items) {
            zinternal("history is empty");
            h->n = 0;
            return;
        }
        del_from_list(hd);
        g_free(hd);
        h->n--;
    }*/
}

static int input_field_cancel(struct dialog_data *dlg, struct dialog_item_data *di)
{
    void (*fn)(void *) = di->item->udata;
    void *data = dlg->dlg->udata2;
    if (fn) fn(data);
    cancel_dialog(dlg, di);
    return 0;
}

static int input_field_ok(struct dialog_data *dlg, struct dialog_item_data *di)
{
    void (*fn)(void *, char *) = di->item->udata;
    void *data = dlg->dlg->udata2;
    char *text = dlg->items->cdata;
    if (check_dialog(dlg)) return 1;
    add_to_history(dlg->dlg->items->history, text);
    if (fn) fn(data, text);
    ok_dialog(dlg, di);
    return 0;
}

static int input_field_complete(struct dialog_data *dlg, struct dialog_item_data *di){
    di->item->tabi++;
    do_tab_compl(&dlg->items[0], 1);
    if (di->item->tabi == 2) di->item->tabi = 0;
    redraw_later();
    return 0;
}

char new_file[MAX_STR_LEN];

void input_field_fn_i(struct dialog_data *dlg, int i)
{
    if (strlen(new_file)){
        char *c = strrchr(dlg->items[i].cdata, '/');
		if (!c) c = strrchr(dlg->items[i].cdata, '\\');
        if (c){
			char *p;
            *c = '\0';
            p = g_strconcat(dlg->items[i].cdata, "/", new_file, NULL);
			z_wokna(p);
            safe_strncpy0(dlg->items[i].cdata, p, dlg->items[i].item->dlen);
            g_free(p);
        }else{
            safe_strncpy0(dlg->items[i].cdata, new_file, dlg->items[i].item->dlen);
        }
        dlg->items[i].cpos = strlen(dlg->items[i].cdata);
        strcpy(new_file, "");
    }
}

void input_field_fn(struct dialog_data *dlg){
	input_field_fn_i(dlg, 0);
}



void input_field(struct memory_list *ml, char *title, char *text, char *okbutton, char *cancelbutton, void *data, struct history *history, int l, char *def, int min, int max, int (*check)(struct dialog_data *, struct dialog_item_data *), void (*fn)(void *, char *), void (*cancelfn)(void *), int tabcompl)
{                                                                                                                                   
    struct dialog *dlg;
    char *field;
    int i;

    strcpy(new_file, "");

    if (!(dlg = g_malloc(sizeof(struct dialog) + 4 * sizeof(struct dialog_item) + l)))
        return;
    memset(dlg, 0, sizeof(struct dialog) + 4 * sizeof(struct dialog_item) + l);
    *(field = (char *)dlg + sizeof(struct dialog) + 4 * sizeof(struct dialog_item)) = 0;
    if (def) {
        if (strlen(def) + 1 > l) 
            memcpy(field, def, l - 1);
        else 
            strcpy(field, def);
    }
    dlg->title = title;
    dlg->fn = dlg_pf_fn;
    if (tabcompl) dlg->fn2 = input_field_fn; 
    dlg->udata = text;
    dlg->udata2 = data;
    dlg->y0 = 1;

    dlg->items[i=0].type = D_FIELD;
    dlg->items[i].gid = min;
    dlg->items[i].gnum = max;
    dlg->items[i].fn = check;
    dlg->items[i].history = history;
    dlg->items[i].dlen = l;
    dlg->items[i].maxl = (term->x * 2)/4;
    dlg->items[i].data = field;
    dlg->items[i].tabcompl = tabcompl;
    dlg->items[i].wrap = 0;

    if (tabcompl){
        dlg->items[++i].type = D_BUTTON;
        dlg->items[i].gid = 0;
        dlg->items[i].fn = input_field_complete;
        dlg->items[i].dlen = 0;
        dlg->items[i].text = "F2";
        dlg->items[i].udata = NULL;
    }
    dlg->items[i].wrap = 2;

    dlg->items[++i].type = D_BUTTON;
    dlg->items[i].gid = B_ENTER;
    dlg->items[i].fn = input_field_ok;
    dlg->items[i].dlen = 0;
    dlg->items[i].text = okbutton;
    dlg->items[i].udata = fn;

    dlg->items[++i].type = D_BUTTON;
    dlg->items[i].gid = B_ESC;
    dlg->items[i].fn = input_field_cancel;
    dlg->items[i].dlen = 0;
    dlg->items[i].text = cancelbutton;
    dlg->items[i].udata = cancelfn;
    dlg->items[i].align = AL_BUTTONS;
    dlg->items[i].wrap = 1;

    dlg->items[++i].type = D_END;
    add_to_ml(&ml, dlg, NULL);
    do_dialog(dlg, ml);
}


/* Sets the selected item to one that is visible.*/
void box_sel_set_visible(struct dialog_item_data *box_item_data, int offset) {
    struct dlg_data_item_data_box *box;
    int sel;
    
    box = (struct dlg_data_item_data_box *)(box_item_data->item->data);
    if (offset > box_item_data->item->gid || offset < 0) {
        return;
    }
    /*debug("offset: %d", offset);*/
    sel = box->box_top + offset;
    if (sel > box->list_len) {
        box->sel = box->list_len - 1;
    } else {
        box->sel = sel;
    }
}

/* Moves the selected item [dist] thingies. If [dist] is out of the current 
    range, the selected item is moved to the extreme (ie, the top or bottom) 
*/
void box_sel_move(struct dialog_item_data *box_item_data, int dist) {
    struct dlg_data_item_data_box *box;
    int new_sel;
    int new_top;
    
    box = (struct dlg_data_item_data_box *)(box_item_data->item->data);

    new_sel = box->sel + dist; 
    new_top = box->box_top;

    /* Ensure that the selection is in range */
    if (new_sel < 0)
        new_sel = 0;
    else if (new_sel >= box->list_len)
        new_sel = box->list_len - 1;
    
    /* Ensure that the display box is over the item */
    if ( new_sel >= (new_top + box_item_data->item->gid) ) {
        /* Move it down */
        new_top = new_sel - box_item_data->item->gid + 1;
#ifdef DEBUG
        if (new_top < 0)
            dbg("Newly calculated box_top is an extremely wrong value (%d). It should not be below zero.", new_top);
#endif  
    } else if ( new_sel < new_top ) {
        /* Move the display up (if necessary) */    
        new_top = new_sel;
    }
    
    box->sel = new_sel;
    box->box_top = new_top;
}


/* Displays a dialog box */
void show_dlg_item_box(struct dialog_data *dlg, struct dialog_item_data *box_item_data) {
    struct dlg_data_item_data_box *box;
    struct box_item *citem; /* Item currently being shown */
    /* struct box_item *items; */  /* List of items to be shown */
    int n;  /* Index of item currently being displayed */

    box = (struct dlg_data_item_data_box *)(box_item_data->item->data);
    /* FIXME: Counting here SHOULD be unnecessary */
    n = 0;

    fill_area(box_item_data->x, box_item_data->y, box_item_data->l, box_item_data->item->gid, COLOR_DIALOG_FIELD);

    foreach (citem, box->items) {
        int len; /* Length of the current text field. */
        len = strlen(citem->text);
        if (len > box_item_data->l) {
            len = box_item_data->l;
        }

        /* Is the current item in the region to be displayed? */
        if ( (n >= box->box_top) && (n < (box->box_top + box_item_data->item->gid)) ) {
            print_text(box_item_data->x, box_item_data->y + n - box->box_top, len, citem->text, n == box->sel ? COLOR_DIALOG_BUTTON_SELECTED : COLOR_DIALOG_FIELD_TEXT);

        }
        n++;
    }

    box->list_len = n;
}
        
void errbox(char *text, int errcode, ...){
    static char str[1026];
    va_list l;
    
    va_start(l, errcode);
    if (errcode){
        GString *gs = g_string_new("");
        g_string_append_vprintf(gs, text, l);
        g_string_append_printf(gs, "\n");
        z_strerror(gs, errcode);
        g_string_append_printf(gs, " (%d)", errcode);
        g_snprintf(str, 1024, "%s", gs->str);
        g_string_free(gs, TRUE);
    }else{
        g_vsnprintf(str, 1024, text, l);
    }
    va_end(l);
    
    msg_box(NULL, 
            VTEXT(T_ERROR), AL_CENTER, 
            str, NULL, 1, 
            VTEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
    
}
    
void dlg_pf_init(int phase, struct dialog_data *dlgd){
    switch(phase){
        case 0:
            dlgd->min = 0;
            dlgd->max = 0;
            dlgd->yy = -1;
            dlgd->ofs = 0;
            break;
        case 1:
            dlgd->w = dlgd->win->term->x * 9 / 10 - 2 * DIALOG_LB;
            if (dlgd->w > dlgd->max) dlgd->w = dlgd->max;
            if (dlgd->w < dlgd->min) dlgd->w = dlgd->min;
            if (dlgd->w > dlgd->win->term->x - 2 * DIALOG_LB) dlgd->w = dlgd->win->term->x - 2 * DIALOG_LB;
            if (dlgd->w < 1) dlgd->w = 1;
            dlgd->rw = 0;
            dlgd->yy = -1;
            dlgd->ofs = 0;
            break;
        case 2:
            dlgd->w = dlgd->rw;
            dlgd->xw = dlgd->w + 2 * DIALOG_LB;
            dlgd->yw = dlgd->yy + 2 * DIALOG_TB;
            center_dlg(dlgd);
			if (dlgd->dlg->position_fn) dlgd->dlg->position_fn(dlgd);
            draw_dlg(dlgd);
            dlgd->yy = dlgd->y + DIALOG_TB + dlgd->dlg->y0;
            dlgd->ofs = 0;
            break;
    }
    
}
    
void dlg_pf_group(int phase, struct dialog_data *dlgd, int cnt){
    int i;
    int nx = 0; /* now X coord */
    
    switch(phase){
        case 0:
            for (i=0; i<cnt; i++){
                int wx = (dlgd->items[dlgd->ofs+i].item->type == D_CHECKBOX || dlgd->items[dlgd->ofs+i].item->type == D_CHECKBOX3) ? 4 : 
                    dlgd->items[dlgd->ofs+i].item->type == D_BUTTON ? strlen(_(dlgd->items[dlgd->ofs+i].item->text)) + 5 : dlgd->items[dlgd->ofs+i].item->dlen + 1;
                if (dlgd->items[dlgd->ofs+i].item->msg){ 
                    wx += strlen(_(dlgd->items[dlgd->ofs+i].item->msg));
                }
                if (wx > dlgd->max) dlgd->max = wx;
                if (wx < dlgd->min) dlgd->min = wx;
            }
            break;
        case 1:
            for (i=0; i<cnt; i++){
                int sl;
                struct dialog_item_data *item = &(dlgd->items[dlgd->ofs+i]);
                int wx = (item->item->type == D_CHECKBOX || item->item->type == D_CHECKBOX3) ? 4 : 
                    item->item->type == D_BUTTON ? 
                    strlen(_(item->item->text)) + 5 : 
                      item->item->maxl ? item->item->maxl+1 : item->item->dlen + 1;        /* widget width */
                if (_(item->item->msg)) sl = strlen(_(item->item->msg));
                else sl = -1;
                wx += sl;
                if (nx && nx + wx > dlgd->w) {
                    nx = 0;
                    dlgd->yy += 1;
                }
                if (nx + wx > dlgd->rw) if ((dlgd->rw = nx + wx) > dlgd->w) dlgd->rw = dlgd->w;
                nx += wx + 1;
            }
            //dlgd->yy++;
            break;
        case 2:
            for (i=0; i<cnt; i++){
                int sl;
                int stringlen;
                struct dialog_item_data *item = &(dlgd->items[dlgd->ofs+i]);
                int wx = (item->item->type == D_CHECKBOX || item->item->type == D_CHECKBOX3) ? 4 : 
                    item->item->type == D_BUTTON ? 
                    strlen(_(item->item->text)) + 5 : 
                      item->item->maxl ? item->item->maxl+1 : item->item->dlen + 1;        /* widget width */
                if (_(item->item->msg)) sl = strlen(_(item->item->msg));
                else sl = -1;
                wx += sl;
                if (nx && nx + wx > dlgd->w) {
                    nx = 0;
                    dlgd->yy += 1;
                }
                if (item->item->msg){
                    print_text(dlgd->x + DIALOG_LB + nx + 4 * (item->item->type == D_CHECKBOX || item->item->type == D_CHECKBOX3), dlgd->yy, strlen(_(item->item->msg)), _(item->item->msg), COLOR_DIALOG_TEXT);
                }
                item->x = dlgd->x + DIALOG_LB + nx + (sl + 1) * (item->item->type != D_CHECKBOX && item->item->type != D_CHECKBOX3);
                item->y = dlgd->yy;
                if (item->item->type == D_FIELD || item->item->type == D_FIELD_PASS) {
                    item->l = item->item->maxl ? item->item->maxl : item->item->dlen;
                    stringlen=0;
                    if (item->item->msg) stringlen = strlen(_(item->item->msg));    
                    if (item->l > dlgd->w - nx - stringlen) item->l = dlgd->w - nx - stringlen;
                }
                nx += wx + 1;
            }
            //dlgd->yy++;
            break;
    }
    dlgd->ofs += cnt;
}

void dlg_pf_buttons(int phase, struct dialog_data *dlgd, int cnt){
    switch(phase){
        case 0:
            max_buttons_width(dlgd->win->term, dlgd->items + dlgd->ofs, cnt, &dlgd->max);
            min_buttons_width(dlgd->win->term, dlgd->items + dlgd->ofs, cnt, &dlgd->min);
            break;
        case 1:
            dlg_format_buttons(NULL, dlgd->win->term, dlgd->items + dlgd->ofs, cnt, 
                    0, &dlgd->yy, dlgd->w, &dlgd->rw, 
                    dlgd->items[dlgd->ofs+cnt-1].item->align);
            break;
        case 2:
            dlg_format_buttons(dlgd->win->term, dlgd->win->term, dlgd->items + dlgd->ofs, cnt, 
                    dlgd->x + DIALOG_LB, &dlgd->yy, dlgd->w, NULL, 
                    dlgd->items[dlgd->ofs+cnt-1].item->align);
            break;
    }
    dlgd->ofs += cnt;
}

void dlg_pf_fn(struct dialog_data *dlgd)
{
    int ph;
    int i,j;
    
    if (dlgd->dlg->fn2) dlgd->dlg->fn2(dlgd);
    
    j=0;
    for (ph = 0; ph < 3; ph++){
        dlg_pf_init(ph, dlgd);
        for (i=0; i< dlgd->n; i++){
//            char *names[]={"end", "check", "field", "pass", "butt", "box", "check3", "?", "?"};
//            dbg("i=%d type=%d %s n=%d\n", i, dlgd->items[i].item->type, names[dlgd->items[i].item->type], dlgd->n);
            j++;
            if (!dlgd->items[i].item->wrap) continue;
            if (dlgd->items[i].item->align & AL_BUTTONS)
                dlg_pf_buttons(ph, dlgd, j);
            else
                dlg_pf_group(ph, dlgd, j);
            dlgd->yy += dlgd->items[i].item->wrap;
            j=0;
        }
        if (j>0){
            if (dlgd->items[i].item->align & AL_BUTTONS)
                dlg_pf_buttons(ph, dlgd, j);
            else
                dlg_pf_group(ph, dlgd, j);
            dlgd->yy += dlgd->items[i].item->wrap;
        }
    }

}

static char *complete_file;
static int filter_complete(const char *dir, const struct dirent *de){
    int ret;

    if (strcmp((char *)de->d_name, ".") == 0) return 0;
    if (strcmp((char *)de->d_name, "..") == 0) return 0;
    ret = strncmp((char *)de->d_name, complete_file, strlen(complete_file));
    return !ret;
}

void complete_choose(void *dir, void *unused){
    /*dbg("menu_load_contest('%s')\n", dir);*/
    safe_strncpy0(new_file, (char *)dir, MAX_STR_LEN);
    redraw_later();
} 


int do_tab_compl(struct dialog_item_data *di, int tabi){
    char *path, *file, *slash;
    int i, n, max = 0;
    struct dirent **namelist=NULL;
    struct menu_item *mi = NULL;

	dbg("complete: %d '%s'\n", tabi, di->cdata);

    slash = strrchr(di->cdata, '/');
	if (!slash) slash = strrchr(di->cdata, '\\');
    if (slash){
        if (di->cdata[0] == '~'){
            char *p = g_strndup(di->cdata + 1 , slash - di->cdata - 1);
            path = g_strconcat(home_dir, "/", p, NULL);
            g_free(p);
        }else{
            path = g_strndup(di->cdata, slash - di->cdata);
        }
        file = g_strdup(slash + 1);
    }else{
        path = g_strdup(".");
        file = g_strdup(di->cdata);
    }
    dbg("path='%s' file='%s'\n", path, file);

    free_namelist();

    complete_file = file;
    n = z_scandir(path, &namelist, filter_complete, z_scandir_alphasort);    
    complete_file = NULL;
    dbg("n=%d\n", n);
    if (n <= 0) goto x;

    for (i=0; i<n; i++){
        dbg("%s ", namelist[i]->d_name);
    }
    dbg("\n");

    if (n == 1){
        char *p = g_strconcat(path, "/", (char*)namelist[0]->d_name, NULL);
		z_wokna(p);
        safe_strncpy0(di->cdata, p, di->item->dlen);
        di->cpos = strlen(di->cdata);
        g_free(p);
    }else{
        if (!(mi = new_menu(3))) goto x;
        for (i=0; i<n; i++){
            char *c = (char*)namelist[i]->d_name;
            if (strlen(c)>max) max=strlen(c);
            add_to_menu(&mi, 
                    g_strdup(c), 
                    "", "", 
                    complete_choose, namelist[i]->d_name, 0);    
        }
//        set_window_ptr(gses->win, (term->x-6-max)/2,(term->y-2-i)/2);

        do_menu(mi, NULL);
    }

x:;
    g_free(path);
    g_free(file);
    //free_namelist();
    z_free_namelist(&namelist, &n);
    return 0;
}
