/* chanwin_cb.c -functions and stuff dealing with channel windows
 * distributed as part of _ s u l a _
 * 
 * Author: Tano Fotang, 1997
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free 
 * Software Foundation; either version 2 of the License , or (at your option) 
 * any later version.
 * see the file COPYING for more  info.
 * 
 */

#include <forms.h>
#include <sys/time.h>

#include "aux.h"
#include "sula.h"
#include "chanwin.h"
#include "etc.h"
#include "global.h"
#include "dcc.h"
#include "ignore.h"
#include "setting.h"
#include "cmd.h"
#include "lag.h"
#include "config.h"
#define BUFF	2048		/* loadin file into browser:max length of file line. shld
				 * really be 1024 ( max lenght of a browser line?) */

/*static void ClearServerChanList(Winstruct * win);*/

int     subwin_atclose(FL_FORM * form, void *obj)
{
	*((FL_OBJECT **) obj) = NULL;
	return FL_OK;
}
static int     atclose_remove(FL_FORM * form, void *data)
{
	*( (void **)data)=NULL;
	return FL_OK;
}
static void free_pups(Winstruct *win){
fl_freepup(*((int *)win->chanwin->file_menu->u_ldata));
		 fl_freepup(*((int *)win->chanwin->file_menu->u_ldata+1));
		 fl_freepup(*((int *)win->chanwin->file_menu->u_ldata+2)); 

		fl_freepup((int)win->chanwin->help_menu->u_ldata);
		fl_freepup(win->chanwin->go_menu->u_ldata);
		fl_freepup(win->chanwin->bookmark_menu->u_ldata);
		fl_freepup(win->chanwin->edit_menu->u_ldata);
		fl_freepup(win->chanwin->window_menu->u_ldata);
}
static void del_chanwin_from_list(Winstruct * win)
{
/* destroy a channel window, removing it from the list. if it's the only
 * window on a server, close connection as well. server isnt removed from
 * server list. */
	Go     *p = win->goStart;
	DCCChat *current;
	int     flags = gflags;
	gflags &= ~SHOW_TIPS;	/* xforms < 0.87.5: disenable post handler */
	fprintf(stderr,"=start:%p=", win);
	if (win->server) {
		CmdSent *p = cmdsentStart;
		int     x;

		search_winlist_byservname(win->server->servname,
					  win->server->port, &x);
		assert (x);	/* this is fatal error since win!=NULL */
		fprintf(stderr," got %d.",x);
		if (x == 1) {
			if (win->server->fd > -1)
				close_server_connection(&win->server->fd);
			win->server->state = -1;
		}
		else {
			int     err = 0;
			Channel *chan;
			chan = win->chanStart->left;
			while (chan != win->chanStart && err == 0) {
				char    buf[128];

				sprintf(buf, "PART %s\n", chan->name);
				err = sendto_server(win, buf);
				chan = chan->left;
			}
		}
		while (p != cmdsentEnd)
			if (p->win == win) {
				CmdSent *q = p->next;

				free(p->cmd);
				free(p->arg);
				if (q == cmdsentEnd)
					cmdsentEnd = p;
				else
					*p = *q;
				free(q);
			}
			else
				p = p->next;
	}
	
	fprintf(stderr," ok..");
	if (win->chanwin->minput->u_vdata) {
		fl_hide_form( ((FD_minput *)win->chanwin->minput->u_vdata)->minput);
		fl_free_form( ((FD_minput *)win->chanwin->minput->u_vdata)->minput);
	}
	if (win->chanwin->find->u_vdata) {
		fl_hide_form( ((FD_find *)win->chanwin->find->u_vdata)->find);
		fl_free_form( ((FD_find *)win->chanwin->find->u_vdata)->find);
	}
	if (win->chanwin->chanusers->u_vdata) {
		fl_hide_form( ((FD_chanusers *)win->chanwin->chanusers->u_vdata)->chanusers);
		fl_free_form( ((FD_chanusers *)win->chanwin->chanusers->u_vdata)->chanusers);
	}
	current = win->dccStart->left;
	while (current != win->dccStart) {
		remove_dccchat(current->id, NULL);
		current = current->left;
	}
	while (p != win->goEnd) {
		Go     *q = p->next;
		free(p->name);
		if (p->key)
			free(p->key);
		if (q == win->goEnd)
			win->goEnd = p;
		else
			*p = *q;
		free(q);
	}
	free(p);
	ClearWinChanList(win);
	free(win->chanStart);
	free_pups(win);
		
	
	fl_hide_form(win->chanwin->chanwin);
	fl_free_form(win->chanwin->chanwin);
	gflags = flags;
	if (win->query_nick)
		free(win->query_nick);
	if (tmp_win == win)
		tmp_win = NULL;
	winstruct[win-winstruct].chanwin=NULL;
	if(!(gflags&CTRL_WIN_SHOWN)){
	/* show control window if this was the last channel window*/
	   for(flags=0;flags<win_count;flags++)
		if(winstruct[flags].chanwin) break;
	   if(flags==win_count){
		fl_show_form(main_form->main_form, FL_PLACE_FREE, FL_FULLBORDER, 
		sula_NAME sula_VERSION ": Control");
		gflags |= CTRL_WIN_SHOWN;
	   }
	}


}

int     AddToHistory(const char *s)
{
	History *p;
	

	if (!s || !(*s))
		return -1;
	p = (History *) malloc(sizeof(History));
	if (p == NULL)
		return -2;

	p->line = strdup(s);
	p->left = histCur;
	p->right = histCur->right;
	histCur->right->left = p;
	histCur->right = p;

	return 0;
#if 0
	History *current;
	HistSize += sizeof(s);
	current = histStart->right;
	while (HistSize > MAX_HIST_SIZE && current != histStart) {
		/* remove 1 command from the top of list .. jeez tere's no top, u dummy */
		History *next;

		if (current == histStart)
			return;	/*list is empty */
		next = current->right;
		HistSize -= sizeof(current->line);
		free(current->line);
		current->left->right = next;
		next->left = current->left;
		free(current);
		current = next;
	}
	return 0;
#endif
}

void    ClearWinChanList(Winstruct * win)
{
	Channel *p, *next;

	if (!win)
		return;
	p = win->chanStart->right;
	while (p != win->chanStart) {
		User   *u = p->userStart, *v;
		next=p->right;
		while (u != p->userEnd) {
			free(u->name);
			free(u->address);
			free(u->ircname);
			v = u->next;
			if (v == p->userEnd)
				p->userEnd = u;
			else
				*u = *v;
			free(v);
		}
		free(u);
		p->left->right = p->right;
		p->right->left = p->left;
		free(p);
		p = next;
	}
	win->chanStart->left = win->chanStart->right = win->chanCur = win->chanStart;
}

extern int parse_input(int i, const char *inp, int log)
{
	char   *str, *buf = NULL;
	Winstruct *win;
	
	str = (char *) inp;
	if (!(*str))
		return 0;
	if(i<0 || i>win_count-1) return (-1);
	win=&winstruct[i];
	if(win->chanwin==NULL) return (-1);
	
	if (log && gflags & HISTORY)
		if (!AddToHistory(str))
			win->chanwin->cmd_fwd->u_ldata = (long) 0L;
	
	if (*str != cmdchar || (*str == cmdchar && *(str + 1) == cmdchar)) {
		/* either  "text" or "//text" */
		if (*str == cmdchar)
			str++;
		if (fl_get_button(win->chanwin->r_quote)) {
			char   *c = NULL, *t = NULL;

			if( !(str=clean(str))) return 0;
			if (!strncasecmp(str, "join", 4)) {	/*use by parse_server_output */
				c = nextstr(str, 2);
				if (on_channel(win->server, c)) {
					fl_addto_browser(win->chanwin->brow2, 
					PROMPT "already on channel");
					return 0;
				}
				t = "JOIN";
			}
			else if (!strncasecmp(str, "mode", 4))	/*used by RPL_CHANNELMODEIS alone */
				t = "MODE";
			else if (!strncasecmp(str, "names", 5))
				t = "NAMES";
			if (t) {
				c = nextstr(str, 2);
				if (!strncasecmp(str, "names", 5))
					addto_cmd_sent(win, t, NULL);
				else if (c && (*c == '#' || *c == '&'))
					addto_cmd_sent(win, t, c);
			}
			buf = (char *)malloc(sizeof(char)*(strlen(str) + 3));
			if (!buf){
				perror("Error"); 
				return errno;
			}
			sprintf(buf, "%s\n", str);
			sendto_server(win, buf);
			free(buf);
			return 0;
		}
		else if (fl_get_button(win->chanwin->r_dcc_chat)) {
			if (win->dccCur == win->dccStart) {
				if (gflags & BEEP_ERR) fl_ringbell(0);
				fl_addto_browser(win->chanwin->brow2, PROMPT "No DCC connection");
			}
			else {
				fmt_string = "%n%c%h%p$";
				buf = format_msg(fmt_you_dccchat, win->server ? 
				win->server->nick : Name, win->dccCur->nick,
				win->dccCur->email ? win->dccCur->email : win->dccCur->nick, str);
				if (buf)
					fl_addto_browser(win->chanwin->browser, buf);
				free(buf);
				buf = (char *)malloc(sizeof(char)*(strlen(str) + 3));
				if (!buf){
				perror("Error"); 
				return errno;
				}
				sprintf(buf, "%s\n", str);
				sendto_dcc_chat(win, win->dccCur, buf);
				free(buf);
			}
			return 0;
		}
		else
		if(fl_get_button(win->chanwin->r_echo)) {
			fl_addto_browser(win->chanwin->browser, str);
			return 0;
		}
		else {
			char   *cmd = NULL, *dest=0;

			if (fl_get_button(win->chanwin->type)) {
				cmd = (char *) fl_get_choice_text(win->chanwin->action);
				if (!cmd) {
					if (gflags & BEEP_ERR) fl_ringbell(0);
					fl_addto_browser(win->chanwin->brow2, 
					PROMPT "No cmd to send (bug?)");
					return -1;
				}
			}
			if (fl_get_button(win->chanwin->r_query))
				if (win->query_nick)
					dest = win->query_nick;
				else {
					if (gflags & BEEP_ERR) fl_ringbell(0);
					fl_addto_browser(win->chanwin->brow2, 
					PROMPT "No nick to query!");
					return 0;
				}
			else if (fl_get_button(win->chanwin->r_channel))
				if (win->chanCur == win->chanStart) {
					if (gflags & BEEP_ERR) fl_ringbell(0);
					fl_addto_browser(win->chanwin->brow2, 
					PROMPT "Not yet on any channel");
					return -1;
				}
				else
					dest = win->chanCur->name;
			if (!cmd)
				cmd = "msg";
			buf = (char *)malloc(sizeof(char)* (strlen(str) + 
			strlen(cmd) + strlen(dest) + 4));
			if (!buf){
				perror("Error");
				return errno;
			}
			sprintf(buf, "%s %s %s", cmd, dest, str);
			str = NULL;
		}
	} 
	else{
	if( !(str=clean(str))) return 0;
	}
	if (!str)
		str = buf;
	else if (!(*++str))
		return 0;
	if(*str=='!'){
		buf=malloc(sizeof(char)*(strlen(str) + 3));
		sprintf(buf,"! %s", ++str);
		str=strdup(buf);
		free(buf);
	}
	if (new_process_cmd(str, i)) {
		buf = (char *)malloc(sizeof(char)*(strlen(str) + 32+strlen(PROMPT)));
		if (!buf) 
			return 0;
		sprintf(buf, PROMPT "%s: cant find command", nextstr(str, 1));
		fl_addto_browser(win->chanwin->brow2, buf);
		if (gflags & BEEP_ERR) fl_ringbell(0);
		free(buf);
	}
	return 0;
}



/* 
 *    CALLBACKS
 */
void    radio_cb(FL_OBJECT * ob, long data)
{
	Winstruct *win = &winstruct[ (int)((FD_chanwin *) ob->form->fdui)->bar->u_ldata];
	switch(data){
	case 0:
		if (!win->server || win->server->fd<0)
		fl_set_button(win->chanwin->r_echo, 1);
		else fl_set_button(win->chanwin->r_quote, 1);
		break;
	case 1:
		if (!win->server || win->server->fd<0 || !win->query_nick)
		fl_set_button(win->chanwin->r_echo, 1);
		else fl_set_button(win->chanwin->r_query, 1);
		break;
	case 2: 
		if(win->chanCur == win->chanStart)
		fl_set_button(win->chanwin->r_echo, 1);
		else fl_set_button(win->chanwin->r_channel, 1);
		break;
	case 3: 
		if(win->dccCur == win->dccStart)
		fl_set_button(win->chanwin->r_echo, 1);
		else fl_set_button(win->chanwin->r_dcc_chat, 1);
		break;
	case 4:
		break;
	}
	UpdateStatus(win);
}

void    join_cb(FL_OBJECT * ob, long data)
{
/* join channels */
	Winstruct *win = &winstruct[ (int)((FD_chanwin *) ob->form->fdui)->bar->u_ldata];
	char *buf=NULL;
	if (!win->server){
		fl_addto_browser(win->chanwin->brow2, PROMPT "No server connection");
		return;
	}
	switch (fl_mouse_button()) {
	    case FL_LEFTMOUSE:
		    jjoin(win);
		    return;

	    case FL_MIDDLEMOUSE:
		    if (win->lastPart->name) {
			    buf = (char *)malloc(sizeof(char)*(strlen(
			    win->lastPart->name)+ (win->lastPart->key ? 
			    strlen(win->lastPart->key):0)+10));
			    sprintf(buf, "JOIN %s %s", win->lastPart->name,
			      win->lastPart->key ? win->lastPart->key : "");
		    }
		    break;
	    default:
		    if (win->server->lastInvite) {
			    buf = (char *)malloc(sizeof(char)*(strlen(
			    win->server->lastInvite)+8));
			    sprintf(buf, "JOIN %s", win->server->lastInvite);
			    
		    }
		    break;
	}
	if(buf){
	       process_cmd(buf, win);
	       free(buf);
	}
}

void    jjoin(Winstruct * win)
{
	char   *buf = NULL;
	char   *argv[10];
	int i=-1, j=0;
	static char already_open = 0;

	if (already_open || !win->server)
		return;
	else {
		char    *s=(char *) malloc(sizeof(char) *(56+strlen(win->server->servname)));

		already_open = 1;
		sprintf(s, "Type channel names below, seperated by blanks."
			);
		buf = getstr2(s, sula_NAME sula_VERSION ": Join channels", "", 0);
		free(s);
	}
	buf = clean(buf);
	if (!buf)
		goto out;
	buf = strtok(buf, ", ");
#define MAX_JOIN 10
	while (++i < MAX_JOIN && buf) {
		argv[i] = (char *)malloc(sizeof(char)*(strlen(buf) + 8));
		if (!argv[i])
			goto out;
		if (*buf != '#' && *buf != '&')
			sprintf(argv[i], "join #%s", buf);
		else
			sprintf(argv[i], "join %s", buf);
		buf = strtok(NULL, ", ");
		/*process_cmd( argv[i], win)      doesnt work: buf becomes NULL! */
	}
	while (j < i && !process_cmd(argv[j], win)) free(argv[j++]);
      out:
	free(buf);
	already_open = 0;

}
void    PartChannel_cb(FL_OBJECT * ob, long data)
{
	Winstruct *win = &winstruct[ (int)((FD_chanwin *) ob->form->fdui)->bar->u_ldata];
	if (!win->server)
		return;
	if (win->server->fd < 0) {
		if (gflags & BEEP_ERR) fl_ringbell(0);
		return;
	}
	switch (fl_mouse_button()) {
	    case FL_LEFTMOUSE:
		    if (win->chanCur == win->chanStart) {
			    if (gflags & BEEP_ERR) fl_ringbell(0);
			    return;
		    }
		    else {
			    char *buf = (char *)malloc(sizeof(char)*(strlen(
			    win->chanCur->name)+8));

			    sprintf(buf, "part %s", win->chanCur->name);
			    process_cmd(buf, win);
			    free(buf);
		    }
		    break;
	    case FL_MIDDLEMOUSE:{
			    Channel *current;
			    char    buf[128];

			    current = win->chanStart->right;
			    while (current != win->chanStart) {
				    sprintf(buf, "part %s", current->name);
				    process_cmd(buf, win);
				    current = current->right;
				    delay(1000);
			    }
		    }
		    break;
	    default:
		    if (win->lastJoin->name) {
			    char *buf = (char *)malloc(sizeof(char)*(strlen(
			    win->lastJoin->name)+8));
			    sprintf(buf, "part %s", win->lastJoin->name);
			    process_cmd(buf, win);
			    free(buf);
		    }
		    else {
			    if (gflags & BEEP_ERR) fl_ringbell(0);
			    return;
		    }
		    break;
	}
}
void    dcc_cb(FL_OBJECT * ob, long data)
{
	
	Winstruct *win = &winstruct[ (int)((FD_chanwin *) ob->form->fdui)->bar->u_ldata];
	long m=fl_mouse_button();
	switch (m) {
	    case FL_LEFTMOUSE:	/*accept last chat request in current window */
	    case FL_MIDDLEMOUSE: /*accept last chat request using a new chat window */
		    if (chatreqStart == chatreqEnd)
			    break;
		    else {
			    char    buf[16];

			    sprintf(buf, "achat %d", chatreqStart->id);
			    process_cmd(buf, m==FL_MIDDLEMOUSE?NULL:win);
		    }
		    break;

	    default:		/*close current char */
		    if (win->dccCur == win->dccStart)
			    return;
		    else {
			    char *buf = (char *)malloc(sizeof(char)*(strlen(win->dccCur->nick)+
			    (win->dccCur->email ? strlen(win->dccCur->email) : 1)+64));
			    sprintf(buf, "Closing DCC Chat connection to %s (%s)",
				    win->dccCur->nick, win->dccCur->email ? win->dccCur->email : "?");
			    fit_object_label(win->chanwin->WinStatusLine, buf);
			    free(buf);
			    if (!remove_dccchat(win->dccCur->id, NULL)) {
				    fl_addto_browser(win->chanwin->brow2, 
				    PROMPT "Error closing dcc chat");
				    return;
			    }
			    UpdateStatus(win);
		    }
		    return;
	}

}

static void minput_send(FL_OBJECT * ob)
{
	FD_minput *form = (FD_minput *) ob->form->fdui;
	Winstruct *win = (Winstruct *) ob->u_vdata;
	char   *buf, *ptr;

	buf = (char *) fl_get_input(form->mline);
	if (!(*buf))
		return;
	if (fl_get_button(win->chanwin->r_quote)) {
		int     errur = 0;

		if (!(gflags & QUOTE_ML)) {	/* dont automatically quote to server:flooding! */
			if (gflags & BEEP_ERR) fl_ringbell(0);
			fl_addto_browser(win->chanwin->brow2, 
			PROMPT "quote to server? set quote_ml to on");
			return;
		}
		if (!win->server)
			return;
		ptr = strtok(buf, "\n");
		while (ptr && !errur) {
			char    *buff=(char *)malloc(sizeof(char)*(strlen(ptr)+3));

			sprintf(buff, "%s\n", ptr);
			errur = sendto_server(win, buff);
			free(buff);
			ptr = strtok(NULL, "\n");
		}
		return;
	}
	else {
		char   *buf2=NULL;
		int     notice = fl_get_button(form->r_notice);

		if (fl_get_button(win->chanwin->r_dcc_chat)) {
			if (win->dccCur == win->dccStart) {
				if (gflags & BEEP_ERR) fl_ringbell(0);
				fl_addto_browser(win->chanwin->brow2, PROMPT "No DCC conection!");
				return;
			}
			fmt_string = "%n%c%h%p$";
			buf2 = format_msg(fmt_you_dccchat,
					  win->server ? win->server->nick : Name, win->dccCur->nick,
					  win->dccCur->email ? win->dccCur->email : win->dccCur->nick, buf);
			if (buf2)
				fl_addto_browser(win->chanwin->browser, buf2);
		}
		else if (fl_get_button(win->chanwin->r_query))
			if (win->query_nick) {
				fmt_string = "%n%c%p$";
				buf2 = format_msg(notice ? fmt_you_priv_notice : fmt_you_priv_msg,
				   win->server->nick, win->query_nick, buf);
				if (buf2)
					fl_addto_browser(win->chanwin->browser, buf2);
			}
			else {
				if (gflags & BEEP_ERR) fl_ringbell(0);
				fl_addto_browser(win->chanwin->brow2, PROMPT "No nick to query!");
				return;
			}
		else if (fl_get_button(win->chanwin->r_channel)) {
			if (win->chanCur == win->chanStart) {
				if (gflags & BEEP_ERR) fl_ringbell(0);
				fl_addto_browser(win->chanwin->brow2, PROMPT "No on any channel");
				return;
			}
			fmt_string = "%n%c%p$";
			buf2 = format_msg(notice ? fmt_you_pub_notice : fmt_you_pub_msg,
				win->server->nick, win->chanCur->name,buf);
			if (buf2)
				fl_addto_browser(win->chanwin->browser, buf2);

		}else{
		/* echo */
		 	fl_addto_browser(win->chanwin->browser, buf);
			return;
		}
		free(buf2);
		buf2=NULL;
		/* send msg now */
		quote_str(&buf);
		if (fl_get_button(win->chanwin->r_query))
			if (win->query_nick) {
				buf2 = (char *) malloc(sizeof(char) * (strlen(buf) +
				strlen( win->query_nick) + 16));
				sprintf(buf2, "%s %s :%s\n", notice ? "NOTICE" : "PRIVMSG", win->query_nick, buf);
				sendto_server(win, buf2);
			}
			else {
				fl_addto_browser(win->chanwin->brow2, PROMPT "No nick for query!");
				return;
			}
		else if (fl_get_button(win->chanwin->r_channel)) {
			buf2 = (char *) malloc(sizeof(char) * (strlen(buf) +
				strlen( win->chanCur->name) + 16));
			sprintf(buf2, "%s %s :%s\n", notice ? "NOTICE" : "PRIVMSG", win->chanCur->name, buf);
			sendto_server(win, buf2);
		}
		else {

			if (fl_get_button(win->chanwin->r_dcc_chat)) {
				buf2 = (char *) malloc(sizeof(char) * (strlen(buf) +2));
				sprintf(buf2, "%s\n", buf);
				sendto_dcc_chat(win, win->dccCur, buf2);
			}
			else {
				fl_addto_browser(win->chanwin->brow2, PROMPT "(bug?)Send msg where??");
				return;
			}
		}
		free(buf2);
	}
}
void    minput_cb(FL_OBJECT * ob, long data)
{

	if (ob->u_vdata) {
		fl_raise_form(((FD_minput *)ob->u_vdata)->minput);
		return;
	} else{
	Winstruct *win = &winstruct[ (int)((FD_chanwin *) ob->form->fdui)->bar->u_ldata];
	FD_minput *form= create_form_minput();
	ob->u_vdata=(void *)form;
	fl_set_form_atclose(form->minput, atclose_remove, (void *) &ob->u_vdata);
	fl_set_object_prehandler(form->mline, set_cursor);
	form->mline->u_ldata = (long) XC_xterm;
	form->send->u_vdata = (void *) win;
	form->close->u_vdata = (void *) &ob->u_vdata;
	form->clear->u_vdata = (void *) form->mline;
	fl_set_form_minsize(form->minput, 385, 185);
	fl_show_form(form->minput, FL_PLACE_MOUSE | FL_FREE_SIZE,
		FL_TRANSIENT, sula_NAME sula_VERSION ": Multiline message");
	fl_do_forms();
	}

}

void    minput_cb_cb(FL_OBJECT * ob, long data)
{
	switch (data) {
	    case 0:		/*send */
		    minput_send(ob);
		    break;
	    case 1:		/* clear */
		    fl_set_input((FL_OBJECT *) ob->u_vdata, "");
		    break;

	    case 2:{		/*dismiss */
			    FD_minput *form = (FD_minput *) ob->form->fdui;

			    fl_hide_form(form->minput);
			    fl_free_form(form->minput);
			    *( (void **)ob->u_vdata) = NULL;
			    break;
		    }
	    default:
		    break;
	}

}

void    menu_cb(FL_OBJECT * ob, long data)
{
	static void do_file_menu(Winstruct *);
	static void do_edit_menu(Winstruct *);
	static void do_bookmark_menu(Winstruct *);
	Winstruct *win;
	
	win = &winstruct[ (int)((FD_chanwin *) ob->form->fdui)->bar->u_ldata];
	switch (data) {
	    case 0:
		    do_file_menu(win);
		    break;
	    case 1:{		/* go */
		int     item = fl_get_menu(ob);

		    if (item <= 0)
			    break;
		    else {
			    char   *chan = (char *) fl_get_menu_item_text(ob, item);
			    Go     *p = win->goStart;

			    while (p != win->goEnd)
				    if (!strcmp(p->name, chan)) {
					    char    *buf;
					    
					    #if 0
					    /* currently sigsevs*/
					    buf=(char *)malloc(sizeof(char)*(strlen(p->name)+64));
					    sprintf(buf, "join %s %s", p->name, p->key ? p->key : "");
					    #else
					    buf=(char *)malloc(sizeof(char)*(strlen(p->name)+8));
					    sprintf(buf, "join %s", p->name);
					    #endif
					    process_cmd(buf, win);
					    free(buf);
					    break;
				    }
				    else
					    p = p->next;
		    }
		    break;
		  }
	    case 2:
		    do_bookmark_menu(win);
		    break;
	    case 3:
		    {
			    static void do_ignore_win(Winstruct *);
			    static void do_dcc_win(Winstruct *);
			    static void do_bookmark_win(Winstruct *);
			    int     item = fl_get_menu(ob);

			    if (item <= 0)
				    break;
			    if (item == 1)
				    do_dcc_win(win);
			    else if (item == 2)
				    do_ignore_win(win);
			    else if (item == 3)
				    fl_trigger_object(win->chanwin->chanusers);
			    else if (item == 4)
				    do_bookmark_win(win);
			    else
				    if (gflags & BEEP_ERR) fl_ringbell(0);
			    break;
		    }
	    case 4:
		    {
			    int     item = fl_get_menu(ob);

			    if (item <= 0)
				    return;
			    if (item == 1)
				    do_about();
			    else if (item == 2)
				    do_help();
			    else
				    if (gflags & BEEP_ERR) fl_ringbell(0);
		    }
		    break;
	    case 5:		/*edit */
		    {
			    do_edit_menu(win);
			    break;
		    }
	    default:
		    break;
	}			/* switch */
}
static void find_text(FL_OBJECT * ob)
{
	FD_find *form = (FD_find *) ob->form->fdui;
	Winstruct *win = (Winstruct *) ob->u_vdata;
	int     total, *i = (int *) ob->u_ldata;
	char   *text;
	int     backwards = 0;
	static int previous = 0;

	text = (char *) fl_get_input(form->input);
	if (!(*text)) {
		if (gflags & VERBOSE_CLIENT)
			fl_addto_browser(win->chanwin->brow2, PROMPT "find: search for what??");
		if (gflags & BEEP_ERR) fl_ringbell(0);
		return;
	}
#if 0
	*((char **) form->done->u_vdata) = (char *)text;
#endif
	total = fl_get_browser_maxline(win->chanwin->browser);
	if (total < 1) {
		*i = 1;
		if (gflags & VERBOSE_CLIENT)
			fl_addto_browser(win->chanwin->brow2, PROMPT "find: empty browser");
		if (gflags & BEEP_ERR) fl_ringbell(0);
		return;
	}
	backwards = fl_get_button(form->direction);
	if (!backwards && *i < 1)
		*i = 1;
	if (!fl_get_button(form->case_sense))
		text = lowercase(text);

	for (; (!backwards && *i <= total) || (backwards && *i >= 1);) {
		char   *s;

		s = (char *) fl_get_browser_line(win->chanwin->browser, *i);
		if (!s || !(*s))
			goto checkpos;
		if (!fl_get_button(form->case_sense))
			s = lowercase(s);
		if (strstr(s, text)) {
			if (previous)
				fl_deselect_browser_line(win->chanwin->browser, previous);
			fl_select_browser_line(win->chanwin->browser, *i);
			previous = *i;
			fl_set_browser_topline(win->chanwin->browser, *i);
			if (!backwards)
				(*i)++;
			else
				(*i)--;
			return;
		}
	      checkpos:
		if (backwards)
			(*i)--;
		else
			(*i)++;
	}
	if (*i > total)
		(*i)--;
	if (gflags & VERBOSE_CLIENT) {
		if (!backwards)
			fl_addto_browser(win->chanwin->brow2,
					 PROMPT "find: Reached end of document. No matching string found");
		else
			fl_addto_browser(win->chanwin->brow2,
					 PROMPT "find: Reached start of document. No matching string found");
		if (gflags & BEEP_ERR) fl_ringbell(0);
	}
}

static void find(Winstruct * win)
{
	static char *s =0;
	static int i = 1;
	FD_find *form;

	if (win->chanwin->find->u_vdata) {
		fl_raise_form(((FD_find *)win->chanwin->find->u_vdata)->find);
		return;
	}
	form=create_form_find();
	win->chanwin->find->u_vdata=(void *)form;
	fl_set_form_atclose(form->find, atclose_remove, (void *) &win->chanwin->find->u_vdata);
	fl_set_object_prehandler(form->input, set_cursor);
	form->input->u_ldata = (long) XC_xterm;
	fl_set_input(form->input, s);
	form->go->u_vdata = (void *) win;
	form->go->u_ldata = (long) &i;	/* current brow line */
	form->done->u_vdata = (void *) &s;
	form->direction->u_vdata=(void *)&win->chanwin->find->u_vdata; /* done */
	form->clear->u_vdata = (void *) form->input;
	fl_set_form_minsize(form->find, 400, 160);
	fl_set_form_maxsize(form->find, 600, 160);
	fl_show_form(form->find, FL_PLACE_MOUSE | FL_FREE_SIZE,
	      FL_TRANSIENT, sula_NAME sula_VERSION ": Find line with text");
	fl_do_forms();

}
void    find_cb(FL_OBJECT * ob, long data)
{
	switch (data) {
	    case 0:		/*send */
		    find_text(ob);
		    break;
	    case 1:		/* clear */
		    fl_set_input((FL_OBJECT *) ob->u_vdata, "");
		    break;

	    case 2:{		/*dismiss */
		FD_find *form = (FD_find *) ob->form->fdui;
		/*Winstruct *win=(Winstruct *)form->go->u_vdata;*/
		*((int *)form->go->u_ldata)=1;
		fl_hide_form(form->find);
		fl_free_form(form->find);
		*((void **)form->direction->u_vdata) = NULL;
		break;
		    }
	    default:
		    break;
	}

}

static int lost_clipboard_cb(FL_OBJECT * ob, long type)
{
	#if 0
	if (gflags & VERBOSE_CLIENT)
		fl_addto_browser(ob, "@i(lost clipboard)");
	#endif
	return 0;
}

static int get_clipboard_cb(FL_OBJECT * ob, long type, const void *data, long size)
{
	if (size == 0) {
		if (gflags & BEEP_ERR) fl_ringbell(0);
		if (gflags & VERBOSE_CLIENT)
			fl_addto_browser(ob, PROMPT "clipboard is empty");
	}
	else
		fl_addto_browser(ob, (char *) data);
	return 0;
}


static int get_send_clipboard_cb(FL_OBJECT * ob, long type, const void *data, long size)
{
	if (size == 0) {
		if (gflags & BEEP_ERR) fl_ringbell(0);
		return -1;
	}
	else {
		char   *buf = strdup((char *) data);
		int i=((FD_chanwin *) ob->form->fdui)->bar->u_ldata;
		if (!buf) {
			berr_ret("%s %d ", __FILE__, __LINE__);
			if (gflags & BEEP_ERR) fl_ringbell(0);
			return -2;
		}
		send_blocktxt(i, buf, 1); /* log it*/
		free(buf);
	}

	return 0;
}

void send_blocktxt(int win, const char *text, int flag)
{
/* send text containin newlines. almost like multiline msg except hthat msg
 * is sent line after line here 
 * flag: add to cmd log*/
	char   *ptr;
	char   *buf = (char *) text;
	if (!buf)
		return;
	ptr = strxstr(buf, "\n");	/* strtok dont work */
	if (!ptr)
		ptr = buf;
	while (ptr) {
		parse_input(win, ptr, flag);
		buf += strlen(ptr);
		if (*buf != '\n')
			break;
		else
			buf++;
		ptr = strxstr(buf, "\n");
		if (!ptr)
			ptr = buf;
	}
}
void unquote_str(char **str){
	char   *ptr;
	char *s;
	s=(char *) malloc(sizeof(char) * (strlen(*str)*2));
	if(!s) return;
	ptr=s;
	while((*ptr=*(*str)++))
		switch(*ptr){
		case 0x02:{
			char *p=ml_nl;
			*ptr++='\n'; 
			if(p) while(*p)
				if(*p=='\\' && *(p+1)=='t'){
					*ptr++='\t';
					p+=2;
				}
				else 
					*ptr++=*p++;
			break;
		}
		case 0x03: *ptr++='\t'; break;
		default: ptr++;break;
		}
	*str=strdup(s);
	free(s);

}
void quote_str(char **str){
	char   *ptr;
	char *s;
	s=(char *) malloc(sizeof(char) * (strlen(*str) + 8));
	if(!s) return;
	*s=0x02;
	*(s+1)=0x02;

	ptr=s+2;
	while((*ptr=*(*str)++))
		switch(*ptr){
		case '\n': *ptr++=0x02; break;
		case '\t': *ptr++=0x03; break;
		default: ptr++;break;
		}
	*str=strdup(s);
	free(s);

}

static void edit_browser(FL_OBJECT * browser, int cut)
{
	FD_chanwin *form = (FD_chanwin *) browser->form->fdui;
	int     total = fl_get_browser_maxline(browser);
	int     i;
	char   *buf = 0;
	size_t  ll = 0;

	fl_freeze_form(form->chanwin);
	for (i = 1; i <= total; i++)
		if (fl_isselected_browser_line(browser, i)) {
			char   *s = (char *) fl_get_browser_line(browser, i);
			buf = realloc(buf, (ll + strlen(s) + 2) * sizeof(char));
			if (!buf) {
				berr_ret("%s %d", __FILE__, __LINE__);
				return;
			}
			sprintf(buf + ll, "%s\n", s);
			ll = strlen(buf);
			if (cut) {
				fl_delete_browser_line(browser, i);
				total--;
				i--;
			}
		}
	fl_unfreeze_form(form->chanwin);
	if (buf) {
		*(buf + --ll) = '\0';	/* remove last \n */
		fl_stuff_clipboard(browser, FL_STRING, (void *) buf, ll, lost_clipboard_cb);
	}
	else {
		if (gflags & BEEP_ERR) fl_ringbell(0);
		if (gflags & VERBOSE_CLIENT)
			fl_addto_browser(form->brow2,
					 PROMPT "No block selcted");
	}
}

static void delete_lines(FL_OBJECT * browser)
{
	FD_chanwin *chanwin = (FD_chanwin *) browser->form->fdui;
	int     total = fl_get_browser_maxline(browser);
	int     i;

	fl_freeze_form(chanwin->chanwin);
	for (i = 1; i <= total; i++)
		if (fl_isselected_browser_line(browser, i)) {
			fl_delete_browser_line(browser, i);
			total--;
			i--;
		}
	fl_unfreeze_form(chanwin->chanwin);
}

static char *extract_nick(const char *str){
	size_t ll=strcspn(str, "!");
	char *s=malloc(sizeof(char)*(1+ll));
	strncpy(s,str,ll);
	*(s+ll)='\0';
	return s;
}
static char *sender2nick(const Sender *s){
	
	return (extract_nick(s->sender));
	#if 0
	char *nick;
	int len=(s->what>>16 & 0xff);
	nick=malloc(sizeof(char)*len);
	strncpy(nick,s->sender,--len);
	*(nick+len)='\0';
	#if DEBUG>2
	fprintf(stderr,"nick:%s, %s, len=%d(%ld)\n", 
	nick,s->sender,len, (s->what&0xff));
	#endif
	return nick;
	#endif
}
static void do_edit_menu(Winstruct * win)
{
	int     item = fl_get_menu(win->chanwin->edit_menu);

	if (item <= 0)
		return;
	switch (item) {
	    case 11:
		    edit_browser(win->chanwin->browser, 0);
		    break;	/* copy browser 1 */
	    case 21:
		    edit_browser(win->chanwin->brow2, 0);
		    break;
	    case 12:
		    edit_browser(win->chanwin->browser, 1);
		    break;	/* cut */
	    case 22:
		    edit_browser(win->chanwin->brow2, 1);
		    break;
	    case 13:
		    fl_request_clipboard(win->chanwin->browser,
					 FL_STRING, get_clipboard_cb);
		    break;
	    case 23:		/* paste into browser */
		    fl_request_clipboard(win->chanwin->brow2,
					 FL_STRING, get_clipboard_cb);
		    break;

	    case 14:
		    delete_lines(win->chanwin->browser);
		    break;
	    case 24:
		    delete_lines(win->chanwin->brow2);
		    break;	/*delete */
	    case 15:
	    case 25:		/* unselect all */
		    fl_freeze_form(win->chanwin->chanwin);
		    fl_deselect_browser(item < 20 ? win->chanwin->browser : win->chanwin->brow2);
		    fl_unfreeze_form(win->chanwin->chanwin);
		    break;
	    case 3:		/* paste into input line */
		    fl_request_clipboard(win->chanwin->clock, FL_STRING, get_send_clipboard_cb);
		    break;
	    default:
		    break;
	}
}

	
void    chanwin_cb(FL_OBJECT * ob, long data)
{
	int id=((FD_chanwin *) ob->form->fdui)->bar->u_ldata;
	Winstruct *win = &winstruct[id];
	switch (data) {

	    case 0:		/* sinput */
		    parse_input(id, fl_get_input(ob),
		    	fl_get_button(win->chanwin->history));
		    fl_set_input(ob, "");
		    break;

	    case 41:		/* cut */
		    switch (fl_mouse_button()) {
			case FL_MIDDLEMOUSE:
				edit_browser(win->chanwin->brow2, 1);
				break;
				break;
			default:
				edit_browser(win->chanwin->browser, 1);
				break;
				break;
		    }
		    break;

	    case 40:		/* copy */
		    switch (fl_mouse_button()) {
			case FL_MIDDLEMOUSE:
				edit_browser(win->chanwin->brow2, 0);
				break;
				break;
			default:
				edit_browser(win->chanwin->browser, 0);
				break;
				break;
		    }
		    break;

	    case 42:		/* delete */
		    switch (fl_mouse_button()) {
			case FL_MIDDLEMOUSE:
				delete_lines(win->chanwin->brow2);
				break;
				break;
			default:
				delete_lines(win->chanwin->browser);
				break;
				break;
		    }
		    break;
	    case 43:		/* paste */
		    switch (fl_mouse_button()) {
			case FL_LEFTMOUSE:
				fl_request_clipboard(win->chanwin->clock, FL_STRING, get_send_clipboard_cb);
				break;
			case FL_MIDDLEMOUSE:
				fl_request_clipboard(win->chanwin->browser, FL_STRING, get_clipboard_cb);
				break;
			default:
				fl_request_clipboard(win->chanwin->brow2, FL_STRING, get_clipboard_cb);
				break;
		    }
		    break;
	    case 4:{		/* find */
			    find(win);
			    break;
		    }
	    case 5:		/* info */
		    break;

	    case 6:{		/* away */
			    char   *msg = (char *)fl_get_input(win->chanwin->sinput);
			    char   *buf, clear_input = 0;

			    if (!(win->server->flag&SET_AWAY)) {
				    if (!(*msg))
					    msg = away_text;
			    }
			    else if (!(*msg))
				    msg = NULL;
			    if (!msg)
				    buf = strdup("away");
			    else {
				    buf = (char *)malloc(sizeof(char)* (strlen(msg) + 8));
				    sprintf(buf, "away %s", msg);
				    clear_input = 1;
			    }
			    if (process_cmd(buf, win))
				    fl_addto_browser(win->chanwin->brow2, PROMPT "away failed!");
			    free(buf);
			    if (clear_input)
				    fl_set_input(win->chanwin->sinput, "");
			    break;
		    }
	    case 7:		/* (dis)connect */
		    if (!win->server) {
			    fl_addto_browser(win->chanwin->brow2,
					     PROMPT "Window has no server. Use /server to connect");
			    if (gflags & BEEP_ERR) fl_ringbell(0);
		    }
		    else if (win->server->fd < 0)
			    connect_to_server(win->server, win,NULL,0);
		    else
			    process_cmd("quit", win);
		    break;

	    case 8:		/* topic */
		    if (win->chanCur == win->chanStart) {
			    if (gflags & BEEP_ERR) fl_ringbell(0);
			    return;
		    }
		    else {
			    char   *cmd = NULL;
			    long    m = fl_mouse_button();

			    if (m == FL_MIDDLEMOUSE)
				    cmd = strdup("topic -clear");
			    else if (m == FL_RIGHTMOUSE)
				    cmd = strdup("topic");
			    else {
				    char   *str = (char *) fl_get_input(win->chanwin->sinput);

				    if (str)
					    cmd = (char *) malloc(sizeof(char) * (
					    strlen(str) + strlen(win->chanCur->name) + 10));
				    else
					    cmd = (char *) malloc(sizeof(char) * (strlen(win->chanCur->name) + 10));
				    sprintf(cmd, "topic %s %s", win->chanCur->name, str ? str : "");
			    }
			    if (cmd){
				    process_cmd(cmd, win);
				    free(cmd);
				}
			    fl_set_input(win->chanwin->sinput, "");
		    }
		    break;
	    case 9:		/*nick*/
	    	if(!win->server || win->server->fd<0){
			if (gflags & BEEP_ERR) fl_ringbell(0);
			if(gflags&VERBOSE_CLIENT)  fl_addto_browser(win->chanwin->brow2,
					     PROMPT"No server connection");
			break;
		}
		switch(fl_mouse_button()){
			case FL_LEFTMOUSE:{/* nick */
				char *s=nextstr(fl_get_input(win->chanwin->sinput),1);
				if(!s){
				      goto erred;	
				}else{
				char *buf= (char *) malloc(sizeof(char) * (strlen(s)+8));
				sprintf(buf,"nick %s",s);
				process_cmd(buf, win);
				fl_set_input(win->chanwin->sinput, "");
				free(buf);
				}
			}
			return;
			case FL_MIDDLEMOUSE:{	/* user mode*/
				char *s=(char *)fl_get_input(win->chanwin->sinput);
				char *buf;
				if(!(*s)) goto erred;
				buf = (char *) malloc(sizeof(char) * (strlen(s)+8)+
					strlen(win->server->nick));
				sprintf(buf,"mode %s %s",win->server->nick, s);
				process_cmd(buf, win);
				fl_set_input(win->chanwin->sinput, "");
				free(buf);
				return;
			}
			default:{	/* chan mode*/
				if(win->chanStart==win->chanCur){
				if (gflags & BEEP_ERR) fl_ringbell(0);
				if(gflags&VERBOSE_CLIENT)  fl_addto_browser(win->chanwin->brow2,
					     PROMPT"change mode: no channel");
				}else{
				 char *s=(char *)fl_get_input(win->chanwin->sinput);
				 char *buf;
				 if(!(*s)) goto erred;
				 buf = (char *) malloc(sizeof(char) * (strlen(s)+8)+strlen(win->chanCur->name));
				sprintf(buf,"mode %s %s",win->chanCur->name, s);
				process_cmd(buf, win);
				fl_set_input(win->chanwin->sinput, "");
				free(buf);
				}
				return;
			}
		}
		erred:
			if (gflags & BEEP_ERR) fl_ringbell(0);
			if(gflags&VERBOSE_CLIENT) 
				fl_addto_browser(win->chanwin->brow2,
				    PROMPT"arguments needed in command line");
			return;
	    case 11:		/* lag */
		    #ifndef NOLAG
		    if(!win->server || win->server->fd<0){
		    	fl_set_object_label(win->chanwin->WinStatusLine,
				    "You are not connected to a server");
		   }else  send_lag_junk(win->server,1,1);
		   #endif
		    break;
	    case 10:		/* go! */
	    case 12:{		/* do it! */
			    char   *target;
			    char   *cmd, *buf=NULL;
			    char history=0;

			    target = (char *) fl_get_choice_text(win->chanwin->target);
			    if (!target || !(*target)) {
				    if (gflags & BEEP_ERR) fl_ringbell(0);
				    fl_addto_browser(win->chanwin->brow2, PROMPT "No target!");
				    return;
			    }
			    cmd = (char *) fl_get_choice_text(win->chanwin->operation);
			    if (!cmd || !(*cmd)) {
				    if (gflags &BEEP_ERR)  fl_ringbell(0);
				    fl_addto_browser(win->chanwin->brow2, PROMPT "No operation?");
				    return;
			    }
			    history=(char)fl_get_button(win->chanwin->history);
			    target=extract_nick(target);
			    if (data == 12) {	/* we dont need a msg */
				    buf = (char *) malloc(sizeof(char) * (strlen(cmd) + strlen(target) + 5));
				    if (history)
				    sprintf(buf, "%c%s %s", cmdchar, cmd, target);
				    else
				    sprintf(buf, "%s %s", cmd, target);
			    }
			    else {
				    char   *msg = (char *) fl_get_input(win->chanwin->sinput);

				    if (!msg || *msg==0)
					    return;
				    buf = malloc(sizeof(char)* (strlen(msg) + strlen(cmd) + 
				    strlen(target) + 5));
				    if (history) 
					    sprintf(buf, "%c%s %s %s", cmdchar, cmd, target, msg);
				    else
					    sprintf(buf, "%s %s %s", cmd, target, msg);
				    fl_set_input(win->chanwin->sinput, "");
			    }
			    if (history)
			    	if (!AddToHistory(buf))
					win->chanwin->cmd_fwd->u_ldata = (long) 0L;

			    if (process_cmd(history?buf+1:buf, win)) {
				    free(buf);
				    buf = (char *)malloc(sizeof(char)*(strlen(cmd) + 32));
				    sprintf(buf, PROMPT "%s: cant find command", cmd);
				    fl_addto_browser(win->chanwin->brow2, buf);
			    }
			    free(target);free(buf);
			    break;
		    }		/* case 10 */
	    case 13:		/* update targets */
		    {
			Sender *s;
			if(senderStart==senderStart->left)
				{fl_ringbell(0);break;}
			fl_clear_choice(win->chanwin->target);
			s=senderStart->left;
			while(s!=senderStart){
				/*nick=sender2nick(s);*/
				fl_addto_choice(win->chanwin->target,s->sender);
				/*free(nick);*/
				s=s->left;
			}
		    }
		    break;
	    default:
		    break;

	    case 30:{		/* cmd fwd */
			    History *hist = ((History *) ob->u_ldata);

			    if (hist == NULL)
				    hist = histCur;
			    else
				    hist = hist->right;
			    ob->u_ldata = (long) hist;
			    if (hist == histStart && (hist = hist->right) == histStart)
				    return;	/*empty list */
			    fl_set_input(win->chanwin->sinput, hist->line);
		    }
		    break;
	    case 31:{
			    History *hist = (History *) ((FD_chanwin *) ob->form->fdui)->cmd_fwd->u_ldata;

			    if (hist == NULL)
				    hist = histCur;
			    else
				    hist = hist->left;
			    ((FD_chanwin *) ob->form->fdui)->cmd_fwd->u_ldata = (long) hist;
			    if (hist == histStart && (hist = hist->left) == histStart)
				    return;	/*empty list */
			    fl_set_input(win->chanwin->sinput, hist->line);
		    }
		    break;
	    case 32:	/* tab key ;  */
	    {
	     		Sender *s=((Sender *) ob->u_ldata);
			if (s == NULL)
				s = senderCur;
			else
			    s = s->right;
			ob->u_ldata = (long) s;
			if (s == senderStart && (s = senderStart->right) == senderStart)
				{fl_ringbell(0);break;}	/*empty list */
			else{
				char *buf, *nick=sender2nick(s);
				char *cmd = (char *) fl_get_choice_text(win->chanwin->operation);
			    	assert(cmd);
				buf=malloc(sizeof(char)*(strlen(cmd)+strlen(nick)+4));
				sprintf(buf, "%c%s %s ", cmdchar, cmd, nick);
				fl_set_input(win->chanwin->sinput, buf);
				free(nick); free(buf);
			}


	    }
		    break;
	    case 33:{		/* chan_right */
			    if (fl_get_button(win->chanwin->r_channel)) {
				    win->chanCur = win->chanCur->right;
				    if (fl_mouse_button() == FL_MIDDLEMOUSE)
					    win->dccCur = win->dccCur->right;
				    if (win->dccCur == win->dccStart)
					    win->dccCur = win->dccCur->right;
				    if (win->chanCur == win->chanStart &&
					(win->chanCur = win->chanCur->right) == win->chanStart
					&& win->dccCur == win->dccStart)
					    return;	/* empty channel list */
			    }
			    else if (fl_get_button(win->chanwin->r_dcc_chat)) {
				    win->dccCur = win->dccCur->right;
				    if (fl_mouse_button() == FL_MIDDLEMOUSE)
					    win->chanCur = win->chanCur->right;
				    if (win->chanCur == win->chanStart)
					    win->chanCur = win->chanCur->right;
				    if (win->dccCur == win->dccStart &&
					(win->dccCur = win->dccCur->right) == win->dccStart &&
					win->chanCur == win->chanStart
				       )
					    return;	/* no dcc chat connections, no channels */
			    }
			    else {
				    /* if(fl_get_button(win->chanwin->r_channel)) */
				    win->chanCur = win->chanCur->right;
				    if (win->chanCur == win->chanStart &&
					(win->chanCur = win->chanCur->right) == win->chanStart)
					    return;	/* empty channel list */
			    }
			    UpdateStatus(win);
			    break;
		    }
	    case 34:{		/* chan_left */

			    if (fl_get_button(win->chanwin->r_channel)) {
				    win->chanCur = win->chanCur->left;
				    if (fl_mouse_button() == FL_MIDDLEMOUSE)
					    win->dccCur = win->dccCur->left;
				    if (win->dccCur == win->dccStart)
					    win->dccCur = win->dccCur->left;
				    if (win->chanCur == win->chanStart &&
					(win->chanCur = win->chanCur->left) == win->chanStart &&
					win->dccCur == win->dccStart
				       )
					    return;	/* empty channel list */
			    }
			    else if (fl_get_button(win->chanwin->r_dcc_chat)) {
				    win->dccCur = win->dccCur->left;
				    if (fl_mouse_button() == FL_MIDDLEMOUSE)
					    win->chanCur = win->chanCur->left;
				    if (win->chanCur == win->chanStart)
					    win->chanCur = win->chanCur->left;
				    if (win->dccCur == win->dccStart &&
					(win->dccCur = win->dccCur->left) == win->dccStart &&
					win->chanCur == win->chanStart
				       )
					    return;	/* no dcc chat connections */
			    }
			    else {
				    /* if(fl_get_button(win->chanwin->r_channel)) */
				    win->chanCur = win->chanCur->left;
				    if (win->chanCur == win->chanStart &&
					(win->chanCur = win->chanCur->left) == win->chanStart)
					    return;	/* empty channel list */
			    }
			    UpdateStatus(win);
			    break;
		    }

	}			/* switch */
}

void    chanusers_cb(FL_OBJECT * ob, long data)
{
	
	
	Winstruct *win = &winstruct[ (int)((FD_chanwin *) ob->form->fdui)->bar->u_ldata];
	switch(fl_mouse_button()){
	
	case FL_RIGHTMOUSE: 
		if (win->chanCur != win->chanStart) {
			char    *buf=malloc(sizeof(char)*(strlen(win->chanCur->name)
			+16));

			sprintf(buf, "who -update %s", win->chanCur->name);
			process_cmd(buf, win);
			free(buf);
		}
		return;
	case FL_MIDDLEMOUSE:
		process_cmd("lu", win);
		return;
   default:{

	Channel *chan;
	FD_chanusers *form;
	if (ob->u_vdata) {
		fl_raise_form( ((FD_chanusers *)ob->u_vdata)->chanusers);
		return;
	}
	form = create_form_chanusers();
	ob->u_vdata=(void *)form;
	fl_set_form_atclose(form->chanusers,atclose_remove, (void *) &ob->u_vdata);

	form->input->u_ldata = (long) XC_xterm;
	fl_set_object_prehandler(form->input, set_cursor);
	fl_set_input_return(form->input, FL_RETURN_END);

	fl_set_object_bw(form->base, -2);
	fl_set_object_bw(form->bar, -2);
	fl_set_object_bw(form->button_grp, -2);
	form->quit->u_vdata = (void *) &ob->u_vdata;
	form->update->u_vdata = (void *) win;
	form->channel->u_vdata = (void *) win;
	form->others->u_vdata = (void *) win;
	form->op->u_vdata = form->deop->u_vdata = (void *) win;
	form->msg->u_vdata = form->chat->u_vdata = (void *) win;
	form->do_it->u_vdata = (void *) win;
	form->query->u_vdata = (void *) win;

	fl_set_button(form->kick, 0);
	fl_set_button(form->ban, 0);
	fl_set_button(form->deop, 0);

	fl_addto_choice(form->others,
		"clientinfo|finger|ping|time|version|userinfo|chelp|whois");
	fl_set_choice(form->others, 2);
	fl_set_choice_fontsize(form->others, 12);
	/*fl_set_choice_align(form->others,FL_ALIGN_LEFT); */
	fl_set_choice_fontsize(form->channel, FL_MEDIUM_SIZE);
	fl_set_choice_align(form->channel, FL_ALIGN_LEFT);
	fl_set_choice_fontstyle(form->channel, FL_TIMESITALIC_STYLE);

	chan = win->chanStart->left;
	while (chan != win->chanStart) {
		fl_addto_choice(form->channel, chan->name);
		chan = chan->left;
	}
	fl_show_form(form->chanusers, FL_PLACE_MOUSE, FL_TRANSIENT,
		     sula_NAME sula_VERSION ": Channel users");
	if (win->chanCur != win->chanStart)
		load_userlist(win, win->chanCur->name, 0);
	fl_do_forms();
	return;
	}
  }/* switch*/

}

void    chanusers_cb_cb(FL_OBJECT * ob, long data)
{
	FD_chanusers *form = (FD_chanusers *) ob->form->fdui;

	switch (data) {
	    case 4:{		/* others */
		char   *cmd = (char *) fl_get_choice_text(form->others);
		int     total, i, no_sel = 1;

		if (!cmd || !(*cmd))
		    return;
		total = fl_get_browser_maxline(form->browser);
		    for (i = 1; i <= total; i++)
			    if (fl_isselected_browser_line(form->browser, i)) {
				    char   *nick = nextstr(fl_get_browser_line(form->browser, i), 1);
				    char   *arg=NULL;
				    no_sel = 0;
				    nick = nextstr(nick, 1);
				    nick+=2; 	/* skip over @f */
				    arg = (char *) malloc(sizeof(char) * (strlen(cmd) + strlen(nick) + 2));
				    sprintf(arg, "%s %s", cmd, nick);
				    if (process_cmd(arg, (Winstruct *) ob->u_vdata))
					    break;
				    fit_object_label(form->status, arg);
				    free(arg);
			    }
			    if (no_sel)
				    fit_object_label(form->status, "no selection?");
		    }
		    break;
	    case 7:{		/* cahnnel */
		    const char *channel = fl_get_choice_text(form->channel);

		    if (!channel)
			    return;
		    else
			    load_userlist((Winstruct *) ob->u_vdata, channel, 0);
		    }
		    break;
	    case 1:
	    case 0:{
		    const char *channel = fl_get_choice_text(form->channel);
		    int     total, i;
		    char    mode[] = "-o", no_sel = 1, *nick, *buf;
		    if (!channel || !(*channel))
			    return;
		    if (data == 0)
			    strcpy(mode, "+o");
			    total = fl_get_browser_maxline(form->browser);
			    for (i = 1; i <= total; i++)
				    if (fl_isselected_browser_line(form->browser, i)) {
					    nick = nextstr(fl_get_browser_line(form->browser, i), 1);
					    buf=malloc(sizeof(char)*(strlen(nick)+strlen(channel)+16));
					    no_sel = 0;
					    sprintf(buf, "MODE %s %s %s\n", channel, mode, nick + 2);
					    if (sendto_server((Winstruct *) ob->u_vdata, buf))
						    break;
						free(buf);
				    }
			    if (no_sel)
				    fit_object_label(form->status, "selection?");
			    else
				    fit_object_label(form->status, "op/deop sent...");
		    }
		    break;
	    case 2:{		/*query */
		    int     total, i;
		   char    no_sel = 1;
		    total = fl_get_browser_maxline(form->browser);
		    for (i = 1; i <= total; i++)
			    if (fl_isselected_browser_line(form->browser, i)) {
				    char   *nick = nextstr(fl_get_browser_line(form->browser, i), 1);
				    char   *arg;

				    no_sel = 0;
				    nick += 2;
				    arg = (char *) malloc(sizeof(char) * (strlen(nick) + 8));

					    sprintf(arg, "query %s", nick);
					    if (process_cmd(arg, (Winstruct *) ob->u_vdata))
						    fit_object_label(form->status, "errur..");
					    else
						    fit_object_label(form->status, arg);
					   free(arg);
					    break;
				    }
			    if (no_sel)
				    fit_object_label(form->status, "no selection?");
		    }
		    break;
	    case 3:{		/* chat */
	    	    char   *cmd = (char *) fl_get_choice_text(form->others);
	    	    int     total, i;
	    	    char    no_sel = 1;

	    	    if (!(*cmd))
	    		    return;
	    	    total = fl_get_browser_maxline(form->browser);
	    	    for (i = 1; i <= total; i++)
	    		    if (fl_isselected_browser_line(form->browser, i)) {
	    			    char   *nick = nextstr(fl_get_browser_line(form->browser, i), 1);
	    			    char   *arg;

	    			    no_sel = 0;
	    			    nick += 2;	/* skip @f */
	    			    arg = (char *) malloc(sizeof(char) * (strlen(nick) + 8));
	    			    sprintf(arg, "chat %s", nick);
	    			    if (process_cmd(arg, (Winstruct *) ob->u_vdata))
	    				    break;
	    				free(arg);
	     		    }
	     	    if (no_sel)
	     		    fit_object_label(form->status, "with whom?");
	     	    else
	     		    fit_object_label(form->status, "sent to cmd.proc.");
		    }
		    break;
	    case 5:{		/* update */
	     	    char   *chan = (char *) fl_get_choice_text(form->channel);
	     	    if (!chan)
	     		    return;
	     	    else {
	     		    char *buf=(char *)malloc(sizeof(char)*(strlen(chan)+16));

	     		    sprintf(buf, "WHO -update %s", chan);
	     		    process_cmd(buf, (Winstruct *) ob->u_vdata);
			    free(buf);
	     		    fit_object_label(form->status, "wait for update..");
	     	    }
		    }
		    break;
	    case 6:{		/* dismiss */
	     	    fl_hide_form(form->chanusers);
	     	    fl_free_form(form->chanusers);
		    *((void **)ob->u_vdata)=NULL;
	     	    break;
		    }
	    case 8:{		/* do it */
	     	    Winstruct *win = (Winstruct *) ob->u_vdata;
	     	    char    kick, ban, ig;
	     	    char *msg = NULL;
	     	    const char *channel = NULL;
	     	    int     total = fl_get_browser_maxline(form->browser);
	     	    int     i, no_sel = 1;

	     	    kick = fl_get_button(form->kick);
	     	    ban = fl_get_button(form->ban);
	     	    ig = fl_get_button(form->ignore);
	     	    if (!kick && !ban && !ig) {
	     		    fit_object_label(form->status, "failed. do what?");
	     		    return;
	     	    }
		    if (kick)
	     		    msg = (char *)fl_get_input(form->input);
	     	    if (ban || kick) {
	     		    channel = fl_get_choice_text(form->channel);
	     		    if (!channel) {
	     			    fit_object_label(form->status, "failed. channel?");
	     			    return;
	     		    }
	     	    }
	     	    for (i = 1; i <= total; i++)
	     		    if (fl_isselected_browser_line(form->browser, i)) {
	     			    char   *nick = nextstr(fl_get_browser_line(form->browser, i), 1);
	     			    char   *addr = nextstr(fl_get_browser_line(form->browser, i), 3);
				    char   *buf=NULL;
	     			    no_sel = 0;
	     			    if (ban) {
					    buf=(char *)malloc(sizeof(char)*(strlen(channel)+
					    strlen(addr)  +32));
	     				    sprintf(buf, "mode %s +b *!*%s", channel, 
					    *addr=='~'?addr+1:addr);
					    process_cmd(buf, win);
				    	    free(buf);
	     			    }
	     			    if (kick) {
					    buf=(char *)malloc(sizeof(char)*(strlen(channel)+
					    strlen(nick)+(msg ?strlen(msg):strlen(
					    sula_NAME sula_VERSION)) +16));
					    if(!buf) return;
					    sprintf(buf, "kick %s %s %s", channel, nick + 2, 
					    msg ? msg : sula_NAME sula_VERSION);
					    process_cmd(buf, win);
				    		free(buf);
	     			    }
	     			    if (ig) {
					    buf=(char *)malloc(sizeof(char)*(strlen(addr)+16));
	     				    sprintf(buf, "ignore *!*%s", addr);
					    process_cmd(buf, win);
				    	    free(buf);
	     			    }


	     		    }
	     	    if (no_sel)
	     		    fit_object_label(form->status, "destination?");
	     	    else
	     		    fit_object_label(form->status, "done..");

		    }
		    break;
	    case 9:{		/*msg */
	    	    char *msg = (char *)fl_get_input(form->input);
	    	    int     total, i;
	    	    char    dest[1024], *buf;
	    	    size_t  ll;
	    	    Winstruct *win;
	    	    if (!(*msg))
	    		    return;
	    	    win = (Winstruct *) ob->u_vdata;
		    ll = strlen(msg);
	    	    dest[0] = '\0';
	    	    total = fl_get_browser_maxline(form->browser);
	    	    for (i = 1; i <= total; i++)
	    		    if (fl_isselected_browser_line(form->browser, i)) {
	    			    char   *nick = nextstr(fl_get_browser_line(form->browser, i), 1);

	    			    nick += 2;
	    			    buf = (char *)malloc(sizeof(char)*(ll + strlen(nick) + 32));
	    			    strcat(dest, nick);
	    			    strcat(dest, " ");
	    			    sprintf(buf, "PRIVMSG %s :%s\n", nick, msg);
	    			    if (sendto_server(win, buf))
	    				    break;
					free(buf);
	    		    }

	    	    if (dest[0]) {
	    		    fmt_string = "%n%c%p$";
	    		    buf = format_msg(fmt_you_priv_msg, win->server->nick, dest, msg);
	    		    if (buf)
	    			    fl_addto_browser(win->chanwin->browser, buf);
	    		    fl_set_input(form->input, "");
	    		    fit_object_label(form->status, "message done");
	    	    }
	    	    else
	    		    fit_object_label(form->status, "selected?");

		    }
	    default:
		    break;
	}			/* switch */
}				/* end of function */

#define LOAD	0
#define SAVE	1
#define save_  "Save as:",sula_NAME sula_VERSION ": Save browser lines"
#define load_ "File name:",sula_NAME sula_VERSION ": Load a file"
static void save_load_browser(Winstruct * win, char what, char save_all)
{
		
	char   *ptr;

	if(what==SAVE) ptr = getstr2(save_, NULL, 1);
	else ptr = getstr2(load_, NULL, 1);
	ptr = clean(ptr);
	if (!ptr)
		return;
	else {
		char *buf=malloc(sizeof(char)*(strlen(ptr)+32));
		sprintf(buf,"window -%s 1 -f %s%s",what==LOAD?"l":"s",
		 ptr, (what==SAVE && save_all)?" -m":"");
		process_cmd(buf, win);
		free(buf);
	}
}

static void do_file_menu(Winstruct * w)
{
	static void newwin(Winstruct * w);
	static void load_hist(Winstruct *, int);
	
	/*static void load_file(Winstruct *);*/
	int     item;

	if ((item = fl_get_menu(w->chanwin->file_menu)) <= 0)
		return;
	switch (item) {
	    case 1:
		    newwin(w);
		    break;
	    case 2:
		    fl_clear_browser(w->chanwin->browser);
		    break;
	    case 3:
		    fl_clear_browser(w->chanwin->brow2);
		    break;
	    case 401:
		    save_load_browser(w,SAVE, 1);
		    break;
	    case 402:
		    save_load_browser(w,SAVE, 0);
		    break;
	    case 5:
		    save_load_browser(w, LOAD, 0);
		    break;
	    case 701:		/* clear */
		    process_cmd("history -clear", w);
		    break;
	    case 702:		/*reload */
		    process_cmd("history -reload", w);
		    break;
	    case 703:
		    load_hist(w, 1);
		    break;
	    case 704:
		    load_hist(w, 0);
		    break;
		    process_cmd("history -save", w);
		    break;
	    case 8:
		    if(w->chanwin->close_con->u_vdata){
		    /* have requested server connection. Wait for it! */
		    	fl_addto_browser(w->chanwin->brow2,PROMPT"Cannot close."
			"You requested server connection. Waiting.");
			fl_ringbell(0);
		    }else
		    if(
		    w->chanStart==w->chanCur ||
		    confirm("Leave channels and close window?",
	 		sula_NAME sula_VERSION": Confirm", 7)
			)
		    del_chanwin_from_list(w);
		    break;
	    case 9:
		    fl_trigger_object(main_form->quit);
		    break;
	    default:
		    /*fprintf(stderr, "%s (%d) error item:%d\n",
		    __FILE__, __LINE__, item);*/
		    break;
	}
}
static void load_hist(Winstruct * win, int load)
{
	char   *ptr, *buf;

	if (!(ptr = getenv("HOME")))
		ptr = ".";
	buf = (char *)malloc(sizeof(char)* (strlen(ptr) + 8 + strlen(P_HOME P_HISTORY)));
	sprintf(buf, "%s/%s/%s", ptr, P_HOME, P_HISTORY);
	ptr = getstr2("Type the name of the history file below",
		      sula_NAME sula_VERSION ": Load history file", buf, 1);
	ptr = clean(ptr);
	if (!ptr)
		free(buf);
	else {
		free(buf);
		buf = (char *)malloc(sizeof(char)* (strlen(ptr) + 24));
		if (load)
			sprintf(buf, "history -load %s", ptr);
		else
			sprintf(buf, "history -save %s", ptr);
		process_cmd(buf, win);
		free(buf);
	}
}




static void newwin(Winstruct * w)
{
/* create a channel window */
	Winstruct *win;
	char   *label=NULL;

#include "info.xpm"
#include "here.xpm"
#include "away.xpm"
#include "connected.xpm"

	win=new_win();
	if(!win) return;
	if (w->server) {
		win->server = (Server *) malloc(sizeof(Server));
		if (win->server == NULL)
		{
			err_ret("%s %d ", __FILE__, __LINE__);
			goto noserver;
		}
		win->server=w->server;
		label = (char *) malloc(sizeof(char) *
			(strlen(win->server->servname)+
			strlen(sula_NAME sula_VERSION) + 32));
		if(label)
		sprintf(label, "[%d] "sula_NAME sula_VERSION ": %s %d",
		(win-winstruct)+1, win->server->servname, win->server->port);
		if (win->server->fd > -1)
		  if(!win->server->connecting || 
		  	win->chanwin->close_con->u_vdata /*ext. process looking up hostname*/)
			{
			fl_set_pixmapbutton_data(win->chanwin->info, info_xpm);
			fl_show_object(win->chanwin->info);
			fl_free_pixmapbutton_pixmap(win->chanwin->close_con);
			fl_set_pixmapbutton_data(win->chanwin->close_con, connected_xpm);
			fl_set_pixmapbutton_data(win->chanwin->away, 
			win->server->flag&SET_AWAY ? away_xpm : here_xpm);
			fl_show_object(win->chanwin->away);

		}else{
			fl_deactivate_object(win->chanwin->close_con);
			fl_set_object_color(win->chanwin->close_con, 
				FL_INACTIVE_COL, FL_MCOL);
		}
	}
	else {
	noserver:
	      win->server=NULL;
		label = (char *) malloc(sizeof(char) * (strlen(sula_NAME sula_VERSION) + 32));
		if(label)
		sprintf(label, "[%d] "sula_NAME sula_VERSION 
			": No server connection", (win-winstruct)+1);
	}
	fl_show_form(win->chanwin->chanwin, FL_PLACE_FREE | FL_FREE_SIZE, FL_FULLBORDER, label);
	free(label);
	UpdateStatus(win);
	fl_do_forms();

}

static void file_menu_setup(FL_OBJECT * menu);
static void bookmarks_setup(FL_OBJECT * menu);
static void window_menu_setup(FL_OBJECT * menu);
static void help_menu_setup(FL_OBJECT * menu);
static void edit_menu_setup(FL_OBJECT * menu);

void    menu_setup(Winstruct * win)
{
/* called by global.c */
	edit_menu_setup(win->chanwin->edit_menu);
	window_menu_setup(win->chanwin->window_menu);
	help_menu_setup(win->chanwin->help_menu);
	bookmarks_setup(win->chanwin->bookmark_menu);
	file_menu_setup(win->chanwin->file_menu);
}


static void help_menu_setup(FL_OBJECT * menu)
{
	int p;

	if(menu->u_ldata!=-1) fl_freepup(menu->u_ldata);
retry:
	p = fl_newpup(FL_ObjWin(menu));
	if(p==-1){
		fprintf(stderr, "%s [%d] pup max increased:%d\n",
		__FILE__, __LINE__,new_pup_max);
		goto retry;
	}
	fl_setpup_shadow(p, 0);
	fl_setpup_cursor(p, XC_right_ptr);
	fl_setpup_bw(p, -1);
	fl_addtopup(p, "About sula%x1%h%l", "#A#a");
	fl_addtopup(p, "User Guide%x2%h", "#U#u");
	fl_set_menu_popup(menu, p);
	menu->u_ldata=(long)p;/* to free it when destroying channel window */
}

static void window_menu_setup(FL_OBJECT * menu)
{
	int p;
	if(menu->u_ldata!=-1) fl_freepup(menu->u_ldata);
retry:
	p = fl_newpup(FL_ObjWin(menu));
	if(p==-1){
		fprintf(stderr, "%s [%d] pup max increased:%d\n",
		__FILE__, __LINE__,new_pup_max);
		goto retry;
	}
	fl_setpup_shadow(p, 0);
	fl_setpup_cursor(p, XC_right_ptr);
	fl_setpup_bw(p, -1);
	fl_addtopup(p, "DCC window\tAlt-D%x1%h%i", "#D#d");
	fl_addtopup(p, "Ignore\t\tAlt-I%x2%h", "#I#i");
	fl_addtopup(p, "Channel users\tAlt-C%x3%h", "#C#c");
	fl_addtopup(p, "Bookmarks\tAlt-B%x4%h", "#B#b");
	fl_set_menu_popup(menu, p);
	menu->u_ldata=(long)p;/* to free pupup when destroying channel window */
}
static void edit_menu_setup(FL_OBJECT * menu)
{
	int p;
	if(menu->u_ldata!=-1) fl_freepup(menu->u_ldata);
retry:
	p = fl_newpup(FL_ObjWin(menu));
	if(p==-1){
		fprintf(stderr, "%s [%d] pup max increased:%d\n",
		__FILE__, __LINE__, new_pup_max);
		goto retry;
	}
	fl_setpup_shadow(p, 0);
	fl_setpup_cursor(p, XC_right_ptr);
	fl_setpup_bw(p, -1);
	fl_addtopup(p, "Browser 1%i");
	fl_addtopup(p, "Copy%x11%h", "Cc");
	fl_addtopup(p, "Cut%x12%h", "uU");
	fl_addtopup(p, "Paste%x13%h", "Pp");
	fl_addtopup(p, "Delete%x14%h", "Dd");
	fl_addtopup(p, "Unselect all%x15%l");
	fl_addtopup(p, "Browser 2%i");
	fl_addtopup(p, "Copy	Alt-C%x21%h", "#C#c");
	fl_addtopup(p, "Cut	Alt-U%x22%h", "#u#U");
	fl_addtopup(p, "Paste	Alt-P%x23%h", "#P#p");
	fl_addtopup(p, "Delete	Alt-D%x24%h", "#D#d");
	fl_addtopup(p, "Unselect all%x25%l");
	fl_addtopup(p, "Paste%x3%h", "aA");	/* paste into input line */
	fl_set_menu_popup(menu, p);
	menu->u_ldata=p;
}

/*----------------windows_menu-------------------------------------------------------*/

static int ignore_form_atclose(FL_FORM * form, void *obj)
{
	*((FD_ignore **) obj) = NULL;
	return FL_OK;
}

static void do_ignore_win(Winstruct * win)
{
	char    *buf;

	if (ignore_form) {
		fl_raise_form(ignore_form->ignore);
		return;
	}
	ignore_form = create_form_ignore();
	fl_set_form_atclose(ignore_form->ignore, ignore_form_atclose, (void *) &ignore_form);

	/*fl_deactivate_object(ignore_form->load); */
	buf= (char *) malloc(sizeof(char) *(66+32));
	sprintf(buf, "%-66s time left", "ignored mask");
	fit_object_label(ignore_form->title, buf);
	free(buf);
	ignore_form->done->u_vdata = (void *) &ignore_form;
	fl_show_form(ignore_form->ignore, FL_PLACE_MOUSE, FL_TRANSIENT, sula_NAME sula_VERSION ": Ignore list");
	fl_call_object_callback(ignore_form->update);
	fl_do_forms();

}

static void do_dcc_win(Winstruct * win)
{

#if 0
	static char already_open = 0;
	static int do_files = 1;

	if (already_open) {
		if (gflags & BEEP_ERR)  fl_ringbell(0);
		return;
	}
	else
		already_open = 1;
	dcc_form = create_form_dcc();
	fl_set_form_atclose(dcc_form->dcc, atclose_reset, (void *) &already_open);
	dcc_form->chat_brow->u_ldata = dcc_form->file_brow->u_ldata = (long) XC_right_ptr;
	fl_set_object_prehandler(dcc_form->file_brow, set_cursor);
	fl_set_object_prehandler(dcc_form->chat_brow, set_cursor);
	if (do_files)
		fl_set_button(dcc_form->r_file, 1);
	else
		fl_set_button(dcc_form->r_chat, 1);
	dcc_form->done->u_vdata = (void *) &already_open;
	dcc_form->r_file->u_ldata = (long) &do_files;
	dcc_form->r_chat->u_ldata = (long) &do_files;
	fl_show_form(dcc_form->dcc, FL_PLACE_MOUSE, FL_FULLBORDER, sula_NAME sula_VERSION ": DCC connections");
#define XXX_
#ifndef XXX_
	fl_hide_object(dcc_form->chat_brow);
#else

	if (do_files) {
		fl_hide_object(dcc_form->chat_brow);

		fl_show_object(dcc_form->file_brow);
		fl_set_button(dcc_form->r_file, 1);
	}
	else {
		fl_hide_object(dcc_form->file_brow);
		fl_show_object(dcc_form->chat_brow);
		fl_set_button(dcc_form->r_chat, 1);
	}
#endif
	fl_do_forms();
#endif

}
static void file_menu_setup(FL_OBJECT * menu)
{
	int     *p;
	p=malloc(sizeof(int)*3);
	if(menu->u_ldata!=-1) fl_freepup(menu->u_ldata);
 
retry2:
	p[0] = fl_newpup(FL_ObjWin(menu));
	if(p[0]==-1){
		fprintf(stderr, "%s [%d] pup max increased:%d\n",
		__FILE__, __LINE__,new_pup_max);
		goto retry2;
	}
retry3:
	p[1] = fl_newpup(FL_ObjWin(menu));
	if(p[1]==-1){
		fprintf(stderr, "%s [%d] pup max increased:%d\n",
		__FILE__, __LINE__,new_pup_max);
		goto retry3;
	}
retry:
	p[2] = fl_newpup(FL_ObjWin(menu));
	if(p[2]==-1){
		fprintf(stderr, "%s [%d] pup max increased:%d\n",
		__FILE__, __LINE__,new_pup_max);
		goto retry;
	}
	fl_setpup_shadow(p[2], 0);
	fl_setpup_cursor(p[2], XC_right_ptr);
	fl_setpup_bw(p[2], -1);
	fl_setpup_shadow(p[0], 0);
	fl_setpup_bw(p[0], -1);
	fl_setpup_shadow(p[1], 0);
	fl_setpup_bw(p[1], -1);

	fl_addtopup(p[2], "New window\tAlt-N%x1%h%l", "#N#n");

	fl_addtopup(p[2], "Clear browser1\tAlt-C%x2%h", "#C#c");
	fl_addtopup(p[2], "Clear browser2\tAlt-B%x3%h%l", "#b#B");
	fl_addtopup(p[2], "Save browser1\tAlt-S%x4%h%m", "#S#s", p[0]);
	fl_addtopup(p[0], "Everything\tAlt-E%x401%h", "#E#e");
	fl_addtopup(p[0], "Selected lines\tAlt-S%x402%h", "#S#s");
	fl_addtopup(p[2], "Load file\t\tAlt-L%x5%h%l", "#L#l");

	fl_addtopup(p[2], "History\t\tAlt-H%x7%h%m%l", "#H#h", p[1]);
	fl_addtopup(p[1], "Clear\t\tAlt-C%x701%h", "#C#c");
	fl_addtopup(p[1], "Reload\t\tAlt-R%x702%h", "#R#r");
	fl_addtopup(p[1], "Load\t\tAlt-L%x703%h", "#L#l");
	fl_addtopup(p[1], "Save\t\tAlt-S%x704%h", "#S#s");

	fl_addtopup(p[2], "Close\t\tAlt-O%x8%h", "#o#O");
	fl_addtopup(p[2], "Exit\t\tAlt-E%x9%h", "#E#e");
	fl_set_menu_popup(menu, p[2]);
	menu->u_ldata=(long)p;/* so as to free pup when destroying chnannel window l8r*/
}

/*----------------bookmark_menu-------------------------------------------------------*/

#if DEBUG>2
static void print_bm_tree(Bookmark * p, int pos)
{
	if (p) {
		print_bm_tree(p->right, pos + 8);
		printf("%*.10s %d\n", pos,
		       p->channel, p->bf);
		print_bm_tree(p->left, pos + 8);
	}
}
#endif

static int item2id(int item)
{
/* pupup menu: convert menu value to bm id */
#if 0
	/* this is a more reliable approach, but somehow slow.. */
	if (item > 29) {
		char   *i = (char *) malloc(sizeof(char) * 16);

		sprintf(i, "%d", item);
		i++;
		sscanf(i, "%d", &item);
	}
	else
		item %= 20;
#else
	if (item < 30)
		item %= 20;
	else if (item < 300)
		item %= 200;
	else if (item < 3000)
		item %= 2000;
	else
		item %= 20000;	/* 29999%20000==9999 max entries */
#endif
	return item;
}

static void do_bookmark_menu(Winstruct * win)
{
	static void *find_bookmark(int id, Bookmark * bm);

	int     item;
	Bookmark *bm;

	/*bookmarks_setup(win->chanwin->bookmark_menu); uncomment */
#if DEBUG>4
	print_bm_tree(bmTree, 8);
#endif
	if ((item = fl_get_menu(win->chanwin->bookmark_menu)) <= 0)
		return;
	if (item == 1) {	/* Add bookmark */
		int     id;

		if (win->chanCur == win->chanStart)
			return;	/* no channels */
		bm = (Bookmark *) malloc(sizeof(Bookmark));
		if (!bm) {
			err_ret("%s %d", __FILE__, __LINE__);
			return;
		}
		bm->channel = strdup(win->chanCur->name);
		if (win->chanCur->topic) {
			bm->title = strdup(win->chanCur->topic);
		}
		else
			bm->title = NULL;
		if (win->server) {
			bm->server = strdup(win->server->servname);
			bm->port = win->server->port;
		}
		else {
			bm->server = NULL;
			bm->port = DEFAULT_PORT;
		}
		bm->id=++bm_id;
		insert_bookmark(&bmTree, bm, &id);
		bookmarks_setup(win->chanwin->bookmark_menu);
		return;
	}
	item = item2id(item);
	bm = find_bookmark(item, bmTree);
	if (!bm) {
		fl_addto_browser(win->chanwin->brow2, PROMPT "bookmark bug: Please try again. /bugs for info ;)");
		return;
	}
	if ((gflags & BKMRK_IG_SERVER) || bm->server == NULL) {
		char *buf= (char *) malloc(sizeof(char) * (strlen(bm->channel)+8));
		sprintf(buf, "join %s", bm->channel);
		process_cmd(buf, win);
		free(buf);
		return;
	}
	else {
		static Winstruct *get_server_win(char *name, int port);
		Winstruct *w = get_server_win(bm->server, bm->port);
		char *buf= (char *) malloc(sizeof(char) * (strlen(bm->channel)+8));
		sprintf(buf, "join %s", bm->channel);
#if DEBUG>2
		fprintf(stderr, "%s %d server:%s %d\n", __FILE__, __LINE__, bm->server, bm->port);
#endif
		if (w && w->server->fd>-1) {
			/* already connected to server */
			process_cmd(buf, w);
			free(buf);
			return;
		}
		else {		/* connect to server */
			
			Server  *s;
		   if(w) s=w->server;
		   else{
			s=(Server *)malloc(sizeof(Server));
			if(!s) {
				err_ret("%s (%d)", __FILE__, __LINE__);
				return;
			}

			s->email = (char *)malloc(sizeof(char)* (strlen(Name) + strlen(host) + 3));
			s->ircname = (char *)malloc(sizeof(char)*(strlen(sula_NAME sula_VERSION) + 8));
			sprintf(s->email, "%s@%s", Name, host);
			s->ircname = sula_NAME sula_VERSION " user";	/* dont want to mess with pwd anymore.. */
			s->port = bm->port;
			s->servname = bm->server;
			s->fd = -1;
			s->state = -1;
			s->password = NULL;
			s->nick = Name;
			s->lag=NULL;
		     }
			add_cmdoncon(CLI_CMD, buf, s->servname, s->port);
			free(buf);
			connect_to_server(s, w?w:NULL,NULL,0);
		}
	}
}

static Winstruct *get_server_win(char *name, int port)
{
/* return first window on this server */
	int i;
	for(i=0;i<win_count;i++)
		if (winstruct[i].chanwin && winstruct[i].server && 
		!strcasecmp(name, winstruct[i].server->servname) &&
		    port == winstruct[i].server->port)
		    return &winstruct[i];
	return NULL;
}

static Bookmark *bm_copy;	/* copy of original bookmarks */

static void *find_bookmark(int id, Bookmark * bm)
{
	/*Bookmark *bm=bmTree; */
	while (bm) {
		if (id == bm->id)
			return bm;
		else if (id < bm->id)
			bm = bm->left;
		else
			bm = bm->right;
	}
	return NULL;
}

static void bookmarks_setup(FL_OBJECT * menu)
{
	int p;
	static void add_entry(int *popup, Bookmark * bm);
	if (menu->u_ldata!=-1)
		fl_freepup(menu->u_ldata);
retry:
	p = fl_newpup(FL_ObjWin(menu));
	if(p==-1){
		fprintf(stderr, "%s [%d] pup max increased:%d\n",
		__FILE__,__LINE__,new_pup_max);
		goto retry;
	}
	fl_setpup_shadow(p, 0);
	fl_setpup_cursor(p, XC_right_ptr);
	fl_setpup_bw(p, -1);
	fl_addtopup(p, "Add bookmark\tAlt-N%x1%h%l", "#A#a");
	add_entry(&p, bmTree);

	fl_set_menu_popup(menu, p);
	menu->u_ldata=p;
}

static void add_entry(int *pup, Bookmark * bm)
{

	if (bm) {
		char   *buf;
	       	add_entry(pup, bm->left);
		if (bm->title)
			buf = (char *)malloc(sizeof(char)* (strlen(bm->title) + 32));

		else
			buf = (char *)malloc(sizeof(char)* (strlen(bm->channel) + 32));
		if (!buf)
			return;
		sprintf(buf, "%d. %s%%x2%d", bm->id, bm->title ? bm->title : bm->channel, bm->id);
#if DEBUG>2
		fprintf(stderr, "%s\n", buf);
#endif
		fl_addtopup(*pup, buf);
		free(buf);

		add_entry(pup, bm->right);
	}
	return;
}

/*----------------------*/
/* window->bookmarks    */
/*----------------------*/

static void add_bm_to_brow(FL_OBJECT * brow, Bookmark * bm)
{
	if (bm) {
		char   *buf;

		add_bm_to_brow(brow, bm->left);
		if (bm->title)
			buf = (char *)malloc(sizeof(char)* (strlen(bm->title) + 32));

		else
			buf = (char *)malloc(sizeof(char)* (strlen(bm->channel) + 32));
		if (!buf) 
			return;
		sprintf(buf, "%3d. %s", bm->id, bm->title ? bm->title : bm->channel);
		fl_add_browser_line(brow, buf);
		free(buf);
		add_bm_to_brow(brow, bm->right);
	}

}

static int bm_form_atclose(FL_FORM * form, void *data)
/* don't use the value  of form. might be NULL .see further below*/
{
	FD_bookmarks **f = (FD_bookmarks **) data;

	if ((*f)->bar->u_vdata) {
		fl_hide_form((FL_FORM *) (*f)->bar->u_vdata);
		fl_free_form((FL_FORM *) (*f)->bar->u_vdata);
	}
	fl_freepup( *((int *)	 (*f)->file->u_ldata));
	fl_freepup( * ( (int *)	 (*f)->file->u_ldata +1));
	if(!form){
		/* menu File->Close */
		fl_hide_form((*f)->bookmarks);
		fl_free_form((*f)->bookmarks);
		*((FD_bookmarks **) (*f)->file->u_vdata) = NULL;
	}else  /* win manager*/
	*f = NULL;
	return FL_OK;
}

static Bookmark *copy_bm(Bookmark * bm)
{
/* make a copy of the bm tree */
	Bookmark *tmp;

	if (bm) {
		tmp = (Bookmark *) malloc(sizeof(Bookmark));
		if (!tmp)
			return NULL;
		tmp->left = copy_bm(bm->left);
		tmp->right = copy_bm(bm->right);
		tmp->channel = strdup(bm->channel);
		tmp->server = strdup2(bm->server);
		tmp->port = bm->port;
		tmp->title = strdup2(bm->title);
		tmp->id = bm->id;
		return tmp;
	}
	return NULL;
}

static int equal(Bookmark * a, Bookmark * b)
{

	return ((!a && !b) ||
		(a && b &&
		 (/*a->id == b->id &&*/ a->port == b->port &&
		 	!strcasecmp(a->channel, b->channel) &&
		   !strcasecmp(a->server, b->server)) &&
		 equal(a->left, b->left) &&
		 equal(a->right, b->right))
	   );
}

static void do_bookmark_win(Winstruct * win)
{
	int     p[2]; /* pup ID's */
	static FD_bookmarks *bm_form = NULL;
	static void item_menu_setup(FL_OBJECT * menu, int browlines, int *pupup);

	if (bm_form) {
		fl_raise_form(bm_form->bookmarks);
		return;
	}
	bm_copy = copy_bm(bmTree);
	if (!equal(bm_copy, bmTree)) {
		fprintf(stderr, "[%s %d] error copying bookmarks..Will use original!\n", __FILE__, __LINE__);
		bm_copy = bmTree;
	}
	bm_form = create_form_bookmarks();
	fl_set_form_atclose(bm_form->bookmarks, bm_form_atclose, (void *) &bm_form);
	bm_form->file->u_vdata = (void *) &bm_form;
	bm_form->file->u_ldata =(long) p;
	fl_set_browser_fontsize(bm_form->browser, FL_NORMAL_SIZE);

	fl_set_object_bw(bm_form->bar, -2);
	bm_form->bar->u_vdata = (void *) 0;	/* no sub windows */
	add_bm_to_brow(bm_form->browser, bm_copy);
	fl_set_form_minsize(bm_form->bookmarks, 250, 200);
	
	p[0] = fl_newpup(0);
	fl_setpup_shadow(p[0], 0);
	fl_setpup_cursor(p[0], XC_right_ptr);
	fl_setpup_bw(p[0], -1);
	fl_addtopup(p[0], "Open%x1%i");
	fl_addtopup(p[0], "Import%x2");
	fl_addtopup(p[0], "Save%x3%l");
	fl_addtopup(p[0], "Close%x4");
	fl_set_menu_popup(bm_form->file, p[0]);
	item_menu_setup(bm_form->item, 0, &(p[1]));

	fl_show_form(bm_form->bookmarks, FL_PLACE_FREE | FL_FREE_SIZE, FL_TRANSIENT,
		     sula_NAME sula_VERSION ": Bookmark editor");
	fl_do_forms();
}

static void do_bm_file_menu(FD_bookmarks * w)
{
	int     item = fl_get_menu(w->file);

	if (item < 1)
		return;
	switch (item) {
	    case 1:
		    break;	/* open */
	    case 2:{		/* import */
			    char   *fname;
			    Bookmark *tmp = NULL;

			    fl_set_cursor(w->bookmarks->window, XC_watch);
			    fl_deactivate_form(w->bookmarks);
			    fname = getstr2("File name:", sula_NAME sula_VERSION ": Load bookmarks", NULL, 1);
			    if (!fname){
			    	fl_activate_form(w->bookmarks);
			    	fl_reset_cursor(w->bookmarks->window);
			    	return;
			    }	
			    if (load_bookmarks(fname, item == 1 ? &tmp : &bm_copy)) {
				    char   *buf = (char *)malloc(sizeof(char)*(strlen(fname) + 128));
				    sprintf(buf, "%s\n%s", fname, strerror(errno));
				    confirm(buf, sula_NAME sula_VERSION ": Error", 0);
				    free(buf);
			    }
			    else {
				    if (item == 1)
					    bm_copy = copy_bm(tmp);
				    fl_clear_browser(w->browser);
				    add_bm_to_brow(w->browser, bm_copy);
			    }
			    fl_activate_form(w->bookmarks);
			    fl_reset_cursor(w->bookmarks->window);
			    break;
		    }
	    case 3:{		/* save */
			    char   *fname;

			    fl_set_cursor(w->bookmarks->window, XC_watch);
			    fl_deactivate_form(w->bookmarks);
			    fname = getstr2("Save as:",
					    sula_NAME sula_VERSION ": Save bookmarks", NULL, 1);
			    if (!fname){
			    	fl_activate_form(w->bookmarks);
			    	fl_reset_cursor(w->bookmarks->window);
			    	return;
			    }	
			    if (save_bookmarks(fname, bm_copy))
				    err_ret("save bookmarks:%s", fname);
			    fl_activate_form(w->bookmarks);
			    fl_reset_cursor(w->bookmarks->window);
			    break;
		    }
	    case 4:		/* close */
			    
#if 1
		if (!equal(bm_copy, bmTree) &&
			confirm("Bookmarks have changed. Use changes?",
	 		sula_NAME sula_VERSION" Confirm bookmark changes", 10))
		{
			int i;
			bmTree = copy_bm(bm_copy);
			if (!equal(bm_copy, bmTree))
			  fprintf(stderr, "error copying bookmarks\n");
			#if 1
			else
			for(i=0;i<win_count;i++)
		    	  if(winstruct[i].chanwin){
			     fl_freepup(winstruct[i].chanwin->bookmark_menu->u_ldata);
		    	     bookmarks_setup(winstruct[i].chanwin->bookmark_menu);
			 }
			#endif
		}
#endif
		bm_form_atclose(NULL, &w);
		break;
	    default:
		    break;
	}
}
static void item_menu_setup(FL_OBJECT * menu, int count, int *p)
/* count:no of bookmarks*/
{
retry:
	*p = fl_newpup(FL_ObjWin(menu));
	if(*p==-1){
		new_pup_max;
		goto retry;
	}
	fl_setpup_shadow(*p, 0);
	fl_setpup_cursor(*p, XC_right_ptr);
	fl_setpup_bw(*p, -1);
		fl_addtopup(*p, "Properties%x1");
		fl_addtopup(*p, "Go to bookmark%x2%i");
		fl_addtopup(*p, "Delete%x3");
		fl_addtopup(*p, "New%x4");
	fl_set_menu_popup(menu, *p);
}

static void do_bm_item_menu(FD_bookmarks * w)
{
	int     item, n;

	item = fl_get_menu(w->item);
	if (item < 1)
		return;
	n = fl_get_browser(w->browser);
	if (n < 1 && item != 4)
		return;

	switch (item) {
	    case 4:		/* new */
	    case 1:{		/* properties */

			    FD_edit_bm *form;
			    Bookmark *bm;
			    char    pp[8];

			    if (item != 4) {
				    int     id;
				    char   *s = (char *) fl_get_browser_line(w->browser, n);

				    if (!s || !(*s))
					    return;
				    if (sscanf(s, "%d.", &id) < 1)
					    return;
				    bm = find_bookmark(id, bm_copy);
				    if (!bm)
					    return;
			    }
			    else
				    bm = NULL;

			    fl_set_cursor(w->bookmarks->window, XC_watch);
			    fl_deactivate_form(w->bookmarks);

			    form = create_form_edit_bm();
			    w->bar->u_vdata = (void *) form->edit_bm;
			    form->channel->u_ldata = form->server->u_ldata = form->title->u_ldata =
			       form->port->u_ldata = (long) XC_xterm;
			    fl_set_object_prehandler(form->channel, set_cursor);
			    fl_set_object_prehandler(form->server, set_cursor);
			    fl_set_object_prehandler(form->title, set_cursor);
			    fl_set_object_prehandler(form->port, set_cursor);

			    if (item != 4) {
				    fl_set_input(form->channel, bm->channel);
				    fl_set_input(form->server, bm->server ? bm->server : "any");
				    if (bm->title)
					    fl_set_input(form->title, bm->title);
				    sprintf(pp, "%d", bm->port);
				    fl_set_input(form->port, pp);
				    form->ok->u_ldata = (long) &n;	/* selectedline */
				    form->add->u_vdata = (void *) bm;	/* selected bm */
			    }
			    else {
				    fl_hide_object(form->ok);
				    fl_set_input(form->server, "any");
				    sprintf(pp, "%d", DEFAULT_PORT);
				    fl_set_input(form->port, pp);
			    }
			    form->cancel->u_vdata = (void *) w;
			    fl_set_form_atclose(form->edit_bm, dont_close_win, 0);
			    fl_set_form_minsize(form->edit_bm, 410, 210);
			    fl_set_form_maxsize(form->edit_bm, 610, 210);
			    fl_show_form(form->edit_bm, FL_PLACE_FREE | FL_FREE_SIZE, FL_TRANSIENT, sula_NAME sula_VERSION ": Edit bookmark");
			    fl_do_forms();
			    break;
		    }
	    case 2:{		/* go to */
			    break;
		    }
	    case 3:{		/*delete */
			    int     id;
			    Bookmark *bm;
			    char   *s = (char *) fl_get_browser_line(w->browser, n);

			    if (!(*s))
				    return;
			    if (sscanf(s, "%d.", &id) < 1)
				    return;
			    bm = find_bookmark(id, bm_copy);
			    if (!bm)
				    return;
			    del_bookmark(&bm_copy, bm);
			    fl_freeze_form(w->bookmarks);
			    fl_clear_browser(w->browser);
			    add_bm_to_brow(w->browser, bm_copy);
			    fl_unfreeze_form(w->bookmarks);
			    break;
		    }
	    default:
		    break;
	}
}

void    edit_bm_cb(FL_OBJECT * ob, long data)
{
	FD_edit_bm *form = (FD_edit_bm *) ob->form->fdui;	/* editing form */
	FD_bookmarks *f = (FD_bookmarks *) form->cancel->u_vdata;	/* bookmark form with browser */

	switch (data) {

	    case 0:		/* ok; replace */
	    case 1:{		/*add */

			    char   *port = NULL;
			    int     id;		/* of new bookmark */
			    Bookmark *bm, *new_bm;

			    bm = (Bookmark *) malloc(sizeof(Bookmark));
			    if (!bm) {
				    err_ret("%s %d", __FILE__, __LINE__);
				    return;
			    }
			    bm->channel = nextstr(fl_get_input(form->channel), 1);
			    if (!bm->channel) {
				    confirm("NO channel name given", sula_NAME sula_VERSION ": Error", 0);
				    return;
			    }
			    bm->server = nextstr(fl_get_input(form->server), 1);
			    if (bm->server && !strcasecmp(bm->server, "any"))
				    bm->server = NULL;
			    bm->title = strdup2(fl_get_input(form->title));
			    port = nextstr(fl_get_input(form->port), 1);
			    if (!port)
				    bm->port = DEFAULT_PORT;
			    else {
				    int     i = atoi(port);
				    size_t  ll;

				    if (i < 0)
					    *port = '\0';
				    ll = strlen(port);
				    while ((i = atoi(port)) > USHRT_MAX)
					    *(port + --ll) = '\0';
				    if ((bm->port = i) == 0)
					    bm->port = DEFAULT_PORT;
			    }
			    if (!data) {	/* replace */
				    Bookmark *old = (Bookmark *) form->add->u_vdata;
				    bm->id=old->id;
				    del_bookmark(&bm_copy, old);
			    }
			    else bm->id=++bm_id;
			    insert_bookmark(&bm_copy, bm, &id);
			    new_bm = find_bookmark(bm->id, bm_copy);
			    #if 1
			    assert(new_bm);
			    #else
			    if (new_bm == NULL) {
				    confirm("Bookmark error. Might have lost 1",
				     sula_NAME sula_VERSION ": Error", 0);
				    break;	/* should quit rreally prog now */
			    }
			    #endif
			    fl_clear_browser(f->browser);
			    fl_freeze_form(f->bookmarks);
			    add_bm_to_brow(f->browser, bm_copy);
			    fl_unfreeze_form(f->bookmarks);
			    break;
		    }
	    case 2:		/* CANCEL */
		    break;
	    default:
		    break;
	}
	fl_reset_cursor(f->bookmarks->window);
	fl_activate_form(f->bookmarks);
	f->bar->u_vdata = (void *) 0;	/*we no longer exist */
	fl_hide_form(form->edit_bm);
	fl_free_form(form->edit_bm);
}
void    bkmark_cb(FL_OBJECT * ob, long data)
{
	switch (data) {
	    case 0:		/*file */
		    do_bm_file_menu((FD_bookmarks *) ob->form->fdui);
		    break;
	    case 1:		/* item */
		    do_bm_item_menu((FD_bookmarks *) ob->form->fdui);
		    break;
	    default:
		    break;
	}
}

