/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2022  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.

    UTF-8 detect: ěščřžýáíé
*/

#include <libzia.h>

#include "header.h"
#include "button.h"
#include "chart.h"
#include "cwdb.h"
#include "dwdb.h"
#include "dxc.h"
#include "fft.h"
#include "fifo.h"
#include "hf.h"
#include "inputln.h"
#include "kbdbind.h"
#include "kbd.h"
#include "kst.h"
#include "main.h"
#include "map.h"
#include "menu.h"
#include "net.h"
#include "player.h"
#include "qrvdb.h"
#include "qsodb.h"
#include "rc.h"
#include "scope.h"
#include "sdr.h"
#include "sdr.h"
#include "tsdl.h"
#include "sdr.h"
#include "session.h"
#include "sked.h"
#include "ssbd.h"
#include "stats.h"
#include "subwin.h"
#include "terminal.h"
#include "tregex.h"
#include "trig.h"
#include "update.h"


struct session *gses;

//#define QSOS_LEN (aband->qsos->len)

struct subwin *new_subwin(enum sw_type type, int where_){
    struct subwin *sw;
#ifdef Z_MSC_MINGW
	char oemcp[32];
#endif

//    dbg("new_subwin(%d, '%s')\n", type, title);
    sw = g_new0(struct subwin, 1);

	sw->type = type;
    sw->x = 1;
    sw->w = term->x - 2;
    sw->h = term->y - QSONR_HEIGHT - 4 - cfg->loglines;
	
	sw->ww = sw->w;
    sw->hh = sw->h;
    if (ctest && !ctest->oldcontest) sw->h -= ctest->spypeers->len;
	sw->cur = -1;
    sw->maxlen = 1000;
    sw->iconv_handle = (iconv_t)-1;

#ifdef Z_UNIX
    sw->ws.ws_col = sw->w;
    sw->ws.ws_row = sw->h-1;
    sw->ws.ws_xpixel = 0;
    sw->ws.ws_ypixel = 0;
#endif

    sw->read_fd =  -1;
    sw->write_fd = -1;
    sw->sock = -1;
	sw->olddragx = -1;
	sw->olddragy = -1;

	switch(sw->type){
		case SWT_SHELL: 
		case SWT_UPD:
			if (sw->type == SWT_SHELL){
				sw->title = g_strdup(VTEXT(T_SW_SHELL));
			}else{
				sw->title = g_strdup(VTEXT(T_UPDATE2));
			}
			sw->kbd_func        = sw_fifo_kbd_func;
			sw->mouse_func      = sw_shell_mouse_func;
			sw->redraw          = sw_shell_redraw;
			sw->check_bounds    = sw_shell_check_bounds;
			sw->lines           = g_ptr_array_new();

	        sw->il = g_new0(struct inputln,1);
			sw->il->enter = sw_shell_enter;
			sw->il->enterdata   = sw;
   			sw->il->allow_ctrlv = 1;
			
			if (sw->type == SWT_UPD){
				sw->zser = update_init_process();
			}else{
#ifdef Z_MSC_MINGW
				sw->zser = zserial_init_process("cmd.exe", NULL);
#else
				sw->zser = zserial_init_process("/bin/bash", NULL);
#endif
			}

			zserial_open(sw->zser);
			sw->read_fd = zserial_fd(sw->zser);
			zselect_set(zsel, sw->read_fd, sw_shell_read_handler, NULL,  NULL, sw);

#if defined(Z_UNIX_ANDROID) && !defined(Z_HAVE_PTY_H)
            char s[100];
            strcpy(s, "echo -n \"$HOSTNAME $PWD\\$ \"\n");
            zserial_write(sw->zser, s, strlen(s));
#endif

#ifdef Z_MSC_MINGW
			sprintf(oemcp, "CP%d", GetOEMCP());
			sw->iconv_handle = iconv_open("iso-8859-2//IGNORE", oemcp);
            //if (sw->iconv_handle == (iconv_t)-1) zinternal("Can't create iconv object %s to iso-8859-2", oemcp);

#endif

			if (sw->type == SWT_SHELL){
				sw_add_block(sw, VTEXT(T_NEW_WINDOWS_3));
			}
            break;

        case SWT_QSOS: 
            sw->title = g_strdup(VTEXT(T_SW_QSOS));
			sw->kbd_func        = sw_qsos_kbd_func;
			sw->mouse_func      = sw_qsos_mouse_func;
			sw->redraw          = sw_qsos_redraw;
			sw->check_bounds    = sw_qsos_check_bounds;
			sw->allqsos        	= ctest ? ctest->qsoglob : 0;
            break;

        case SWT_LOG: 
            sw->title = g_strdup(VTEXT(T_SW_LOG));
			sw->kbd_func        = sw_fifo_kbd_func;
			sw->mouse_func      = sw_fifo_mouse_func;
			sw->redraw          = sw_fifo_redraw;
			sw->check_bounds    = sw_fifo_check_bounds;
			sw->fifo            = glog;
            break;

        case SWT_TALK: 
            sw->title = g_strdup(VTEXT(T_SW_TALK));
			sw->kbd_func        = sw_fifo_kbd_func;
			sw->mouse_func      = sw_fifo_mouse_func;
			sw->redraw          = sw_fifo_redraw;
			sw->check_bounds    = sw_fifo_check_bounds;
			sw->fifo            = gtalk;

	        sw->il = g_new0(struct inputln,1);
			sw->il->enter = sw_talk_enter;
			sw->il->enterdata   = sw;
			sw->il->allow_ctrlv = 1;
            break;

        case SWT_SKED: 
            sw->title = g_strdup(VTEXT(T_SW_SKED));
			sw->kbd_func        = sw_sked_kbd_func;
			sw->mouse_func      = sw_sked_mouse_func;
			sw->redraw          = sw_sked_redraw;
			sw->check_bounds    = sw_sked_check_bounds;
			sw->fifo            = NULL; /* not used */
            break;

        case SWT_UNFI: 
            sw->title = g_strdup(VTEXT(T_SW_UNFI));
			sw->kbd_func        = sw_unfi_kbd_func;
			sw->mouse_func      = sw_unfi_mouse_func;
			sw->redraw          = sw_unfi_redraw;
			sw->check_bounds    = sw_unfi_check_bounds;
			sw->fifo            = NULL; /* filled in sw_unfi* */
            break;

        case SWT_DXC: 
            sw->title = g_strdup(VTEXT(T_SW_DXC));
			sw->kbd_func        = sw_dxc_kbd_func;
			sw->mouse_func      = sw_dxc_mouse_func;
			sw->redraw          = sw_dxc_redraw;
			sw->check_bounds    = sw_dxc_check_bounds;
			
            sw->lines = g_ptr_array_new();

			sw->il = g_new0(struct inputln, 1);
			sw->il->enter = sw_dxc_enter;
			sw->il->enterdata = sw;
   			sw->il->allow_ctrlv = 1;

			sw->adns = zasyncdns_init();
			{
				FILE *f;
				char s[1024];
				/*int *a = NULL;
				*a = 0;*/
				strcpy(s, "../src/_SPOTS");
				z_wokna(s);
				f = fopen(s, "rt");
				if (!f) f = fopen("_SPOTS", "rt");
				if (f){
	                progress("Loading %s", s);
					while ((fgets(s, 1000, f)) != NULL){
						sw_add_block(sw, s);
					}
					fclose(f);
				}
			
			}
			sw_add_line(sw, VTEXT(T_ENTER_TO_DXC2), 1);
            break;

        case SWT_STAT: 
            sw->title = g_strdup(VTEXT(T_SW_STAT));
			sw->kbd_func        = sw_stat_kbd_func;
			sw->mouse_func      = sw_stat_mouse_func;
			sw->redraw          = sw_stat_redraw;
			sw->check_bounds    = sw_stat_check_bounds;
			sw->raise           = sw_stat_raise;
			sw->fifo            = NULL; /* filled in sw_stat* */
            break;

        case SWT_SWAP: 
/*            sw = new_subwin(SWT_SWAP, VTEXT(T_SW_STAT), NULL);*/
            break;

        case SWT_MAP:
            sw->title = g_strdup(VTEXT(T_SW_MAP));
			sw->kbd_func        = sw_map_kbd_func;
			sw->mouse_func      = sw_map_mouse_func;
			sw->redraw          = sw_map_redraw;
			sw->check_bounds    = sw_map_check_bounds;
			sw->raise           = sw_map_raise;
                
			//sw->acs->sw = sw;
#ifdef Z_HAVE_SDL        
			if (sdl){
				sw->l1map = SDL_CreateRGBSurface(SDL_SWSURFACE, sw->w*FONT_W, sw->h*FONT_H, sdl->bpp, 
						sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
				sw->screen = SDL_CreateRGBSurface(SDL_SWSURFACE, sw->w*FONT_W, sw->h*FONT_H, sdl->bpp, 
						sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
				MUTEX_INIT(sw->screen);
				sw->zoom = 10000;
				    //sw->zoom = 1000; 
				sw->gdirty = 1;
				map_update_layout(sw);
				map_update_qth(sw);
			}
#endif
            break;

        case SWT_SCOPE:
            sw->title = g_strdup(VTEXT(T_SW_SCOPE));
			sw->kbd_func        = sw_scope_kbd_func;
			sw->mouse_func      = sw_scope_mouse_func;
			sw->redraw          = sw_scope_redraw;
			sw->check_bounds    = sw_scope_check_bounds;
			sw->raise           = sw_scope_raise;

#ifdef Z_HAVE_SDL        
#ifdef USE_FFT
			sw->scope_mode = 3;
#else
			sw->scope_mode = 0;
#endif
			if (sdl){
				sw->screen = SDL_CreateRGBSurface(SDL_SWSURFACE, sw->w*FONT_W, sw->h*FONT_H, sdl->bpp, 
						sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
				MUTEX_INIT(sw->screen);
				sw->gdirty = 1;
#ifdef HAVE_LIBFFTW3
				fft_resize(gfft, sw); 
#endif
			}
#endif
            break;

        case SWT_QRV:
            sw->title = g_strdup(VTEXT(T_SW_QRV));
			sw->kbd_func        = sw_qrv_kbd_func;
			sw->mouse_func      = sw_qrv_mouse_func;
			sw->redraw          = sw_qrv_redraw;
			sw->check_bounds    = sw_qrv_check_bounds;
			sw->raise           = sw_qrv_raise;
			sw->cur = 0;
            break;

        case SWT_HF:
            sw->title = g_strdup(VTEXT(T_SW_HF));
			sw->kbd_func        = sw_hf_kbd_func;
			sw->mouse_func      = sw_hf_mouse_func;
			sw->redraw          = sw_hf_redraw;
			sw->check_bounds    = sw_hf_check_bounds;
			sw->raise           = sw_hf_raise;
	//        sw->fifo            = NULL; /* filled in sw_stat* */
			sw->allqsos = 1;
            break;

        case SWT_PLAYER:
            sw->title = g_strdup(VTEXT(T_SW_PLAYER));
			sw->kbd_func        = sw_player_kbd_func;
			sw->mouse_func      = sw_player_mouse_func;
			sw->redraw          = sw_player_redraw;
			sw->check_bounds    = sw_player_check_bounds;
			sw->raise           = sw_player_raise;

#ifdef Z_HAVE_SDL        
			if (sdl && sw->h > PLAYER_H){
				sw->screen = SDL_CreateRGBSurface(SDL_SWSURFACE, sw->w*FONT_W, (sw->h - PLAYER_H)*FONT_H, sdl->bpp, 
						sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
				MUTEX_INIT(sw->screen);
				sw->gdirty = 1;
			}
#endif
            break;

        case SWT_CHART:
            sw->title = g_strdup(VTEXT(T_SW_CHART));
			sw->kbd_func        = sw_chart_kbd_func;
			sw->mouse_func      = sw_chart_mouse_func;
			sw->redraw          = sw_chart_redraw;
			sw->check_bounds    = sw_chart_check_bounds;
			sw->raise           = sw_chart_raise;

#ifdef Z_HAVE_SDL        
			if (sdl && sw->h > CHART_H){
				sw->screen = SDL_CreateRGBSurface(SDL_SWSURFACE, sw->w*FONT_W, (sw->h - CHART_H)*FONT_H, sdl->bpp, 
						sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
				MUTEX_INIT(sw->screen);
				sw->gdirty = 1;
			}
#endif
			sw->chbands = g_ptr_array_new();
			sw_chart_recalc_extremes(sw, aband);
            break;

		case SWT_KST:
			sw->title = g_strdup(VTEXT(T_KST));
			sw->kbd_func        = sw_kst_kbd_func;
			sw->mouse_func      = sw_kst_mouse_func;
			sw->redraw          = sw_kst_redraw;
			sw->check_bounds    = sw_kst_check_bounds;
			sw->raise			= sw_kst_raise;
        
			sw->lines = g_ptr_array_new();

			sw->il = g_new0(struct inputln, 1);
			sw->il->enter = sw_kst_enter;
			sw->il->enterdata = sw;
   			sw->il->allow_ctrlv = 1;

			sw->adns = zasyncdns_init();

			sw->timer_id = 0;
			sw->kstusers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); // 2x
#ifdef Z_HAVE_SDL
			sw->zoom = 10000;
			map_update_qth(sw);
#endif
			sw->first_shus = 1;
			sw->wrbuf = zbinbuf_init();

			{
				FILE *f;
				char s[1024];
				/*int *a = NULL;
				*a = 0;*/
				strcpy(s, "../src/_KST");
				z_wokna(s);
				f = fopen(s, "rt");
				if (!f) f = fopen("_KST", "rt");
				if (f){
					int w = sw->w;
	                progress(VTEXT(T_LOADING_S), s);
					if (gacs) sw->w -= KSTQRVW2;
					else sw->w -= KSTQRVW1;
					while ((fgets(s, 1000, f)) != NULL){
						sw_add_block(sw, s);
					}
					fclose(f);
					sw->w = w;
				}
    	
			}
			sw_add_line(sw, VTEXT(T_ENTER_TO_KST), 1);
			gettimeofday(&sw->kst_latest_data, NULL);
			break;
		case SWT_SDR:
            sw->title = g_strdup(VTEXT(T_SDR));
#ifdef USE_SDR 
			sw->kbd_func        = sw_sdr_kbd_func;
			sw->mouse_func      = sw_sdr_mouse_func;
			sw->redraw          = sw_sdr_redraw;
			sw->check_bounds    = sw_sdr_check_bounds;
			sw->raise           = sw_sdr_raise;

#ifdef Z_HAVE_SDL            
			if (sdl){
				sw->buttons = g_ptr_array_new();
				sw->screen = SDL_CreateRGBSurface(SDL_SWSURFACE, sw->w*FONT_W, sw->h*FONT_H, sdl->bpp, 
						sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
				MUTEX_INIT(sw->screen);
				sw->gdirty = 1;
				sdr_resize(gsdr, sw); 
			}
#endif            
			
#endif

			break;
		default:
            log_addf(VTEXT(T_INVALID_WINDOW_TYPE), sw->type);
			g_free(sw);
            return NULL;
	}

    sw->high = g_ptr_array_new();
    g_ptr_array_add(sw->high, g_strdup("<\\*[0-9a-zA-Z\\-]+\\*>"));

    if (sw->il){
        struct event ev = {EV_INIT, 0,0,0};
        sw->il->term = term;
        sw->il->sw = sw;
        inputln_func(sw->il, &ev);
        sw->il->x = sw->x;
        sw->il->y = sw->y + sw->h - 1;
        sw->il->l = sw->w;
        sw->h--;
    }
    
    if (sw->check_bounds) sw->check_bounds(sw);
	if (where_ >= 0){
		g_ptr_array_index(gses->subwins, where_) = sw;
		sw_set_ontop(where_, 0);
	}else{
		g_ptr_array_add(gses->subwins, sw);
		sw_set_ontop(gses->subwins->len - 1, 0);
	}
    
    return sw;
}

void free_subwins(){
    int i;

    for (i=gses->subwins->len-1;i>=0;i--){
        struct subwin *sw;

        sw=(struct subwin *)g_ptr_array_index(gses->subwins, i);
	    g_ptr_array_remove(gses->subwins, sw);
        free_subwin(sw);
    }
    g_ptr_array_free(gses->subwins, TRUE);
}

void free_subwin(struct subwin *sw){
	if (sw->timer_id) zselect_timer_kill(zsel, sw->timer_id);
    
    if (sw->high) zg_ptr_array_free_all(sw->high);
    if (sw->title) g_free(sw->title);
    if (sw->lines) zg_ptr_array_free_all(sw->lines);
    sw_shell_kill(sw);
	if (sw->iconv_handle != (iconv_t)-1) iconv_close(sw->iconv_handle);
	if (sw->sock >= 0){
		zselect_set(zsel, sw->sock, NULL, NULL, NULL, NULL);
		closesocket(sw->sock);
	}
#ifdef Z_HAVE_SDL    
    if (sw->l1map) SDL_FreeSurface(sw->l1map);
    //if (sw->l2rot) SDL_FreeSurface(sw->l2rot);
    if (sw->screen) SDL_FreeSurface(sw->screen);
    MUTEX_FREE(sw->screen);
    zg_free0(sw->pwwlo);
#ifdef HAVE_SNDFILE
    sw_player_free(sw);
#endif    
    sw_chart_free(sw);
	if (sw->chart) zchart_free(sw->chart);
#endif
    if (sw->il) g_free(sw->il);
	if (sw->adns) zasyncdns_free(sw->adns);
	if (sw->kstusers) g_hash_table_destroy(sw->kstusers);
	g_free(sw->acunder);
	g_free(sw->callunder);
	g_free(sw->refunder);
	zg_ptr_array_free_all(sw->callsunder);
	g_free(sw->pattern);
	g_free(sw->player_filename);
	zbinbuf_free(sw->wrbuf);
    if (sw->zser) zserial_free(sw->zser);
    g_free(sw);
}

struct subwin *sw_raise_or_new(enum sw_type type){
	struct subwin *swnew;
	struct event ev = { EV_INIT, 0, 0, 0, 0, 0};
	{
		zg_ptr_array_foreach(struct subwin *, sw, gses->subwins)
		{
			if (sw->type != type) continue;
			sw_set_ontop(sw_i, 0);
			return sw;
		}
	}
	swnew = new_subwin(type, -1);
    if (!swnew) return NULL;
	sw_default_func(swnew, &ev, 0);
	il_unset_focus(INPUTLN(aband));
    sw_set_focus();
	return swnew;
}

// in-place replace &amp; -> &
static void htmlchars(char *str){
	char *s = str;
	while (1){
		char *c = strchr(s, '&');
		if (c == NULL) return;

		if (strncasecmp(c + 1, "amp;", 4) == 0){
			memmove(c + 1, c + 5, strlen(c) - 4);
		}
		s++;
	}
}

int sw_add_line(struct subwin *sw, gchar *line, int eol){
    int len, a, ret;
    gchar *c, *last, *str, fill[10];
    GString *gs;
      
//    dbg("add_line '%s' %d \n", line, eol);
    len = strlen(line);
    if (len>0 && line[len-1]=='\r') line[len-1]='\0';
    
    
    if (sw->eol || sw->lines->len==0){
        if (sw->offset > 0 && 
            sw->offset < sw->maxlen-sw->h) {
            sw->offset++;
        }
        g_ptr_array_add(sw->lines, g_strdup(line));
        str = (gchar *)g_ptr_array_index(sw->lines, sw->lines->len - 1);
    }
    else{
        last = (gchar *)g_ptr_array_index(sw->lines, sw->lines->len - 1);
        str = g_strconcat( last, line, NULL);
        g_free(last);
    }

	if (sw->type == SWT_KST){
		htmlchars(str);
	}
    
	if (eol){
		ret = dxc_read_spot(str);
		if (ret != 1) sw->unread = 1;
		if (ret >= 0 && !sw->dxc_from_net){
            char *d=g_strconcat("DX ",str,"\n",NULL);
            rel_write_all(d);
            g_free(d);
        }
		if (ret > 0){ // not qrv band, filtering
			g_ptr_array_remove_index(sw->lines, sw->lines->len - 1);
			g_free(str);
			sw->eol = 1;
			if (sw->offset > 0) sw->offset--;
			return 1;
		}
        qrv_read_line(str);
		if (kst_read_line(sw, str) > 0){ // filtering /sh us output
			g_ptr_array_remove_index(sw->lines, sw->lines->len - 1);
			g_free(str);
			sw->eol = 1;
			if (sw->offset > 0) sw->offset--;
			return 1;
		}
    }
    
    c = strchr(str, '\t');
    if (c){
        gs = g_string_new(str);
        c = strchr(gs->str, '\t');
        while (c){
            a = c - gs->str;
            strcpy(fill, "         ");
            fill[8-(a%8)]='\0';
            g_string_erase(gs, a, 1);
            g_string_insert(gs, a, fill);
            c = strchr(gs->str, '\t');
        }
        g_free(str);
        str = g_strdup(gs->str);
        g_string_free(gs, 1);
    }
    
    g_ptr_array_index(sw->lines, sw->lines->len - 1) = str;
    sw->eol = eol;

	if (eol) {
        /*dbg("complete line: '%s'\n", str);*/

		char *x, *z;
		if (sw->type == SWT_KST && 
			strlen(str) > sw->w && 
			(x = strchr(str, '>')) != NULL && 
			x - str < sw->w - 20){
			GString *remains;

			z = strchr(x, ')');	  // take target call
			if (z != NULL){
				if (z - x < 17) x = z;
			}

			remains = g_string_new("");
			g_string_append_len(remains, str, x - str + 1);
			g_string_append(remains, "...");
			g_string_append(remains, str + sw->w);
			str[sw->w] = '\0';
			sw_add_line(sw, remains->str, 1);
			g_string_free(remains, TRUE);
		}
	}

    return 0;
}

int sw_add_block(struct subwin *sw, gchar *data){
    gchar *c, *d, *s;

    /*dbg("add_block '%s'\n", data);*/
    c = s = data;
    while (c != NULL && strlen(s)>0){
        c = strchr(s, '\n');
        if (c) {
            d = g_strndup(s, c-s);
            sw_add_line(sw, d, 1);
            g_free(d);
            s = c+1;
        }else{
            sw_add_line(sw, s, 0);
        }
    }
    sw_check_len(sw);
    redraw_later();
    return 0;
}

int sw_printf(struct subwin *sw, const char *m, ...){
	va_list l;
	char *c;
	int ret;

    va_start(l, m);
	c = g_strdup_vprintf(m, l);
	ret = sw_add_block(sw, c);
    g_free(c);
    va_end(l);
	return ret;
}

int sw_default_func(struct subwin *sw, struct event *ev, int fw){
    //dbg("sw_default_func %p,[%d,%d,%d,%d],%d)\n",sw,ev->ev,ev->x,ev->y ,ev->b,fw);
    //dbg("              term %dx%d \n",term->x, term->y);
    switch(ev->ev){
        case EV_ABORT:
			if (zselect_profiling(zsel)) zselect_hint(zsel, "sw_default_func EV_ABORT %s", sw->title);
            if (sw->il) inputln_func(sw->il,ev);
            sw_shell_kill(sw);
            break;
        case EV_INIT:
			if (zselect_profiling(zsel)) zselect_hint(zsel, "sw_default_func EV_INIT %s", sw->title);
            
            
//            break;
        case EV_RESIZE:
			if (zselect_profiling(zsel)) zselect_hint(zsel, "sw_default_func EV_RESIZE %s", sw->title);

            if (cfg->splitheight > 0){
                if (sw == gses->ontop && sw == gses->ontop2){
                    sw->y = gses->y1;
                    sw->h = MIN(gses->height1, gses->height2);
                }else if (sw == gses->ontop2){
                    sw->y = gses->y2;
                    sw->h = gses->height2;
                }else if (sw == gses->ontop){
                    sw->y = gses->y1;
                    sw->h = gses->height1;
                }
            }else{
               sw->y = gses->y1;
               sw->h = gses->height1;
            }
			
            sw->w = term->x - 2;
            sw->ww = sw->w;
            sw->hh = sw->h;

			//dbg("sw_default_func(EV_RESIZE, '%s') y=%d  h=%d\n", sw->title, sw->y, sw->h);

            /*dbg("sw_default_func: title=%s term->y=%d sw->h=%d\n", sw->title, term->y, sw->h); */
            
#ifdef Z_UNIX
            if (sw->write_fd>=0 && sw->write_fd==sw->read_fd &&
                (sw->ws.ws_col != sw->w ||
                sw->ws.ws_row != sw->h- 1)){
                sw->ws.ws_col = sw->w;
                sw->ws.ws_row = sw->h - 1;
                ioctl(sw->write_fd, TIOCSWINSZ, (char*)&sw->ws);
            }
#endif
            
			if (sw->type == SWT_DXC){
				if (sw->w >= BANDMAPMINW){
					sw->w -= BANDMAPW;
				}
			}
			if (sw->type == SWT_KST){
                if (gacs) sw->w -= KSTQRVW2;
                else sw->w -= KSTQRVW1;
			}
            if (sw->il) sw->h--;

#ifdef Z_HAVE_SDL            
            if (sw->screen){
				int h;
                MUTEX_LOCK(sw->screen);
                SDL_FreeSurface(sw->l1map);
//                SDL_FreeSurface(sw->l2rot);
                SDL_FreeSurface(sw->screen);
                h = sw->h*FONT_H;
                if (sw->type == SWT_PLAYER) h = (sw->h - PLAYER_H) * FONT_H;
                sw->l1map = SDL_CreateRGBSurface(SDL_SWSURFACE, sw->w*FONT_W, h, sdl->bpp, 
                    sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
                sw->screen = SDL_CreateRGBSurface(SDL_SWSURFACE, sw->w*FONT_W, sw->h*FONT_H, sdl->bpp, 
                    sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
                switch(sw->type){
                    case SWT_MAP:
                        map_update_layout(sw);
                        sw->gdirty=1;
                        break;
                    case SWT_SCOPE:
#ifdef USE_FFT
                        fft_resize(gfft, sw); 
#endif
                        break;
                    case SWT_PLAYER:
                        break;
                    case SWT_SDR:
#ifdef HAVE_LIBFFTW3
                        sdr_resize(gsdr, sw);
#endif
                        break;
                    default:
                        break;
                }
                MUTEX_UNLOCK(sw->screen);
            }
#endif
        case EV_REDRAW:
			if (zselect_profiling(zsel)) zselect_hint(zsel, "sw_default_func EV_REDRAW %s", sw->title);

            if (sw->redraw) sw->redraw(sw, aband, 0);
            if (sw->il) {
                inputln_func(sw->il,ev);
            }
            
            break;
        case EV_KBD:
			if (zselect_profiling(zsel)) zselect_hint(zsel, "sw_default_func EV_KBD %s", sw->title);
            if (sw->kbd_func && sw->kbd_func(sw, ev, fw)) return 1;
            break;
        case EV_MOUSE:
			if (zselect_profiling(zsel)) zselect_hint(zsel, "sw_default_func EV_MOUSE %s", sw->title);
#if 0
			dbg("sw_default_func EV_MOUSE %4d %4d ", ev->x, ev->y);
			if ((ev->b & BM_EBUTT) == B_LEFT) dbg(" LEFT");
			if ((ev->b & BM_EBUTT) == B_MIDDLE) dbg(" MIDDLE");
			if ((ev->b & BM_EBUTT) == B_RIGHT) dbg(" RIGHT");
			if ((ev->b & BM_EBUTT) == B_WHUP) dbg(" WHUP");
			if ((ev->b & BM_EBUTT) == B_WHDOWN) dbg(" WHDOWN");
			if (ev->b & B_UP) dbg(" UP");
			if (ev->b & B_DRAG) dbg(" DRAG");
			if (ev->b & B_MOVE) dbg(" MOVE");
			if (ev->b & B_CLICK) dbg(" CLICK");
			dbg("\n");
#endif
			//if (ev->b & B_MOVE) return 1;


            if ((ev->b & (B_MOVE | B_CLICK | B_DRAG)) ==0){
                if (!gses->focused){
                   sw_set_focus();
                   il_unset_focus(INPUTLN(aband));
                   redraw_later();
                }
            }
            if (sw->mouse_func && sw->mouse_func(sw, ev, fw)) return 1;
            break;
        default:
            error("ERROR: unknown event");    

    }
    return 0;
}


/* for init,abort & resize */
int sw_all_func(struct event *ev, int fw){
    struct subwin *sw;
    int i;

    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin*)g_ptr_array_index( gses->subwins, i);
        sw_default_func(sw, ev, 1);
    }
    return 0;
}

/* returns 1 if event was handled */
int sw_focus_func(struct event *ev, int fw){
    struct subwin *sw;
    int i;
	
	if (!gses->focused) return 0;

    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin*)g_ptr_array_index(gses->subwins, i);
		if (!sw->focused) continue;

		if (cfg->splitheight > 0 && sw->ontop2){
			return sw_default_func(sw, ev, 1);
		}
        if (sw->ontop ){
            return sw_default_func(sw, ev, 1);
        }
    }
    return 0;
}

int sw_ontop_func(struct event *ev, int fw){
    //struct subwin *sw;
    int ret;

	//  dbg("sw_ontop_func(ev=%d)\n", ev->ev);
	if (cfg->splitheight > 0){
		gses->ontop2->y = gses->y2;
		ret = sw_default_func(gses->ontop2, ev, 1);
		gses->ontop->y = gses->y1;
		ret |= sw_default_func(gses->ontop, ev, 1);
	}else{
		ret = sw_default_func(gses->ontop, ev, 1);
	}

    /*for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin*)g_ptr_array_index(gses->subwins, i);
		if (cfg->splitheight > 0 && sw->ontop2){
			sw_default_func(sw, ev, 1);
		}
        if (sw->ontop){
            return sw_default_func(sw, ev, 1);
        }
    } */
    return ret;
}

struct subwin *find_sw_ontop(){
    struct subwin *sw;
    int i;

    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin*)g_ptr_array_index( gses->subwins, i);
        if (sw->ontop) return sw;
    }
    return NULL;
}

void sw_set_focus(){
    struct subwin *sw;
	//dbg("sw_set_focus()\n");

    sw = gses->ontop;
    il_set_focus(sw->il);
    gses->focused = 1;
    /*if (gses->ontop->type == SWT_QSOS && aband){ 
        gses->ontop->cur = QSOS_LEN;
    } */
    if (gses->ontop->check_bounds) gses->ontop->check_bounds(gses->ontop);
}

void sw_unset_focus(){
	//dbg("sw_unset_focus()\n");

    il_unset_focus(gses->ontop->il);
    if (gses->ontop->type != SWT_QRV){
        gses->ontop->cur = 0;
    }
#ifdef Z_HAVE_SDL    
    if (gses->ontop->type == SWT_KST){
        kst_free_screen(gses->ontop);
    }
#endif
    gses->focused = 0;
	fill_lastarea(gses->ontop->x - 1, gses->ontop->y - 1, gses->ontop->ww + 2, gses->ontop->hh + 2, 0);
    if (gses->ontop->type != SWT_STAT){
        gses->ontop->offset = 0; 
    }
}

struct subwin *sw_set_ontop(int n, int set2){
    struct subwin *sw, *swret;
    int i;
	//struct event ev = { EV_RESIZE, 0, 0, 0, 0, 0};

    swret=NULL;
    if (n<0 || n>=gses->subwins->len) return swret;
    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin *)g_ptr_array_index( gses->subwins, i);
        if (i==n){
            if (set2){ 
				sw->ontop2 = 1;
				gses->ontop2 = sw;
            }else{
				sw->ontop = 1;
				gses->ontop = sw;
			}
			sw->focused = 1;
            sw->unread = 0;
            if (sw->il) il_set_focus(sw->il);
            swret = sw;
//			sw_default_func(sw, &ev, 1);   makes problems with scope - fft history is lost
            if (sw->raise) sw->raise(sw); // if (sw->raise) is correct
#ifdef HAVE_SNDFILE
            if (gssbd) gssbd->swontoptype = sw->type;
#endif
        }else{
			if (set2)
				sw->ontop2 = 0;
			else
				sw->ontop = 0;
			sw->focused = 0;
        }
        
    }
    return swret;
    
}

struct subwin *sw_totop_next(int n, int set2){
    struct subwin *sw,*ontop;
    int i;

    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin *)g_ptr_array_index( gses->subwins, i);
		ontop = set2 ? gses->ontop2 : gses->ontop;
        if (sw == ontop) break;
    }
    n+=i;
    if (n<0) n = gses->subwins->len-1;
    if (n>gses->subwins->len-1) n=0;

    return sw_set_ontop(n, set2);
}




void draw_titles(int y, int ontop2){
    struct subwin *sw;
    gchar *c;
    int i, x, max, ontop;
    
    /*dbg("draw_titles\n");*/
    sw = (struct subwin *)g_ptr_array_index( gses->subwins, 0);
    if (!sw) return;

    /* first try */
    x=sw->x+1;
    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin *)g_ptr_array_index( gses->subwins, i);
        c=g_strdup_printf("[%i:%s]", i+1, sw->title);
        sw->titl1=x;
        x+=strlen(c)+1;
        sw->titl2=x-2;
        g_free(c);
    }
    if (x-3 < sw->w) goto draw;
    /* second try */
    x=sw->x;
    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin *)g_ptr_array_index( gses->subwins, i);
        c=g_strdup_printf("[%i:%s]", i+1, sw->title);
        sw->titl1=x;
        x+=strlen(c);
        sw->titl2=x-2;
        g_free(c);
    }
    if (x-2 < sw->w) goto draw;
    /* third try */
    x=sw->x;
    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin *)g_ptr_array_index( gses->subwins, i);
        c=g_strdup_printf("%i:%s", i+1, sw->title);
        sw->titl1=x;
        x+=strlen(c)+1;
        sw->titl2=x-2;
        g_free(c);
    }
    if (x-3 < sw->w) goto draw;

/*            if (x+strlen(c)>=sw->w+1) max=sw-w-x+1; else max=-1;
            print_text(x,sw->y-1,max,c,col_active);
        }else{    
            c=g_strdup_printf(" %i:%s ", i+1, sw->title);
            if (x+strlen(c)>=sw->w+1) max=sw->w-x+1; else max=-1;
            print_text(x,sw->y-1,max,c,sw->unread?COL_YELLOW:col_passive);
        }
    }   */

draw:;            
    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin *)g_ptr_array_index( gses->subwins, i);
		ontop = ontop2 ? sw->ontop2 : sw->ontop;
        if (ontop) continue;

        c=g_strdup_printf(" %i:%s ", i+1, sw->title);
        if (sw->titl1 + strlen(c) >= sw->w + 1) max=sw->w-sw->titl1+1; else max=-1;
        print_text(sw->titl1,y,max,c,sw->unread?COL_YELLOW:COL_NORM);
        g_free(c);
    }

    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin *)g_ptr_array_index( gses->subwins, i);
		ontop = ontop2 ? sw->ontop2 : sw->ontop;
        if (!ontop) continue;
        
        c=g_strdup_printf("[%i:%s]", i+1, sw->title);
        if (sw->titl1 + strlen(c) >= sw->w + 1) max=sw->w-sw->titl1+1; else max=-1;
        print_text(sw->titl1,y,max,c,COL_INV);
        g_free(c);
    }
}

void sw_default_redraw(struct subwin *sw, int flags){
    
    draw_frame(sw->x,sw->y,sw->w,sw->h-1,COL_NORM,1);
    draw_frame(sw->x+1,sw->y+1,sw->w-2,sw->h-3,COL_NORM,1);
}


void sw_check_len(struct subwin *sw){
    gpointer p;
    int count,i;
    
    count = sw->lines->len - sw->maxlen;
    if (count>0){
        for (i=0; i<count; i++) {
            p = g_ptr_array_index(sw->lines, 0);   
            /*dbg("scl: %d:%p = '%s'\n",i, p, p); */
            g_free(p);
            g_ptr_array_remove_index(sw->lines, 0);
        }
    }
}

void sw_set_unread(struct fifo *fifo){
    struct subwin *sw;
    int i;

    if (!gses) return;
    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin *)g_ptr_array_index( gses->subwins, i);
/*        dbg(" %p",sw->fifo);*/
        if (sw->type == SWT_LOG) continue; /* don't highlight fifo */
        if (fifo != sw->fifo) continue;
        
        if (!sw->ontop && !sw->unread) {
            sw->unread=1;
            redraw_later();
        }
    }
}

void sw_unset_unread(struct fifo *fifo){
    struct subwin *sw;
    int i;

    if (!gses || !gses->subwins) return;
    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin *)g_ptr_array_index( gses->subwins, i);
/*        dbg(" %p",sw->fifo);*/
        if (sw->type == SWT_LOG) continue; /* don't highlight fifo */
        if (fifo != sw->fifo) continue;
        
        if (sw->unread) {
            sw->unread=0;
            redraw_later();
        }
    }
}


/*#define sw_highlight(text, ho, needle, color){\
    gchar *cc, *d, *fd; \
    int rx, fx, fl; \
    \
    if ((needle) && strlen(needle)>=2){ \
        cc=(text); \
        while ((d=z_strcasestr((cc), (needle)))!=NULL){ \
            fl=MIN(sw->w-(d-(text))+(ho) , strlen(needle)); \
            fx=rx=d-(text)-(ho); \
            fd=d; \
            if (rx<0) { \
                fl+=rx; \
                fx=0; \
                fd=d-rx; \
            } \
            if (fl>0) print_text(sw->x+fx, sw->y+i, fl, fd, (color)); \
            cc=d+strlen(needle); \
        } \
    }\
} */


struct config_subwin *get_config_sw_by_number(GPtrArray *sws, int nr){
    struct config_subwin *csw;
    int i;

    for (i=0; i<sws->len; i++){
        csw = (struct config_subwin *)g_ptr_array_index(sws, i);
        if (csw->nr==nr) return csw;
    }
    return NULL;   
}


/********************** QSOS ****************************************/

/* only EV_KBD */
int sw_qsos_kbd_func(struct subwin *sw, struct event *ev, int fw){
    /*dbg("sw_qsos_func [%d,%d,%d,%d]\n",ev->ev,ev->x,ev->y,ev->b);*/
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);

    switch(kbd_action(KM_MAIN,ev)){
        case ACT_ESC:
            return 0;
            break;
        case ACT_DOWN:
            sw->cur++;
            sw_qsos_check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_UP:
            sw->cur--;
            sw_qsos_check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_PAGE_DOWN:
            sw->cur += sw->h - 1;;
            sw_qsos_check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_PAGE_UP:
            sw->cur -= sw->h - 1;;
            sw_qsos_check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_HOME:
            sw->cur = 1;
            sw_qsos_check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_END:
            if (!aband) return 1;
            sw->cur = QSOS_LEN;
            sw_qsos_check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_ENTER:
            if (!aband) return 1;
            sw_qsos_check_bounds(sw);
            if (aband->readonly) {
                errbox(VTEXT(T_BAND_RO), 0);
                return 1;
            }
            if (sw->cur==0) break;
            edit_qso(get_gqso(aband, sw, sw->cur-1));
            
            return 1;    
        case ACT_PLAY_LAST:
            if (!aband) return 1;
#ifdef HAVE_SNDFILE
            ssbd_play_last_sample(gssbd, get_gqso(aband, sw, sw->cur-1));
#endif
            break;
        case ACT_SKED: 
            if (!aband) return 0;
            if (sw->cur==0) break;
            sked_from_qso(get_gqso(aband, sw, sw->cur-1));
            return 1;
		case ACT_SCROLL_LEFT:
    	    if (sw->ho>0) sw->ho--;
            redraw_later();
            return 1;
		case ACT_SCROLL_RIGHT:
		    if (sw->ho<73) sw->ho++;
            redraw_later();
            return 1;
        case ACT_CALLINFO:
            if (sw->cur==0) break;
            call_info(get_gqso(aband, sw, sw->cur-1));
            return 1;

        
    }
	switch (ev->x){
		case 'A':
		case 'a':
			if (ev->y == 0){
                sw->allqsos = sw->allqsos ? 0 : 1;
				sw_qsos_check_bounds(sw);
				redraw_later();
			}
			break;
	}
    return 0;
}



int sw_qsos_mouse_func(struct subwin *sw, struct event *ev, int fw){
    int y0;
    
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);

	if (ev->b & B_DRAG){
		int dy = sw_accel_dy(sw, ev);
		if (dy != 0) {
			if (cfg->usetouch) dy = -dy;

			sw->cur += dy;
			sw_qsos_check_bounds(sw);
			redraw_later();
		}
		sw->olddragx = ev->x;

		return 1;
	}
    /*dbg("sw_qsos_mouse_func\n");*/
  /*  if ((ev->b & BM_ACT)!=B_DOWN) return 0; */
    switch (ev->b & (BM_EBUTT | B_CLICK | B_MOVE)){
		case B_LEFT:
			sw->olddragx = ev->x;
			sw->olddragy = ev->my;
			return 1;
        case B_LEFT | B_CLICK:
			if (!aband) return 1;
			y0=0;
			if (QSOS_LEN+1 <= sw->h)
				y0 = sw->h - (QSOS_LEN+1) + 1;
            
			sw->cur = QSOS_LEN + 1 - sw->h + ev->y - sw->y - sw->offset + y0;
			sw_qsos_check_bounds(sw);
			redraw_later();
			if (cfg->usetouch){
				if (aband->readonly) {
					errbox(VTEXT(T_BAND_RO), 0);
					return 1;
				}
				if (sw->cur==0) break;
				edit_qso(get_gqso(aband, sw, sw->cur-1));
			}
			return 1;
        case B_MIDDLE:
        /*    dbg("middle\n");*/
            break;
        case B_RIGHT:
            /*dbg("right\n");*/
            break;
        case B_WHUP:
            /*dbg("wheel up\n");*/
            sw->cur-=3;
            sw_qsos_check_bounds(sw);
            redraw_later();
            return 1;
        case B_WHDOWN:
            /*dbg("wheel down\n");*/
            sw->cur+=3;
            sw_qsos_check_bounds(sw);
            redraw_later();
            return 1;
    }
    return 0;
}

int show_qs(){ /* gses->qs is locked */
    
    if (gses->ontop && gses->ontop->type == SWT_HF) return 0;
    if (aband){

        if (( aband->qs->len>0 || 
              aband->oqs->len>0 || 
              gses->qs->len>0 ) 
            && aband->il->focused ) return 1;
    }else{
        /*if (gses->qs->len>0) return 1;    */
        if (gses->qs->len>0 && INPUTLN(aband)->focused) return 1;
    }
    return 0;
}

void sw_qs_redraw(){ /* gses->qs is locked */
    int i, max0, max1, max2;
    gchar *c;
    struct subwin *sw=NULL;

    for (i=0;i<gses->subwins->len;i++){
        sw=(struct subwin *)g_ptr_array_index(gses->subwins,i);
        if (sw->type!=SWT_QSOS) continue;
        break;
    }
    if (i==gses->subwins->len) return;
    
    max0=27;
    if (term->x-3<max0) max0=term->x-3;
    if (max0<0) max0=0;
    max1=27;
    if (term->x-32<max1) max1=term->x-32;
    if (max1<0) max1=0;
    max2=53;
    if (term->x-62<max2) max2=term->x-62;
    if (max2<0) max2=0;

    for (i=0;i<sw->h;i++){
    
        set_char(29, sw->y+i, 0x8000|COL_NORM|179);
        set_char(58, sw->y+i, 0x8000|COL_NORM|179);
        if (i==sw->h-1) {
            if (ctest)
                print_text(sw->x+ 1+3 ,sw->y+i,-1,VTEXT(T_THIS_BAND),COL_NORM);
            else 
                print_text(sw->x+ 1+3 ,sw->y+i,-1,VTEXT(T_CW_DB),COL_NORM);
            
            if (ctest && ctest->bands->len>1)
                print_text(sw->x+30+3 ,sw->y+i,-1,VTEXT(T_OTHER_BANDS),COL_NORM);
            else
                print_text(sw->x+30+3 ,sw->y+i,-1,VTEXT(T_CW_DB),COL_NORM);
            
            print_text(sw->x+60+3 ,sw->y+i,-1,VTEXT(T_CW_DB),COL_NORM);
            continue;
        }

        if (ctest){
            if (i<aband->qs->len){
                c = (gchar *)g_ptr_array_index(aband->qs, i);
                print_text(sw->x+1 ,sw->y+i,max0,c,COL_NORM);
            }
            if (ctest->bands->len>1){
                if (i<aband->oqs->len){
                    c = (gchar *)g_ptr_array_index(aband->oqs, i);
                    print_text(sw->x+30 ,sw->y+i,max1,c,COL_NORM);
                }
            
                if (i<gses->qs->len){
                    c = (gchar *)g_ptr_array_index(gses->qs, i);
                    print_text(sw->x+60,sw->y+i,max2,c,COL_NORM);
                }
            }else{
                if (i<gses->qs->len){
                    c = (gchar *)g_ptr_array_index(gses->qs, i);
                    print_text(sw->x+30,sw->y+i,max1,c,COL_NORM);
                }

                if (i+sw->h-1 < gses->qs->len){
                    c = (gchar *)g_ptr_array_index(gses->qs, i + sw->h-1 );
                    print_text(sw->x+60,sw->y+i,max2,c,COL_NORM);
                }
            }
        }else{
            if (i<gses->qs->len){
                c = (gchar *)g_ptr_array_index(gses->qs, i);
                print_text(sw->x+1,sw->y+i,max0,c,COL_NORM);
            }

            if (i+sw->h-1 < gses->qs->len){
                c = (gchar *)g_ptr_array_index(gses->qs, i + sw->h-1 );
                print_text(sw->x+30,sw->y+i,max1,c,COL_NORM);
            }

            if (i+2*(sw->h-1) < gses->qs->len){
                c = (gchar *)g_ptr_array_index(gses->qs, i + 2*(sw->h-1) );
                print_text(sw->x+59 ,sw->y+i,max2,c,COL_NORM);
            }
        }
        
    }
}


void sw_qsos_redraw(struct subwin *sw, struct band *band, int flags){
    int i, index, y0, len;
    struct qso *q;
    gchar *c;
    char dtime[6];


    /*dbg("term->x=%d max0=%d max1=%d max2=%d \n", term->x, max0, max1, max2);*/

    MUTEX_LOCK(gses->qs);
    if (show_qs()){
        sw_qs_redraw();
        MUTEX_UNLOCK(gses->qs);
        return;
    }
    MUTEX_UNLOCK(gses->qs);

    if (!aband) return;
        
    y0=0;
    if (QSOS_LEN+1 <= sw->h)
        y0 = sw->h - (QSOS_LEN+1) + 1;
    
    for (i=0;i<sw->h;i++){
        char dateband[16];
        char call[50];
        char new_[10];
        char qsop[15];
        char suspcall, susploc,unkcall;
        char qtf[15];
        char exc[10];
        

        index = QSOS_LEN+1-sw->h + i - sw->offset;
        if (index < 1) {
            print_text(sw->x,sw->y+i-y0+sw->h,-1," ~",COL_NORM);
            continue;
        }

        q = get_gqso(aband, sw, index-1);
        strcpy(call,q->callsign);
        if (q->error) {
            if (q->callsign && strcmp(q->callsign, "ERROR")!=0){
                sprintf(call, "ERROR %s", q->callsign);
                z_str_lc(call+5);
            }else
                strcpy(call, "ERROR");
        }
        call[12]='\0';
        
        strcpy(new_, "");
        if (ctest->wwlused && (q->new_ & NEW_WWL)) strcat(new_, "w");
        if (                   q->new_ & NEW_DXC ) strcat(new_, "d");
        if (ctest->exctype>0 && (q->new_ & NEW_EXC)) strcat(new_, "e");
        if (q->new_ & NEW_PREF)                    strcat(new_, "p");
        if (q->qsl)                             strcat(new_, "q");
        if (q->remark && strlen(q->remark)>0)   strcat(new_, "r");
		if (q->new_ & NEW_PHASE) strcat(new_, "h");

        if (!q->dupe) sprintf(qsop,"%6d", q->qsop);
        else strcpy(qsop, "DUPE");
            
        dtime[0]=q->time_str[0]; dtime[1]=q->time_str[1]; dtime[2]=':';
        dtime[3]=q->time_str[2]; dtime[4]=q->time_str[3]; dtime[5]='\0';
        
        switch(q->suspcall1){
            case SUSP_QRB:
                suspcall='>';
                break;
            case SUSP_REM:
                suspcall='r';
                break;
            case SUSP_WARN:
                suspcall='?';
                break;
            case SUSP_ERR:
                suspcall='!';
                break;
            default:
                suspcall=' ';
                break;
        }

        if (q->susploc==1) susploc='?';
        else if (q->susploc==2) susploc='!';
        else susploc=' ';
        if (q->unkcall) unkcall=VTEXT(T_HK_NEW_CALL)[0];
        else unkcall=' ';
        sprintf(qtf,"%3d", q->qtf);
#if defined(Z_HAVE_SDL) && !defined(Z_ANDROID)     
//        if (sdl) strcat(qtf, "°");
        if (sdl) strcat(qtf, "\xb0");
#endif
        safe_strncpy0(exc, q->exc, 5);
        g_snprintf(dateband, sizeof(dateband), "  %8s", q->date_str);
        len = strlen(q->band->bandname);
		if (sw->allqsos && len>0){
            memcpy(dateband, "           ", 10);
           // if (len>4) len=4;
            memcpy(dateband+2, q->band->bandname, Z_MIN(len, 8));
//            dbg("bandname='%s'\n", q->band->bandname);
        }
        
		char qrgstr[50];
		if (q->qrg > 0){
			z_qrg_format(qrgstr, 30, q->qrg);
			strcat(qrgstr, " ");
		}else{
			strcpy(qrgstr, "");
		}

        c=g_strdup_printf("%10s %5s %-12s%c%5s%4s%5s%4s %-4s %-6s %c%c%6s %-4s%-5s %-7s %s%s",
                dateband, dtime, call, suspcall, 
                q->rsts, q->qsonrs, q->rstr, q->qsonrr,
                exc, q->locator, susploc, unkcall,
                qsop, *q->locator?qtf:"", new_, q->operator_,
                qrgstr, q->remark);

		if (index==sw->cur && !cfg->usetouch) fill_area(sw->x, sw->y+i-y0, sw->w, 1, COL_INV);
        if (strlen(c)>sw->ho) 
			print_text(sw->x,sw->y+i-y0,sw->w,c+sw->ho, index==sw->cur && !cfg->usetouch? COL_INV : COL_NORM);
        
        
        g_free(c);
        
    }
    /*dbg("\n");*/
}

void sw_qsos_check_bounds(struct subwin *sw){
    int len;

    if (!aband) return;
    
    /*dbg("sw_qsos_check_bounds cur=%d, offset=%d ", sw->cur, sw->offset);*/
    
    len = QSOS_LEN;
    if (sw->cur < 1 ){
		sw->cur = 1;
        if (sw->cur <= len) sw_shake(sw, 1);
    }
    if (sw->cur > len) {
        sw->cur = len;
        if (len > 0) sw_shake(sw, 0);
    }

    
    if (sw->cur > len - sw->offset - 1){
        sw->offset = len - sw->cur;
    }
    if (sw->cur < len - sw->h + 1 - sw->offset){
        sw->offset = len - sw->h - sw->cur + 1;
    }
    if (sw->offset < 0) sw->offset=0;
/*    dbg(" ---> cur=%d, offset=%d\n", sw->cur, sw->offset);*/
}


/************************ FIFO *******************************/

/* only EV_KBD */
int sw_fifo_kbd_func(struct subwin *sw, struct event *ev, int fw){
    /*dbg("sw_fifo_func [%d,%d,%d,%d]\n",ev->ev,ev->x,ev->y,ev->b);*/
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);

    
    if (sw->il && (
        sw->il->wasctrlv ||    
		(ev->x!='[' && ev->x!=']') )){
	    if (inputln_func(sw->il, ev)) return 1;
	}

    switch(kbd_action(KM_MAIN,ev)){
        case ACT_ESC:
            return 0;
            break;
        case ACT_DOWN:
			dbg("DOWN: ofs pred=%d\n", sw->offset);
            sw->offset--;
            sw->check_bounds(sw);
			dbg("DOWN: ofs pote=%d\n", sw->offset);
            redraw_later();
            return 1;
        case ACT_UP:
			dbg("UP: ofs pred=%d\n", sw->offset);
            sw->offset++;
            sw->check_bounds(sw);
			dbg("UP: ofs pote=%d\n", sw->offset);
            redraw_later();
            return 1;
        case ACT_PAGE_DOWN:
            sw->offset -= sw->h - 1;;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_PAGE_UP:
            sw->offset += sw->h - 1;;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_HOME:
            sw->offset = fifo_len(sw->fifo) - sw->h; 
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case ACT_END:
            sw->offset = 0;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
/*		case ACT_SCROLL_LEFT:
			if (sw->fifo->ho>0) sw->fifo->ho--;
            redraw_later();
            return 1;
		case ACT_SCROLL_RIGHT:
			sw->fifo->ho++;
            redraw_later();
            return 1;*/
		case ACT_SCROLL_LEFT:
            if (sw->fifo)
    			if (sw->fifo->ho>0) sw->fifo->ho--;
            if (sw->lines)
    			if (sw->ho>0) sw->ho--;
            redraw_later();
            return 1;
		case ACT_SCROLL_RIGHT:
            if (sw->fifo)
			    sw->fifo->ho++;
            if (sw->lines)
			    sw->ho++;
            redraw_later();
			return 1;
		case ACT_ENTER:
			sw->fifo->withouttime = 1;
			fifo_adds(sw->fifo, "");
			sw->fifo->withouttime = 0;
			return 1;
    }
    return 0;
}

int sw_fifo_mouse_func(struct subwin *sw, struct event *ev, int fw){
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);

	if (ev->b & B_DRAG){
		int dx = ev->x - sw->olddragx;
        int dy = sw_accel_dy(sw, ev);
			
		//dbg("dx=%d  dy=%d  sw->ho=%d\n", dx, dy, sw->ho);
                     
#ifdef Z_HAVE_SDL
		if (abs(dx * FONT_W) > abs(dy)){
			if (sw->fifo) {
				sw->fifo->ho -= dx;
				if (sw->fifo->ho < 0) sw->fifo->ho = 0; 
			}
            if (sw->lines){
				sw->ho -= dx;
    			if (sw->ho < 0) sw->ho = 0; 
			}
		}else{
			sw->offset += dy;
		}
#endif
		sw->olddragx = ev->x;
        sw->check_bounds(sw);
        redraw_later();
		return 1;
	}

    //if ((ev->b & BM_ACT)!=B_DOWN) return 0;
    switch (ev->b & (BM_EBUTT | B_CLICK | B_MOVE)){
		case B_LEFT:
			sw->olddragx = ev->x;
			sw->olddragy = ev->my;
			return 1;
        case B_LEFT | B_CLICK:
            //dbg("x il=%p\n",sw->il);
            if (!sw->il) break;
/*            dbg("y=%d sw->y=%d sw->hh=%d sw->h=%d\n", ev->y, sw->y, sw->hh, sw->h);*/
            if (ev->y==sw->y+sw->h)
                inputln_func(sw->il, ev);
            return 1;
        case B_MIDDLE:
            //dbg("middle\n");
            break;
        case B_RIGHT:
           // dbg("right\n");
            break;
        case B_WHUP:
            //dbg("wheel up\n");
            sw->offset+=3;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case B_WHDOWN:
           // dbg("wheel down\n");
            sw->offset-=3;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
    }
    return 0;
}


void sw_fifo_redraw(struct subwin *sw, struct band *band, int flags){
    int i, index;
    gchar *c, *suff;
    
    /*dbg("sw_log_redraw  log->len=%d sw->h=%d sw->offset=%d \n", fifo_len(sw->fifo), sw->h, sw->offset);*/
    
    for (i=0;i<sw->h; i++){
        index = fifo_len(sw->fifo) - sw->offset - sw->h+i;
        if (index<0 || index>=fifo_len(sw->fifo))
           c = "~"; 
        else
           c = fifo_index(sw->fifo, index);

        if (c && strlen(c)>sw->fifo->ho) {
			print_text(sw->x, sw->y+i, sw->w, c+sw->fifo->ho, COL_NORM);

            if (!aband) continue;
            sw_highlight(sw, c, ctest->pcall, COL_YELLOW, i, 1);
            if (strlen(ctest->pcall)>=2){
                suff=z_suffix(ctest->pcall);
                sw_highlight(sw, c, suff, COL_YELLOW, i, 1);
            }
            
            sw_highlight(sw, c, aband->operator_, COL_YELLOW, i, 1);
            if (strlen(aband->operator_)>=2){
                suff=z_suffix(aband->operator_);
                sw_highlight(sw, c, suff, COL_YELLOW, i, 1);
            }

        }
    }
    
}

void sw_fifo_check_bounds(struct subwin *sw){
    	 // sw->offset = fifo_len(sw->fifo) - sw->h; 
   	if (sw->offset < 0) {
		sw->offset=0;
		sw_shake(sw, 0);
		return;
	}
	if (fifo_len(sw->fifo) <= sw->h){
		if (sw->offset > 0){
			sw_shake(sw, 1);
			sw->offset = 0;
		}
	}else{
		if (sw->offset > fifo_len(sw->fifo) - sw->h) {
			sw->offset = fifo_len(sw->fifo) - sw->h; // must be >0
			sw_shake(sw, 1);
		}
	}
    
}



/********************** SHELL ****************************************/

/* Return:
 * -1 = hidden
 *  0 = not highlighted
 *  1 = highlighted, call not worked
 *  2 = highlighted, call worked
 */

int sw_line_is_highlighted(struct subwin *sw, gchar *c){
    int i, ret;

    for (i=0; i<sw->high->len; i++){
        if (!regcmp(c, (char *)g_ptr_array_index(sw->high, i))){
            return 1;
        }
    } 
    ret = sw_shell_hihglight(sw, c);
	return ret;
}

void sw_shell_redraw(struct subwin *sw, struct band *band, int flags){
    int i, index, col=COL_NORM;
    gchar *c, *to, *call, *suffcall, *operator_, *suffoperator;

	if (ctest){
		call=ctest->pcall;
		operator_=aband->operator_;
	}else{
		call=cfg->pcall;
		operator_=cfg->operator_;
		if (!operator_ || !*operator_) operator_=NULL;
	}

	if (strlen(call)>3){
   		suffcall=z_suffix(call);
	}else{
		suffcall=NULL;
	}

    if (operator_ && strlen(operator_)>3){
		suffoperator=z_suffix(operator_);
	}else{
		suffoperator=NULL;
	}
	to=g_strconcat("to ", call, NULL);
    
/*    dbg("sw_shell_redraw: offset=%d\n", sw->offset);*/
    for (i=0;i<sw->h; i++){
        index = sw->lines->len - sw->offset - sw->h +i;
        if (index<0 || index>=sw->lines->len)
           c = "~"; 
        else
           c = (char *)g_ptr_array_index(sw->lines, index);

        if (c && strlen(c)>sw->ho){
            switch (sw_line_is_highlighted(sw, c)){
                case 0:
                    col=COL_NORM;
                    break;
                case 1:
                    col=COL_YELLOW;
                    break;
                case 2:
                    col=COL_DARKYELLOW;
                    break;
            }

            print_text(sw->x, sw->y+i, sw->w, c + sw->ho, col);
			continue;

			sw_highlight(sw, c, call, COL_RED, i, 1);
			if (suffcall){
				sw_highlight(sw, c, suffcall, COL_RED, i, 1);
			}
			if (operator_){
				sw_highlight(sw, c, operator_, COL_RED, i, 1);
				if (suffoperator){
					sw_highlight(sw, c, suffoperator, COL_RED, i, 1);
				}
			}
		
			sw_highlight(sw, c, to, COL_RED, i, 1);

        }
    }
	g_free(to);
}


void sw_shell_check_bounds(struct subwin *sw){
    
    if (sw->offset < 0) {
        sw->offset=0;
        sw_shake(sw, 0);
    }
	if (sw->lines->len - 1 <= sw->h){
		if (sw->offset > 0){
			sw_shake(sw, 1);
			sw->offset = 0;
		}
	}else{
		if (sw->offset > sw->lines->len - sw->h){
			sw->offset = sw->lines->len - sw->h;
			sw_shake(sw, 1);
		}
	}

    /*if (sw->offset > sw->lines->len - 1) {
        sw->offset = sw->lines->len - 1;
        if (sw->offset >= 0) sw_shake(sw, 1);
    }
    if (sw->offset < 0) {
        sw->offset=0;
        sw_shake(sw, 0);
    } */
} 

/*static time_t lastenter = 0;*/

void sw_shell_enter(void *data, gchar *str, int cq){
    struct subwin *sw;
    int towrite, ret;
    sw = (struct subwin*)data;

    /*dbg("sw_shell_enter(%p,'%s')\n",data,str);*/
    
    
    

    towrite = strlen(str);
	ret = zserial_write(sw->zser, str, towrite);
    if (ret < 0) goto failed;
#ifdef Z_MSC_MINGW
	ret = zserial_write(sw->zser, "\r\n", 2);
    if (ret < 0) goto failed;
#else
	ret = zserial_write(sw->zser, "\n", 1);
    if (ret < 0) goto failed;
#endif
    //dbg("   towrite=%d  ret=%d (%d)\n", towrite, ret, errno);
#if defined(Z_UNIX_ANDROID) && !defined(Z_HAVE_PTY_H)
    char s[100];
    sw_add_block(sw, str);
    sw_add_block(sw, "\n");
    strcpy(s, "echo -n \"$HOSTNAME $PWD\\$ \"\n");
    ret = zserial_write(sw->zser, s, strlen(s));
    if (ret < 0) goto failed;
#endif
    return;
failed:;
    sw_add_block(sw, VTEXT(T_PROC_EXITED));
    sw_shell_kill(sw);
    
}


void sw_shell_read_handler(void *arg){
    struct subwin *sw;
    char buf[1030], *d;
    int ret,i;

	char buf2[1030], *pbuf, *pbuf2;
    

    sw = (struct subwin*)arg;

    memset(buf, 0, sizeof(buf));
    /*dbg("sw_shell_read_handler\n");*/
    ret = z_pipe_read(sw->read_fd, buf, 1024);
    /*dbg("read returns %d (%d) buf='%s' \n", ret, errno, buf);*/

    if (ret<=0) {
        sw_add_block(sw, VTEXT(T_PROC_EXITED));
        sw_shell_kill(sw);
        return;
    }
    for (i=0,d=buf;i<ret;i++){
        if (buf[i]=='\0') continue;
        if (buf[i]=='\r') continue;
        *d=buf[i];
        d++;
    }
    *d='\0';
    buf[ret]='\0';

	if (sw->iconv_handle != (iconv_t)-1){
		size_t a = ret, b = ret;
        pbuf = buf;
        pbuf2 = buf2;
		iconv(sw->iconv_handle, &pbuf, &a, &pbuf2, &b);
	    buf2[ret - b]='\0';
		sw_add_block(sw, buf2);
	}else{
		sw_add_block(sw, buf);
	}
    if (!sw->ontop && !sw->unread) {
        sw->unread=1;
        redraw_later();
    }

}


void sw_shell_kill(struct subwin *sw){
    if (sw->zser){
    	zserial_close(sw->zser);
//        sw->zser = NULL;
    }
	if (sw->read_fd >= 0){
		closesocket(sw->read_fd); 
		zselect_set(zsel, sw->read_fd,  NULL, NULL, NULL, NULL);
	}
    
    if (sw->write_fd >= 0 && sw->write_fd != sw->read_fd){
        closesocket(sw->write_fd); 
        zselect_set(zsel, sw->write_fd, NULL, NULL, NULL, NULL);
    }
    sw->read_fd  = -1;
    sw->write_fd = -1;
}

#define FREE_SW_CX if (c1) {g_free(c1); c1=NULL;} if (c2) {g_free(c2); c2=NULL;}

void sw_toggle_highlight(struct subwin *sw, gchar *s){
    int ret;
    char *c1,*c2;
    char *c, call[25];
    //time_t kst_time = 0;
    //char *text = NULL;
    
    //dbg("sw_toggle_highlight(sw, '%s')\n", s);
    c=g_strdup(s);
    z_str_uc(c);
    /**** WWC via ON4KST ****/
    /* 1808Z WWC: <f4azf>: jcs done today !! */
    c1=NULL;
    c2=NULL;
    ret=regmatch(c, "^[0-9]{4}Z WWC: <\\**([0-9A-Z\\/]+)\\**>: ", &c1, &c2, NULL);
    if (ret==0){
        safe_strncpy0(call, c2, 19);
        //text = "@WWC via KST";
        goto doit;
    }
    FREE_SW_CX;
    /**** ON4KST ****/
    /* 1808Z OK1KRQ/P Club> GE all */
    c1=NULL;
    c2=NULL;
    ret=regmatch(c, "^[0-9]{4}Z ([0-9A-Z\\/]+).*>", &c1, &c2, NULL);
    if (ret==0){
        safe_strncpy0(call, c2, 19);
        //kst_time = time(NULL);
        //text = "@KST";
        goto doit;
    }
    FREE_SW_CX;
    /**** WWC ****/
    /* <f4azf>: allok ! */
    c1=NULL;
    c2=NULL;
    ret=regmatch(c, "^<\\**([0-9A-Z\\/]+)\\**>: ", &c1, &c2, NULL);
    if (ret==0){
        safe_strncpy0(call, c2, 19);
        //text = "@WWC";
        goto doit;
    }
    FREE_SW_CX;
    /**** DX cluster ****/
    /* DX de PA4PS:    144145.0  K6MYC        Tnx eme new# best-20           0841Z */
    c1=NULL;
    c2=NULL;
    ret=regmatch(c, "^DX [Dd][Ee] [0-9A-Z/]+:..............([0-9A-Z\\/]+)", &c1, &c2, NULL);
    if (ret==0){
        safe_strncpy0(call, c2, 19);
        //text = "@DXC";
        goto doit;
    }
    FREE_SW_CX;
    /**** DX cluster SH/DX ****/
/* 432160.0  OK1KIR       7-Oct-2007 0010Z  PSE TURN TO JN 76             <S57M>
   2320170.0  OK1KIR       7-Oct-2007 1338Z  tnx qso 678km.                <HA8V>
   24048140.0  OK1KIR       6-Oct-2007 2148Z  jo60pm-jo50vi               <DL6NCI>
   24048100.0  OK1KIR       6-Oct-2007 1314Z  nice 59/59 in JO60LJ         <OK7RA> */
    c1=NULL;
    c2=NULL;
    ret=regmatch(c, "^ *[0-9]+\\.[0-9]  ([0-9A-Z\\/]+) ", &c1, &c2, NULL);
    if (ret==0){
        safe_strncpy0(call, c2, 19);
        //text = "DXC SH/DX";
        goto doit;
    }
    FREE_SW_CX;
    /**** DX cluster login/logout ****/
    /* User OM3TCG has logged in */
    c1=NULL;
    c2=NULL;
    ret=regmatch(c, "^USER ([0-9A-Z\\/]+) HAS LOGGED ", &c1, &c2, NULL);
    if (ret==0){
        safe_strncpy0(call, c2, 19);
        //text = "DXC LOGIN/LOGOUT";
        goto doit;
    }
    FREE_SW_CX;
    /**/
doit:;    
   /* dbg("ret=%d  c1='%s'  c2='%s'\n", ret, c1, c2);*/
    FREE_SW_CX;
    g_free(c);
    if (ret) return;

	sw_kst_toggle_highlight(sw, call, 1);
    /*z_get_raw_call(t, call);
    

    if (g_hash_table_lookup_extended(gses->hicalls, (gpointer)t, (gpointer)&key, (gpointer)&value)){
        g_hash_table_remove(gses->hicalls, key);
        g_free(key);
        g_free(value);
    	dbg("call='%s' removed\n", t);
        
    }else{
        g_hash_table_insert(gses->hicalls, g_strdup(t), g_strdup(""));
        if (aband){
            int wkd[32];
			char *wwl = NULL;

            wwl = find_wwl_by_call(cw, t);
            if (wwl == NULL) wwl = "";
            memset(wkd, 0, sizeof(wkd));
            qrv_add(t, wwl, (1<<aband->bi), wkd, text, kst_time);  
        }
    	dbg("call='%s' inserted\n", t);
    } */
    
/*    dbg("%d\n", g_hash_table_size(gses->hicalls));*/
    redraw_later();
}

int sw_shell_mouse_func(struct subwin *sw, struct event *ev, int fw){
    int index;
    gchar *c;
    
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);
	
	if (ev->b & B_DRAG){
		int dy = sw_accel_dy(sw, ev);
		if (dy == 0) return 1;

        sw->offset += dy;
        sw->check_bounds(sw);
        redraw_later();

		sw->olddragx = ev->x;
		return 1;
	}
    //if ((ev->b & BM_ACT)!=B_DOWN) return 0;
//    dbg("ev->b=%x BM_ACT=%x B_DOWN=%x BM_EBUTT=%x  \n", ev->b, BM_ACT, B_DOWN, BM_EBUTT);
	switch (ev->b & BM_BUTCL){
        case B_LEFT:
			sw->olddragx = ev->x;
			sw->olddragy = ev->my;
			break;
        case B_LEFT | B_CLICK:
            //dbg("x il=%p\n",sw->il);
            if (!sw->il) break;
/*            dbg("y=%d y=%d hh=%d \n", ev->y, sw->y, sw->hh);*/
            if (ev->y==sw->y+sw->h){
                inputln_func(sw->il, ev);
                return 1;
            }
            index = sw->lines->len - sw->offset - sw->h + (ev->y - sw->y);
/*            dbg("index=%d\n",index);*/
            if (index<0 || index>=sw->lines->len) break;
            c = (char *)g_ptr_array_index(sw->lines, index);
/*            dbg("c='%s'\n", c);*/
            sw_toggle_highlight(sw, c);
            return 1;
        case B_MIDDLE:
/*            dbg("middle\n");*/
            break;
        case B_RIGHT:
/*            dbg("right\n");*/
            break;
        case B_WHUP:
/*            dbg("wheel up\n");*/
            sw->offset+=3;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
        case B_WHDOWN:
/*            dbg("wheel down\n");  */
            sw->offset-=3;
            sw->check_bounds(sw);
            redraw_later();
            return 1;
    }
    return 0;
}

static int sw_match;
void sw_shell_match(gpointer acall, gpointer pval, gpointer astr){
    gchar *call = (gchar *)acall;
	int *value = (int *)pval;
    gchar *str = (gchar *)astr;

    if (*value < sw_match) return; /* speed up search */
    /*dbg("    match call='%s' str='%s'\n", call, str);*/
    //return regcmpi(str, call);
	if (strstr(str, call) == NULL) return;
    sw_match = *value;
    
    if (!aband) return;
    //if (get_qso_by_callsign(aband, call)==NULL) return;
    if (!worked_on_all_rw(call)) return;
    sw_match=2;
}

int sw_shell_hihglight(struct subwin *sw, char *str){
    sw_match=-100;
    g_hash_table_foreach(gses->hicalls, sw_shell_match, str);
//    dbg("sw_match: %d for %s\n", sw_match, str);
    return sw_match;
}


/******************** TALK ************************/

void sw_talk_enter(void *enterdata, gchar *str, int cq){
    struct subwin *sw;
    gchar *msg, *c, *pband, *operator_;
    
    /*dbg("sw_talk_enter(%p,'%s')\n",enterdata,str);*/
    
    sw = (struct subwin *)enterdata;
    
    if (aband){
        pband = g_strdup(aband->pband);
        c = strchr(pband, ' ');
        if (c) *c='\0';
        operator_=aband->operator_;
    }else{
        pband="";
        operator_=cfg->pcall;
    }
    
    msg=g_strdup_printf("%6s@%-3s: %s", operator_, pband, str);
    c=g_strdup_printf("TA %s\n", msg);
    rel_write_all(c);
    g_free(c);
    //fifo_addf(sw->fifo,"%6s: %s", operator_, str);
    fifo_addf(sw->fifo, "%6s@%-3s: %s", operator_, pband, str);
    g_free(msg);
}

void sw_talk_read(char *data, int ignoredupe){
    struct subwin *sw;
    int i, unread=0;
   /* dbg("sw_talk_read('%s')\n", data);*/

    for (i=0; i<gses->subwins->len; i++){
        sw = (struct subwin*)g_ptr_array_index(gses->subwins, i);
        if (sw->type == SWT_TALK){
			if (ignoredupe)
			{
				if (fifo_contains(sw->fifo, data)) continue;	
			}

            fifo_adds(sw->fifo, data);
            log_adds(data);
            /* no sw_check_len ! */
            if (sw->ontop) unread=1;
            if (!sw->ontop) sw->unread=1;
        }
    }
    if (unread) redraw_later();
    return;

}


/******************* UNFINISHED ****************************/

int sw_unfi_kbd_func(struct subwin *sw, struct event *ev, int fw){
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);

    if (!aband) return 0;
    sw->fifo = aband->unfi;
    return sw_fifo_kbd_func(sw, ev, fw);
}

int sw_unfi_mouse_func(struct subwin *sw, struct event *ev, int fw){
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);

    if (!aband) return 0;
    sw->fifo = aband->unfi;
    return sw_fifo_mouse_func(sw, ev, fw);
}

void sw_unfi_redraw(struct subwin *sw, struct band *band, int flags){
    if (!aband) return;
    sw->fifo = aband->unfi;
    sw_fifo_redraw(sw, band, flags);
}

void sw_unfi_check_bounds(struct subwin *sw){
    if (!aband) return;
    sw->fifo = aband->unfi;
    sw_fifo_check_bounds(sw);
}


/******************* STATICTICS ****************************/

int sw_stat_kbd_func(struct subwin *sw, struct event *ev, int fw){
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);

    if (!aband) return 0;
    sw->fifo = aband->statsfifo1;
    return sw_fifo_kbd_func(sw, ev, fw);
}

int sw_stat_mouse_func(struct subwin *sw, struct event *ev, int fw){
	if (zselect_profiling(zsel)) zselect_hint(zsel, __FUNCTION__);

    if (!aband) return 0;
    sw->fifo = aband->statsfifo1;
    return sw_fifo_mouse_func(sw, ev, fw);
}

void sw_stat_redraw(struct subwin *sw, struct band *band, int flags){
    if (!aband) return;
    sw->fifo = aband->statsfifo1;
    sw_fifo_redraw(sw, band, flags);
}

void sw_stat_check_bounds(struct subwin *sw){
    if (!aband) return;
    sw->fifo = aband->statsfifo1;
    sw_fifo_check_bounds(sw);
}

void sw_stat_raise(struct subwin *sw){
    /*dbg("sw_stat_raise\n"); */
    if (!aband) return;
    recalc_statsfifo(aband);
}


void clip_char(struct subwin *sw, int xrel, int yrel, unsigned c){
    if (xrel < 0) return;
    if (yrel < 0) return;
    if (xrel >= sw->ww) return;
    if (yrel >= sw->hh) return;
    set_char(sw->x + xrel, sw->y + yrel, c);
}


void clip_color(struct subwin *sw, int xrel, int yrel, unsigned c){
    if (xrel < 0) return;
    if (yrel < 0) return;
    if (xrel >= sw->w) return;
    if (yrel >= sw->h) return;
    set_color(sw->x + xrel, sw->y + yrel, c);
}

void clip_printf(struct subwin *sw, int xrel, int yrel, short color, char *fmt, ...){
    va_list l;
      gchar *c, *s;
//     dbg("clip_printf('%s')\n", fmt);
    va_start(l, fmt);
    s=g_strdup_vprintf(fmt, l);
    va_end(l);
    for (c=s; *c!='\0'; c++){
        clip_char(sw, xrel++, yrel, (unsigned char)*c | color);
    }

    g_free(s);
}

void sw_export_lines(struct subwin *sw, char *fname){
	int i;
	char *filename = NULL;
	FILE *f;

	
	for (i = 0; 1; i++) {
		struct stat st;
		
		filename = g_strdup_printf("%s/%s%d.txt", ctest ? ctest->directory : tucnak_dir, fname, i);
		z_wokna(filename);
		if (stat(filename, &st) != 0)
			break;

		g_free(filename);
	}

	
	f = fopen(filename, "wt");
	if (f){
		for (i = 0; i < sw->lines->len; i++){
			char *line = (char *)g_ptr_array_index(sw->lines, i);
			fprintf(f, "%s\n", line);
		}
		fclose(f);
		log_addf(VTEXT(T_SAVED_S), filename);
	}else{
		log_addf("Error");
	}
	g_free(filename);
}

int sw_shake_tmo = -1;
void sw_shake(struct subwin *sw, int down){
#if defined(Z_HAVE_SDL) && !defined(Z_MACOS)
	SDL_Surface *old = NULL;
	SDL_Rect src, dst, fill;
    int len;
	int h;

    if (!sdl || !zsdl) return;
	if (sw->w <= 0 || sw->h <= 0) return;
	if (sw->shaking) return;
   
	h = zsdl->font_h / 4;
    if (sw_shake_tmo != -1){
        if (!ztimeout_occured(sw_shake_tmo)) {
//            sw_shake_tmo = ztimeout_init(300); 
            return;
        }
    }
	sw->shaking = 1;
	redraw_terminal(NULL);
	sdl_redraw_screen();
    sw_shake_tmo = ztimeout_init(500); 

    MUTEX_LOCK(sdl->eventpipestate);
    len = sdl->eventpipestate;
    MUTEX_UNLOCK(sdl->eventpipestate);

    if (len > 0) goto x;
    //log_addf("eventpipe=%d  %d", len, len / sizeof(struct event));
	//if (!sw->screen) return;

	src.x = sw->x * FONT_W;
	src.y = sw->y * FONT_H;
	src.w = sw->w * FONT_W;
	src.h = sw->h * FONT_H;
	old = SDL_CreateRGBSurface(SDL_SWSURFACE, src.w, src.h, sdl->bpp, 
            sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
	if (old == NULL) goto x;
	SDL_BlitSurface(sdl->screen, &src, old, NULL);

	if (down){
		dst.x = src.x;
		dst.y = src.y + h;
		dst.w = src.w;
		dst.h = src.h - h;

		fill.x = src.x;
		fill.y = src.y;
		fill.w = src.w;
		fill.h = h;

		src.x = 0;
		src.y = 0;
		src.h -= h;
		SDL_BlitSurface(old, &src, sdl->screen, &dst);
		SDL_FillRect(sdl->screen, &fill, z_makecol(0, 0, 0));
	}
	else{
		dst.x = src.x;
		dst.y = src.y;
		dst.w = src.w;
		dst.h = src.h - h;

		fill.x = src.x;
		fill.y = src.y + src.h - h;
		fill.w = src.w;
		fill.h = h;

		src.x = 0;
		src.y = 0;
		src.y += h;
		src.h -= h;
		SDL_BlitSurface(old, &src, sdl->screen, &dst);
		SDL_FillRect(sdl->screen, &fill, z_makecol(0, 0, 0));
	}
	
	SDL_UpdateRect(sdl->screen, 0, 0, 0, 0);
	usleep(100000);

	dst.x = sw->x * FONT_W;
	dst.y = sw->y * FONT_H;
	dst.w = sw->w * FONT_W;
	dst.h = sw->h * FONT_H;
	SDL_BlitSurface(old, NULL, sdl->screen, &dst);
	SDL_UpdateRect(sdl->screen, dst.x, dst.y, dst.w, dst.h);
x:;
    if (old != NULL) SDL_FreeSurface(old);
	sw->shaking = 0;
#endif
}


int sw_accel_dy(struct subwin *sw, struct event *ev){
#ifdef Z_HAVE_SDL
    int dragdiv;
    double v;

    int dy = (ev->my - sw->olddragy);
    //log_addf("my=%d  oldy=%d  dy=%4d  vy=%8.0f", ev->my, sw->olddragy, dy, ev->vy);
    if (dy == 0) return 0;
    
    dragdiv = 16;
    v = ev->vy;
    if (v < 0) v = -v;
    if (v < 50) dragdiv = 16;
    else if (v < 100) dragdiv = 8;
    else if (v < 300) dragdiv = 4;
    else if (v < 400) dragdiv = 2;
    else dragdiv = 1;
    //log_addf("dy=%4d  vy=%8.0f  dragdiv=%4d", dy, ev->vy, dragdiv);
    dy /= dragdiv;
    if (dy == 0) return 0;
	sw->olddragy = ev->my;
    return dy;
#else
    return 0;
#endif
}


void sw_highlight(struct subwin *sw, const char *text, const char *needle, int color, int i, int whole){
    const gchar *cc;
	char *fd, *d;
	unsigned char after;
    int rx, fx, fl, ho; 
	int needlelen = strlen(needle);
    
    if (needle == NULL || *needle == '\0') return;

	ho = sw->ho;
	if (sw->fifo) ho = sw->fifo->ho;
	
    cc = text; 
    while ((d = z_strcasestr(cc, needle)) != NULL){ 
		if (whole){
			if (d > cc){
				unsigned char before = d[-1];
				if (isalnum(before)) {
					cc = d + strlen(needle); 
					continue;
				}
			}
			after = d[strlen(needle)];
			if (isalnum(after)){
				cc = d + strlen(needle); 
				continue;
			}
		} 

        fl = MIN(sw->w - (d - text) + ho, needlelen); 
        fx = rx = (d - text) - ho; 
        fd = d; 
        if (rx < 0) { 
            fl += rx; 
            fx = 0; 
            fd = d - rx; 
        } 
        if (fl > 0) print_text(sw->x + fx, sw->y + i, fl, fd, color); 
        cc = d + strlen(needle); 
    } 
    
}

void sw_set_gdirty(enum sw_type type){
#ifdef Z_HAVE_SDL    
	if (!gses) return;
	int i;
	for (i = 0; i < gses->subwins->len; i++){
        struct subwin *sw = (struct subwin *)g_ptr_array_index(gses->subwins, i);
        if (sw->type != type) continue;
        sw->gdirty = 1;
	}
	redraw_later();
#endif    
}
