/* here are all options for the gp programs, stored in a static variable called
 * programs, of type programopt_s. Functions in this source file deal with
 * preparing the menu entries and creating a suitable options window if one of
 * these entries is called. Then a commandline is passed to function "execute"
 * from main.c */


#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <gtk/gtk.h>
#include "main.h"



/* structure holding vital data for the seqed functions */
typedef struct {
	opt_s *o ; /* main program options */

	GtkWidget *text ;
	GtkWidget *main_window ;
	GtkWidget *name_label ; /* label holding the name of the current sequence */
	GtkWidget *selection[2] ; /* for the selection from...to dialog */
	GtkStatusbar *status ; /* status bar */
	GtkWidget * combo ; /* the combo box with sequence names */

	sekw *sequences ;	/* linked list of edited sequences */
	sekw *current ; /* pointer to the currently edited sequence */

	int id ; /* context id of the status bar */
	int pos ; /* position in the window */
	int seqp ; /* position in the sequence */

	int nblocks ;
	int sblock ;
	int header ;
	int lblock ;
	char *top_header ;

	int modified ;
	int current_modified ;
} seqed_s ;

GtkWidget * seqed_create_combo(GtkWidget *parent, seqed_s *s) ;
void seqed_delete_handler(GtkEditable *w, gint st, gint en, gpointer data) ;


/* converts position in the text window to position in the sequence
 * returns 0 or the position of the left-most nucleotide ; *check is set to -1
 * if the cursor is in the top header, 0 if it is in the left hand header, and
 * res if it is within the sequence */
int seqed_pos_to_seqp(int pos, int *check, seqed_s *s) {
	int res = 0, nl, nb ;

	say(0, "-- seqed_pos_to_seqp: got %i", pos) ;
	*check = -1 ;
	if(pos < s->lblock + s->header) return res ; /* cursor within the top header */

	pos -= s->lblock ;
	nl = pos / s->lblock ;
	res = nl * s->sblock * s->nblocks ;
	pos -= nl * s->lblock ;

	*check = 0 ;
	if(pos < s->header) return res ;

	pos -= s->header ;
	nb = pos / (s->sblock + 1) ;
	res += nb * (s->sblock) ;
	pos -= nb * (s->sblock + 1) ;

	res += pos ;
	*check = res ;
	say(0, "-- seqed_pos_to_seqp: %i", res) ;
	return res ;
}


/* converts a sequence position to cursor position on the screen */
int seqed_seqp_to_pos(int seqp, seqed_s *s) {
	int res = 0, nl, nb, ll, tmp ;

	say(0, "-- seqed_seqp_to_pos: got %i", seqp) ;
	ll = s->nblocks * s->sblock ;
	nl = seqp / ll ; /* number of full lines */

	res += s->lblock * (nl + 1) + s->header ;
	seqp -= nl * ll ;
	if(!seqp) {
		res -= (s->header + 1) ;
		say(0, "-- seqed_seqp_to_pos %i", res) ;
		return res ;
	}

	nb = seqp / s->sblock ;
	tmp = nb * s->sblock ;
	res += nb + tmp ;
	seqp -= tmp ;
	if(!seqp) {
		res-- ;
		say(0, "-- seqed_seqp_to_pos %i", res) ;
		return res ;
	}
	
	res += seqp ;
	say(0, "-- seqed_seqp_to_pos %i", res) ;
	return res ;
}


/* frees the sequence list */
int seqed_sequences_free(sekw *s) {
	sekw *s1, *s2 ;

	s1 = s ;

	while(s1) {
		s2 = s1->next ;
		gp_seq_free(s1) ;
		s1 = s2 ;
	}

	return EXIT_SUCCESS ;
}
		

/* creates a new empty sequence */
sekw *seqed_sequence_new() {
	sekw *seq ;

	seq = g_malloc(sizeof(*seq)) ;
	seq->name = my_strdup("Unknown:") ;
	seq->next = NULL ;
	seq->sequ = NULL ;
	seq->type = UNKNOWN ;
	seq->leng = 0 ;
	return seq ;
}


/* creates the header visible on the top */
char *seqed_create_top_header(seqed_s *s) {
	char *res, *tmp ;
	int i ;
		
	res = g_malloc(s->lblock * 2) ;
	tmp = g_malloc(s->sblock * 2) ;
	sprintf(res, "%*s", s->header, "") ;

	for(i = 0 ; i < s->nblocks ; i++) {
		sprintf(tmp, "%*i ", s->sblock, s->sblock * (i + 1)) ;
		strcat(res, tmp) ;
	}

	res[s->lblock - 1] = '\n' ;
	res[s->lblock] = '\0' ;
	g_free(tmp) ;
	return res ;
}


/* filters out any nonalpha characters from the input */
int seqed_filter(char *out, char *in, int pos, int length) {
	char c ;
	int i, o ;

	if(length == -1) length = strlen(in) ;
	if(out == NULL) out = g_malloc(length + 1) ;

	for(i = 0, o = pos ; i < length ; i++) {
		c = in[i] ;

		if(isalpha(c) /* && strchr("NACTGU", toupper(c)) */ ) {
			out[o] = c ;
			o++ ;
		}
	}

	out[o] = '\0' ;
	return o ;
}


/* loads sequences from the "input" panel */
sekw * seqed_load_sequences(opt_s *o){
	FILE *fp ;
	sekw *res, *tmp, *current ;
	int i = 0 ;

	res = NULL ;
	current = NULL ;

	say(0, "----------------------------------------------- loading sequences") ;

	/* first, write the window to a file */
	if( !(fp = fopen(o->panels[0].tmpfil, "w")) ||
		print_file(fp, o, &o->panels[0]) == EXIT_FAILURE) {
		komunikat("Cannot open temporary file for writing") ;
		return NULL ;
	}

	fclose(fp) ;
	fp = fopen(o->panels[0].tmpfil, "r") ;

	/* now consecutively read the sequences from the same file */
	while( (tmp = gp_seq_read(fp)) ) {
		i++ ;
		say(0, "read sequence %s", tmp->name) ;
		if(current) {
			current->next = tmp ;
			tmp->prev = current ;
			current = tmp ;
		} else {
			res = tmp ;
			current = res ;
		}
		current->next = NULL ;
	} 

	say(0, "------------------------- loading sequences done, %i sequences read", i) ;

	fclose(fp) ;
	return res ;
}


/* the string 'in' gets formatted and returned in the malloc'ed string res */
char * seqed_format(char *in, int fmt) {
	int i, o, l ;
	char * res ;

	l = strlen(in) ;
	res = g_malloc(l + 2 * l / fmt + 2) ;

	for(i = 0, o = 0 ; i < l ; o++, i++) {
		res[o] = in[i] ;

		if( ((i + 1) % fmt) == 0) {
			o++ ;
			res[o] = '\n' ;
		}
	}

	res[o] = '\0' ;
	return res ;
}


/* the core of the seqed module: routine handling text insertion and deletion */
int seqed_insert(seqed_s *s, GtkWidget *w, char *txt, int sl, int *pos) {
	char *tmp, *num, *outxt, *intxt ;
	int max, spos, l, i, oi, li, lt, js, ks, j, k, 
		a, nblocks , header , sblock , lblock ;
	opt_s *o ;

	o = s->o ;

	s->seqp = seqed_pos_to_seqp(*pos, &a, s) ;
	say(0, "*** seqed_insert, inserting at nucl. position %i, cursor pos %i", 
		s->seqp, *pos) ;

	nblocks = s->nblocks ;
	sblock = s->sblock ;
	header = s->header ;
	lblock = s->lblock ;

	tmp = gtk_editable_get_chars(GTK_EDITABLE(w), *pos, -1) ;

	lt = strlen(tmp) ;
	l = sl + lt ; 

	intxt = g_malloc(l + 1) ;

	sl = seqed_filter(intxt, txt, 0, sl) ; /* discarding unwanted characters */
	s->seqp += sl ;
	l = seqed_filter(intxt, tmp, sl, lt) ;
	g_free(tmp) ;

	outxt = g_malloc(l + (header + 1) * l / (nblocks * sblock) + 2*l/sblock + 100) ;
	say(0, "allocated %i bytes", l+(header+1)*l/(nblocks*sblock)+2*l/sblock+100) ;

	/* if we have to print the header in the first line */
	if(*pos == 0 && lt == 0) {
		say(0, "printing header") ;
		gtk_text_insert(GTK_TEXT(w), o->czcionka, 
			&o->colors[BLUE], NULL, s->top_header, -1) ;
		*pos += lblock ;
	} else if(*pos < lblock) *pos = lblock ;

	/* position in the line */
	spos = *pos % lblock ;

	if(spos < header) { 
		js = 0 , ks = 0 ; 
		if( lt > 0) {
			*pos += (header - spos) ;
			gtk_text_set_point(GTK_TEXT(w), *pos) ;
		} 
	} else {
		js = (spos - header) / (sblock + 1) ;
		ks = (spos - header) % (sblock + 1) ;
	}

	li = 0, oi = 0 ;

	/* l is the total number of characters to insert in the text area */
	for( ; li < l ; js = 0, oi++) {
		/* drawing nblocks */
		for(j = js ; li < l && j < nblocks ; j++, oi++, ks = 0) {
			/* sblock letters in each block */
			for(k = ks ; li < l && k < sblock ; k++, li++, oi++) 
				outxt[oi] = intxt[li] ;

			/* each block ends with a space */
			if(li == l) break ;
			if(j + 1 == nblocks) oi-- ; /* last block does not end with a space */
			else outxt[oi] = ' ' ;
		}
		/* each line ends with a newline */
		if(li == l) break ;
		outxt[oi] = '\n' ;
	}

	outxt[oi] = '\0' ;

  gtk_signal_handler_block_by_func(GTK_OBJECT(s->text), 
		GTK_SIGNAL_FUNC(seqed_delete_handler), s) ;
	gtk_editable_delete_text(GTK_EDITABLE(w), *pos, -1) ;
	gtk_text_insert(GTK_TEXT(w), o->czcionka, NULL, NULL, outxt, -1) ;
	gtk_signal_handler_unblock_by_func(GTK_OBJECT(s->text),
		GTK_SIGNAL_FUNC(seqed_delete_handler), s) ;

	/* adding numeric labels on the left side of the sequence */
	if( (*pos % lblock) == 0) a = *pos ;
	else a = (*pos / lblock + 1) * lblock  ;

	max = *pos + oi ; /* length of the new text */

	say(0, "pos = %i, a = %i, max = %i", *pos, a, max) ;

	num = g_malloc(header + 1) ;
	for(i = a ; i <= max ; i += lblock) {
		gtk_text_set_point(GTK_TEXT(w), i) ;
		sprintf(num, "%7i ", 1 + (i/lblock - 1) * nblocks *sblock  ) ;
		gtk_text_insert(GTK_TEXT(w), o->czcionka, &o->colors[RED], NULL, num, -1) ;
		max += header ;
	}
	g_free(num) ;

	*pos = seqed_seqp_to_pos(s->seqp, s) ;

	say(0, "*** now seqp %i, *pos %i", s->seqp, *pos) ;
	/* freeing memory */
	g_free(outxt) ;
	g_free(intxt) ;
	return EXIT_SUCCESS ;
}


/* updates the cursor info on the status bar */
void update_cursor(seqed_s *s) {
	char msg[100] ;

	gtk_statusbar_pop(s->status, s->id) ;
	if(s->seqp > 0) 
		sprintf(msg, "Nucleotide number %i (left-hand side of the cursor)", s->seqp) ;
	else sprintf(msg, "Cursor outside editable area") ;

	gtk_statusbar_push(s->status, s->id, msg) ;
}


/* checks whether cursor info is up-to-date with the current position */
void check_cursor(gpointer data) {
	seqed_s *s ;
	int newpos ;

	s = (seqed_s *) data ;
	newpos = gtk_editable_get_position(GTK_EDITABLE(s->text)) ;

	if(newpos != s->pos) {
		s->pos = newpos ;
		seqed_pos_to_seqp(s->pos, &s->seqp, s) ;
		update_cursor(s) ;
	}
}


/* reacts to "changed" signal of the main text box */
void seqed_changed_handler(GtkWidget *w, gpointer data) {
	seqed_s *s ;
	char *tmp ;

	say(0, "         !changed") ;
	s = (seqed_s *) data ;
	if(s->modified && s->current_modified) return ;

	s->modified = TRUE ;
	s->current_modified = TRUE ;

	if(!s->current || !s->current->name) return ;
	tmp = g_malloc(strlen(s->current->name) + 15) ;
	strcpy(tmp, s->current->name) ;
	strcat(tmp, " (modified)") ;
	
	gtk_window_set_title(GTK_WINDOW(s->main_window), tmp) ;
	g_free(tmp) ;
}


/* called when text gets deleted */
void seqed_delete_handler(GtkEditable *w, gint st, gint en, gpointer data) {
	opt_s *o ;
	seqed_s * s ;
	int i, l, textl, start, end ;

	s = (seqed_s *) data ;
	o = s->o ;
	start = st ;
	end = en ;

	say(0, "----------------------------- seqed delete handler: %i to %i", start, end) ;

	/* block the handler */
  gtk_signal_handler_block_by_func(GTK_OBJECT(w), 
		GTK_SIGNAL_FUNC(seqed_delete_handler), data) ;

	textl = gtk_text_get_length(GTK_TEXT(w)) ;
	gtk_text_freeze(GTK_TEXT(w)) ;

	/* if the start falls into one of the headers, we have to modify it accordingly */
	l = s->header + (s->nblocks) * (s->sblock + 1) ;
	if(start < l) start = l + s->header ;
	i = start % l ;
	
	if(i > 0 && i < s->header) start += s->header - i ;
	say(0, "textl: %i, start: %i", textl, start) ;
	if(start > textl) start = textl ;
	if(start < 0) start = 0 ;

	/* delete text and rearrange the text area */
	gtk_editable_delete_text(w, start, end) ;
	seqed_insert(s, GTK_WIDGET(w), "", 0, &start) ;
	gtk_text_thaw(GTK_TEXT(w)) ;

	/* unblocking handler and stopping the signal -- we already handled it */
	gtk_signal_handler_unblock_by_func(GTK_OBJECT(w),
		GTK_SIGNAL_FUNC(seqed_delete_handler), data) ;
	gtk_signal_emit_stop_by_name(GTK_OBJECT(w), "delete_text") ;

	/* moving cursor to the new position */
	gtk_text_set_point(GTK_TEXT(w), start) ;
	gtk_editable_set_position(GTK_EDITABLE(w), start) ;
	update_cursor(s) ;

	say(0, "---------------------------------------- ") ;
}


/* called when text is inserted */
void seqed_insert_handler(GtkEditable *w, char *txt, gint l, gint *p, gpointer data) {
	opt_s *o ;
	seqed_s * s ;
	
	s = (seqed_s *) data ;
	o = s->o ;

	say(0, "---------------------------------------- seqed insert handler ") ;

	/* blocking the signal handlers; we have to block delete as well! */
  gtk_signal_handler_block_by_func(GTK_OBJECT(w), 
		GTK_SIGNAL_FUNC(seqed_insert_handler), data) ;
  gtk_signal_handler_block_by_func(GTK_OBJECT(w), 
		GTK_SIGNAL_FUNC(seqed_delete_handler), data) ;

	/* freeze! -- then handle the signal, and then thaw */
	gtk_text_freeze(GTK_TEXT(w)) ;
	seqed_insert(s, GTK_WIDGET(w), txt, l, p) ;
	gtk_text_thaw(GTK_TEXT(w)) ;

	/* unblocking signal handlers */
	gtk_signal_handler_unblock_by_func(GTK_OBJECT(w),
		GTK_SIGNAL_FUNC(seqed_delete_handler), data) ;
	gtk_signal_handler_unblock_by_func(GTK_OBJECT(w),
		GTK_SIGNAL_FUNC(seqed_insert_handler), data) ;

	/* no need for the default handler */
	gtk_signal_emit_stop_by_name(GTK_OBJECT(w), "insert_text") ;

	/* moving cursor to the new position */
	gtk_text_set_point(GTK_TEXT(w), *p) ;
	gtk_editable_set_position(GTK_EDITABLE(w), *p) ;

	update_cursor(s) ;
	say(0, "---------------------------------------- ") ;

}


/* store the window contents in the given sequence */
int seqed_store_sequence(sekw *seq, seqed_s *s) {
	int sl ;
	char *p ;

	say(0, " storing sequence %s", seq->name) ;
	p = gtk_editable_get_chars(GTK_EDITABLE(s->text), 0, -1) ;
	sl = strlen(p) ;

	if(seq->sequ) g_free(seq->sequ) ;
	seq->sequ = g_malloc(sl) ;
	seqed_filter(seq->sequ, p, 0, sl) ;

	return EXIT_SUCCESS ;
}


/* load the text of the seqed window into the "input" panel of the main window */
int seqed_save_data(seqed_s *s) {
	GtkWidget *txt ;
	char *outfmt ;
	sekw *tmp ;
	opt_s *o ;

	say(0, "________ saving data into input") ;

	o = s->o ;
	txt = o->panels[0].tekst ;

	if(!s->modified || !s->sequences) return EXIT_SUCCESS ;
	if(s->current) seqed_store_sequence(s->current, s) ;

	tmp = s->sequences ;

	gtk_text_freeze(GTK_TEXT(txt)) ;
	gtk_editable_delete_text(GTK_EDITABLE(txt), 0, -1) ;

	/* format nicely the sequences into the input window */
	while(tmp) {
		outfmt = seqed_format(tmp->sequ, 70) ;

		gtk_text_insert(GTK_TEXT(txt), o->czcionka, &o->colors[GREEN], NULL, ">", -1) ;
		gtk_text_insert(GTK_TEXT(txt), o->czcionka, &o->colors[BLUE], NULL, tmp->name, -1) ;
		gtk_text_insert(GTK_TEXT(txt), o->czcionka, NULL, NULL, "\n", -1) ;
		gtk_text_insert(GTK_TEXT(txt), o->czcionka, &o->colors[BLACK], NULL, outfmt, -1) ;
		gtk_text_insert(GTK_TEXT(txt), o->czcionka, NULL, NULL, "\n", -1) ;
		g_free(outfmt) ;

		tmp = tmp->next ;
	}
	gtk_text_thaw(GTK_TEXT(txt)) ;
	s->modified = FALSE ;
	s->current_modified = FALSE ;
	
	/* formatting output and inserting it into the input window */
	say(0, "________ saving data done") ;
	return EXIT_SUCCESS ;
}


/* save the data and finish everything */
void seqed_done(GtkWidget *w, gpointer d) {
	seqed_s *s ;

	say(0, "-------------------------------------- seqed done ") ;
	if(!demand("Warning: this will overwrite the current contents of the 'input'\n"
	"window. If you have data other than sequences, you might want to save it first.\n"
	"   Overwrite?    ")) return ;

	s = (seqed_s *) d ;
	seqed_save_data(s) ;
	say(0, "seqed_done: modified=%i", s->modified) ;
	gtk_widget_destroy(GTK_WIDGET(s->main_window)) ;
	say(0, "-------------------------------------- seqed done-done :)");
}


/* catch the "activate" signal from the main text widget and initiate exit sequence */
void seqed_activate_handler(GtkEditable *w, gpointer data) {
	seqed_done(GTK_WIDGET(w), data) ;
}


/* activated when the "cancel" button gets pressed */
void seqed_cancel(GtkEditable *w, gpointer data) {
	seqed_s *s ;

	s = (seqed_s *) data ;
	say(0, "s->modified = %i", s->modified) ;
	if(s->modified && !demand("Are you sure that want to discard the modifications?"))
		return ;
	else {
		s->modified = FALSE ;
		s->current_modified = FALSE ;
	}

	gtk_widget_destroy(GTK_WIDGET(s->main_window)) ;
}


/* handles the destruction of the main window */
void seqed_destroy_handler(GtkEditable *w, gpointer data) {
	seqed_s *s ;

	s = (seqed_s *) data ;

	say(0, "seqed destroy signal caught") ;
	say(0, "s->modified = %i", s->modified) ;
	if(s->modified && !demand("Do you want to discard the modifications?")) 
		seqed_save_data(s) ;
	gtk_main_quit() ;
}


/* reacts to the "go to position" entry */
void seqed_goto_pos(GtkWidget *w, gpointer data) {
	seqed_s *s ;
	int maxpos, i ;
	char *tmp ;

	s = (seqed_s *) data ;

	tmp = gtk_editable_get_chars(GTK_EDITABLE(w), 0, -1) ;
	if(!tmp) return ;

	if(strlen(tmp) == 0) {	
		g_free(tmp) ;
		return ;
	}

	/* calculate the new position */
	s->seqp = atoi(tmp) ;
	g_free(tmp) ;
	s->pos = seqed_seqp_to_pos(s->seqp, s) ;

	/* check boundaries */
	if(s->pos <= 0) s->pos = 1 ;
	maxpos = gtk_text_get_length(GTK_TEXT(s->text)) ;
	if(s->pos > maxpos) s->pos = maxpos ;
	s->seqp = seqed_pos_to_seqp(s->pos, &i, s) ;

	/* set position */
	gtk_text_set_point(GTK_TEXT(s->text), s->pos) ;
	gtk_editable_set_position(GTK_EDITABLE(s->text), s->pos) ;
	gtk_editable_delete_text(GTK_EDITABLE(w), 0, -1) ;
	update_cursor(s) ;
	gtk_widget_grab_focus(GTK_WIDGET(s->text)) ;
	say(0, "goto: seqp=%i, pos=%i", s->seqp, s->pos) ;
}



/* reacts to the "go to position" entry */
void seqed_selection(GtkWidget *w, gpointer data) {
	seqed_s *s ;
	int maxpos, i, sfrom, sto, pfrom, pto ;
	char *tmp1, *tmp2 ;

	s = (seqed_s *) data ;

	tmp1 = gtk_editable_get_chars(GTK_EDITABLE(s->selection[0]), 0, -1) ;
	tmp2 = gtk_editable_get_chars(GTK_EDITABLE(s->selection[1]), 0, -1) ;

	if(!tmp1 || !tmp2) return ;

	if(strlen(tmp1) == 0 || strlen(tmp2) == 0) {	
		g_free(tmp1) ;
		g_free(tmp2) ;
		return ;
	}

	/* calculate the new position */
	sfrom = atoi(tmp1) - 1 ;
	sto = atoi(tmp2) ;
	g_free(tmp1) ;
	g_free(tmp2) ;

	if(sfrom == sto) return ;
	if(sfrom > sto) { i = sfrom ; sfrom = sto ; sto = i ; }

	pfrom = seqed_seqp_to_pos(sfrom, s) ;
	pto = seqed_seqp_to_pos(sto, s) ;

	/* check boundaries */
	maxpos = gtk_text_get_length(GTK_TEXT(s->text)) ;

	if(pfrom <= 0) pfrom = 1 ;
	if(pto <= 0) pto = 1 ;
	if(pfrom > maxpos) pfrom = maxpos ;
	if(pto > maxpos) pto = maxpos ;

	/* set position */
	gtk_editable_delete_text(GTK_EDITABLE(s->selection[0]), 0, -1) ;
	gtk_editable_delete_text(GTK_EDITABLE(s->selection[1]), 0, -1) ;

	gtk_editable_select_region(GTK_EDITABLE(s->text), pfrom, pto) ;
	say(0, "selecting text from %i to %i (%i to %i)", sfrom, sto, pfrom, pto) ;

	update_cursor(s) ;
	gtk_widget_grab_focus(GTK_WIDGET(s->text)) ;
}


/* replaces the currently edited sequence with a new one */
int seqed_reload_data(seqed_s *s, sekw *seq, char *name) {
	sekw *tmp ;
	opt_s *o ;
	int sl ;

	o = s->o ;
	say(0, "-------------------------------------- sequence reload data") ;

	/* no need if there is no real change (e.g. user pressed combo twice) */
	if(seq && seq == s->current) return EXIT_SUCCESS ;

	/* create an empty sequence if seq == NULL */
	say(0, "should create an empty sequence structure now?") ;
	if(seq == NULL) {
		say(0, "yes") ;
		seq = seqed_sequence_new() ;
		if(s->sequences) {
			tmp = s->sequences ;
			while(tmp->next != NULL) tmp = tmp->next ; /* skipping to the end of the list */
			tmp->next = seq ;
		} else {
			s->sequences = seq ;
		}
	}
	
  gtk_signal_handler_block_by_func(GTK_OBJECT(s->text), 
		GTK_SIGNAL_FUNC(seqed_changed_handler), s) ;

	/* we have to store back the sequence if it was modified */
	if(s->current) {
		if(s->current_modified) seqed_store_sequence(s->current, s) ;
		gtk_editable_delete_text(GTK_EDITABLE(s->text), 0, -1) ;
	}

	s->current = seq ; /* choosing the current sequence */

	/* insert text into the dialog unless it is empty */
	say(0, "inserting text") ;
	s->pos = 0 ;
	gtk_text_freeze(GTK_TEXT(s->text)) ;
	if(seq->sequ) {
		sl = strlen(seq->sequ) ;
		seq->leng = sl ;
		seqed_insert(s, s->text, seq->sequ, sl, &s->pos) ;
	} else {
		seqed_insert(s, s->text, " ", 1, &s->pos) ;
	}
	gtk_text_thaw(GTK_TEXT(s->text)) ;

	say(0, "recreating combo") ;
	s->current_modified = FALSE ;
	seqed_create_combo(NULL, s) ;
	
	gtk_window_set_title(GTK_WINDOW(s->main_window), s->current->name) ;
	gtk_signal_handler_unblock_by_func(GTK_OBJECT(s->text),
		GTK_SIGNAL_FUNC(seqed_changed_handler), s) ;
	say(0, "-------------------------------------- sequence reload data done") ;
	return EXIT_SUCCESS ;
}


/* chooses the sequence to edit */
void seqed_choose_sequence(GtkWidget *w, gpointer data) {
	seqed_s *s ;
	sekw *seq ;
	char *tmp ;

	say(0, "---- choosing sequence") ;
	s = (seqed_s *) data ;

	tmp = gtk_editable_get_chars(GTK_EDITABLE(w), 0, -1) ;
	say(0, "---- chosen sequence name: %s", tmp) ;

	seq = s->sequences ;
	while(seq != NULL && strcmp(seq->name, tmp) != 0)
		seq = seq->next ;

	if(!seq) say(0, "%s: no sequence by that name found", tmp) ;
	
	/* new name typed, but not "New...", and we are already editing smth */
	if(s->current && tmp && seq == NULL && strcmp(tmp, "New...") != 0) {
		say(0, "name changed from %s to %s", s->current->name, tmp) ;
		if(s->current->name) free(s->current->name) ;
		s->current->name = tmp ;
		seqed_create_combo(NULL, s) ;
		s->modified = TRUE ;
		s->current_modified = TRUE ;
		tmp = g_malloc(strlen(s->current->name) + 15) ;
		sprintf(tmp, "%s (modified)", s->current->name) ;
		gtk_window_set_title(GTK_WINDOW(s->main_window), tmp) ;
		g_free(tmp) ;
	} else {
		seqed_reload_data(s, seq, tmp) ; /* we do not, I repeat, do not free tmp! */
	}
}


/* creates the "select sequence" combo */
GtkWidget * seqed_create_combo(GtkWidget *parent, seqed_s *s) {
	GList *list = NULL ;
	sekw *tmp ;

	if(s->current) list = g_list_append(list, s->current->name) ;
	
	/* creating the g_list out of sequence names */
	tmp = s->sequences ;

	while(tmp) {
		/* only for other sequences */
		if(!s->current || tmp != s->current) {
			if(!tmp->name) tmp->name = my_strdup("?Unknown") ;

			say(0, "create combo: adding %s", tmp->name) ;
			list = g_list_append(list, tmp->name) ;
		}

		tmp = tmp->next ;
	}

	if(!list) list = g_list_append(list, "Unknown") ;
	else list = g_list_append(list, "New...") ;

	/* if combo not yet created, do it */
	if(s->combo == NULL) {

		if(parent == NULL) return NULL ;
		s->combo = gtk_combo_new() ;
		gtk_box_pack_start(GTK_BOX(parent), s->combo, TRUE, TRUE, 5) ;

		gtk_combo_disable_activate(GTK_COMBO(s->combo)) ;
		gtk_signal_connect(GTK_OBJECT(GTK_COMBO(s->combo)->entry), "activate", 
			GTK_SIGNAL_FUNC(seqed_choose_sequence), s) ;
	}

	gtk_combo_set_popdown_strings(GTK_COMBO(s->combo), list) ;
	say(0, "done with creating combo") ;
	return s->combo ;
}


/* build the interface and load the data */
void seqed_start(gpointer dane, guint signal, GtkWidget *kontrolka) {
	GtkWidget *tmp, *hpole, *ok, *ypole ;
	okno_s *p ;
	seqed_s s ;
	opt_s * o = (opt_s *) dane ;
	int id ;

	say(0, "--------------------------------------------- sequence editor start") ;
	if(o->sequence_editor_window_opened) return ;
	else o->sequence_editor_window_opened = TRUE ;

	s.o = o ;
	p = &o->panels[0] ;

	/* setting some initial values */
	s.nblocks = 10 ; /* number of character boxes in each line */
	s.sblock = 5 ; /* number of characters in each line, separated by spaces */
	s.header = 8 ; /* an 8-char header in each line */
	s.lblock = (s.header + s.nblocks * (s.sblock + 1)) ; /* total line length */
	s.modified = FALSE ; /* was any of the sequences modified? */
	s.current_modified = FALSE ; /* was the current sequence modified? */
	s.sequences = seqed_load_sequences(o) ;
	s.top_header = seqed_create_top_header(&s) ;
	s.current = NULL ;
	s.combo = NULL ;

	/* initializing main window */
	s.main_window = gtk_window_new(GTK_WINDOW_DIALOG) ;
	gtk_signal_connect(GTK_OBJECT(s.main_window), 
		"destroy", GTK_SIGNAL_FUNC(seqed_destroy_handler), &s) ;

	ypole = gtk_vbox_new(FALSE, 5) ;
	gtk_container_add(GTK_CONTAINER(s.main_window), ypole) ;

	/* create the "select sequence" dialog */
	hpole = create_hbox(ypole) ;
	create_label(hpole, "Select sequence to edit: \n"
		"(press ENTER to confirm your choice)") ;
	s.combo = seqed_create_combo(hpole, &s) ;
	gtk_tooltips_set_tip(GTK_TOOLTIPS(o->tips), GTK_COMBO(s.combo)->entry, 
		"All sequences found in the 'input' window are shown in this list. "
		"Select the one to edit and press 'Enter'. If you want to change "
		"a sequence name, type in the new name and press 'Enter'. "
		"Select 'New...' and press 'Enter' to create a new sequence.", NULL) ;

	/* buttons */
	create_button(hpole, seqed_cancel, " Cancel ", &s) ;
	ok = create_button(hpole, seqed_done, " Done ", &s) ;
	gtk_tooltips_set_tip(GTK_TOOLTIPS(o->tips), ok, 
		"Save the modified sequences to the input window end exit sequence editor", NULL) ;
	gtk_widget_grab_default(ok) ;

	hpole = create_hbox(ypole) ;
	create_label(hpole, "Go to position ") ;
	tmp = gtk_entry_new_with_max_length(10) ;
	gtk_tooltips_set_tip(GTK_TOOLTIPS(o->tips), tmp, 
		"Place the cursor behind the nucleotide number... "
		"Press 'Enter' to activate", NULL) ;

	gtk_box_pack_start(GTK_BOX(hpole), tmp, FALSE, FALSE, 5) ;
	gtk_signal_connect(GTK_OBJECT(tmp), "activate", GTK_SIGNAL_FUNC(seqed_goto_pos), &s) ;

	hpole = create_hbox(ypole) ;
	create_label(hpole, "Select sequence from ") ;
	s.selection[0] = gtk_entry_new_with_max_length(10) ;
	gtk_box_pack_start(GTK_BOX(hpole), s.selection[0], FALSE, FALSE, 5) ;
	gtk_signal_connect(GTK_OBJECT(s.selection[0]), "activate", 
		GTK_SIGNAL_FUNC(seqed_selection), &s) ;
	gtk_tooltips_set_tip(GTK_TOOLTIPS(o->tips), s.selection[0], 
		"The number of the first nucleotide in the new selection" 
		"Press 'Enter' to activate the selection", NULL) ;

	create_label(hpole, " to ") ;
	s.selection[1] = gtk_entry_new_with_max_length(10) ;
	gtk_box_pack_start(GTK_BOX(hpole), s.selection[1], FALSE, FALSE, 5) ;
	gtk_signal_connect(GTK_OBJECT(s.selection[1]), "activate", 
		GTK_SIGNAL_FUNC(seqed_selection), &s) ;
	gtk_tooltips_set_tip(GTK_TOOLTIPS(o->tips), s.selection[1], 
		"The number of the last nucleotide in the new selection."
		"Press 'Enter' to activate the selection", NULL) ;

	gtk_box_set_spacing(GTK_BOX(hpole), 0) ;

	s.text = create_text(ypole, NULL, TRUE) ;

	gtk_tooltips_set_tip(GTK_TOOLTIPS(o->tips), s.text,
		"Enter / modify your sequence here. Note that you can enter only "
		"upper/lowercase letters. Press 'Ctrl-ENTER' or click 'Done' "
		"when you have finished", NULL) ;

	/* connecting text handling signals -- the core of the seqed module */
  gtk_signal_connect(GTK_OBJECT(s.text), "insert_text",
    GTK_SIGNAL_FUNC(seqed_insert_handler), &s) ;
  gtk_signal_connect(GTK_OBJECT(s.text), "delete_text",
    GTK_SIGNAL_FUNC(seqed_delete_handler), &s) ;
  gtk_signal_connect(GTK_OBJECT(s.text), "activate",
    GTK_SIGNAL_FUNC(seqed_activate_handler), &s) ;
  gtk_signal_connect(GTK_OBJECT(s.text), "changed",
    GTK_SIGNAL_FUNC(seqed_changed_handler), &s) ;

	seqed_reload_data(&s, s.sequences, NULL) ;

	/* creating the status bar */
	s.status = GTK_STATUSBAR(gtk_statusbar_new()) ;
	gtk_box_pack_start(GTK_BOX(ypole), GTK_WIDGET(s.status), FALSE, FALSE, 5) ;
	s.id = gtk_statusbar_get_context_id(s.status, "Sequence editor") ;
	gtk_statusbar_push(s.status, s.id, "Sequence loaded from input") ;

	/* periodically update information on status bar */
	id = gtk_timeout_add(100, (GtkFunction) check_cursor, &s) ;

	gtk_box_set_spacing(GTK_BOX(ypole), 0) ;
	gtk_widget_show_all(s.main_window) ;

	s.modified = FALSE ;
	s.current_modified = FALSE ;
	gtk_main() ;

	gtk_timeout_remove(id) ;
	seqed_sequences_free(s.sequences) ;
	g_free(s.top_header) ;

	if(s.modified) say(0, "sequences were modified") ;
	o->sequence_editor_window_opened = FALSE ;
	say(0, "--------------------------------------------- sequence editor done") ;
}
