/*  "See": a neat-o superlite POSIX file and man page viewer
Copyright 2008,2009,2010 Mark Eriksen	     Version 0.72

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, version 3 (or any later).  

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
General Public License for more details.  You should have received a copy of the GNU General 
Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

/***** The compiled executable must be called "seetxt" to function correctly *****/


#include "main.h"

/* Slightly Excessive use of Global Variables o_O */
forblink Sff,wff;
seefile *Seefile;
forconfig *Config;
GtkWidget *MainWindow, *Scroll, *TxT,  *Fent, *numLbl, *FindBT, *CaseTgl, *regxTgl, *pushTgl, *MMenu, 
	*watchTgl, *watchoff, *watchon, *ServTgl, *ServOff, *ServOn, *mainframe, *textframe;
navbar Nbar;
GtkTextBuffer *Tbuf;
GtkTextIter start, finish;
GtkTextMark *bmk;
GtkTextTag *norm, *hlite, *lnnm, *pushed, *boldblue, *italred, *hlink, *ctitle;
char *FileCon=NULL, **Fhist;
int CurFS = 1, TF[200000], totalF=0, histC=0, Chist=0, SFsz=0;
gint *bookmarks=NULL; /* an array -- first element will be the number of elements */

/* boolean flags*/
char DND = 0, Settitle = 1;
gint LNsw = 0, watchSW = 0;
int Debug = 0;


int main (int argc, char *argv[]) {
	const gchar *homedir = g_get_home_dir();
	char *cwd=NULL, type, image[MPTH], *term=NULL, options[]="Kd:s:x:vh", *p;
	PangoFontDescription *Tfont, *Nfont;
	int opt, len, server = 0;
	GdkCursor *cursor, *crosshair;
	GdkColor textback;
	GtkAdjustment *vadj, *hadj;
	GtkWidget *v_box, *h_box, *hbox_0;

	if ((p = strrchr(argv[0],'/'))) argv[0] = ++p; /* called with path */

	if (strlen(SDIR) > MPTH-128) {
		fprintf(stderr,"Your runtime directory path, %s, is too long.  In order to access the files there, "
			"it must be shorter than your system's PATH_MAX (%d) by 128 characters.\n",SDIR,MPTH);
		exit(0);
	}

	signal(SIGPIPE,SIG_IGN);

	Config = reconfigure(NULL); /* parse ~/.seeconfig */

	if (!Config->seedata) {
		Config->seedata=ec_malloc(strlen(homedir)+10); 
		sprintf(Config->seedata, "%s/.seedata",homedir);
		if ((opt=open(Config->seedata,O_WRONLY|O_CREAT,S_IWRITE|S_IREAD))==-1) { 
			free(Config->seedata); 
			Config->seedata=NULL; 
		}
		else close(opt);
	}
	
/* command line arguments */
	if (argc>1) {
		if (!strcmp(argv[0],"seeman")) type = 'M';
		else if (!strcmp(argv[0],"seetxt")) type = 'R';
		else {
			puts("The executable name must be \"seetxt\" to function correctly.");
			return 66;
		}
		if (argv[1][0] != '-') {
		/*nb, filenames starting with a dash will need ./ on command line*/ 
			if (strlen(argv[1])>MPTH-1) { 
				puts("Filename is too long!"); 
				return 10; 
			}
			if (type == 'R') {
				if (argv[1][0] != '/') {
					cwd=getcwd(NULL,MPTH+1);
					if (!strncmp(argv[1], "./", 2)) argv[1] += 2;
					if (!cwd || (strlen(cwd) + strlen(argv[1]) > MPTH-1)) {
						puts("Path to file is too long!"); 
						return 10; 
					}
					strcat(cwd,"/");
					strcat(cwd,argv[1]);
					Seefile = initSF(cwd,type,NULL);
				} else Seefile = initSF(argv[1],type,NULL);
			} else {
				if (strchr(argv[1],'/')) {	/* out of tree man page */
					if (!strncmp(argv[1],"./",2)) {
						argv[1] += 2;
						cwd=getcwd(NULL,MPTH+1);
						if (!cwd || (strlen(cwd) + strlen(argv[1]) > MPTH-1)) {
								puts("Path to out of tree manpage is too long!"); 
								return 10; 
							}
						strcat(cwd,"/");
						strcat(cwd,argv[1]);
						Seefile = initSF(cwd,type,"***");
					} else Seefile = initSF(argv[1],type,"***");
				} else Seefile = initSF(argv[1],type,NULL);
			}
			argv[1][0] = '\0';
		}
		if (cwd) free(cwd);
		while ((opt=getopt(argc, argv, options))>0) switch (opt) {
			case ('?'): usage(argv[0]); return 2;
			case ('h'): usage(argv[0]); return 2;
			case ('K'): 
				sprintf(image, "killall -9 seetxt & killall -9 seeman");
				system(image);
				return 0;   /* altho we already suicided */
			case ('d'):
				if (!sscanf(optarg,"%d",&Debug)) {
					usage(argv[0]);
					return 2;
				}
				break; 
			case ('s'): 
				if ((strlen(optarg)>15) || (strcmp(argv[0],"seeman")!=0) || !Seefile) {
					usage(argv[0]); 
					return 2;
				}
				strncpy(Seefile->sec,optarg,SECLEN);
				len = strlen(Seefile->name);
				if (NAMELEN-len > SECLEN+2) {
					strcat(Seefile->name,"(");
					strncat(Seefile->name,optarg,SECLEN);
					strcat(Seefile->name,")");
				}
				break;
			case ('x'):
				if (strlen(optarg) > MAXTERMLEN) {
					fprintf(stderr, "Maximum search term length from command line is %d\n.",MAXTERMLEN);
					return 3;
				}
				term=ec_malloc(strlen(optarg)+1);
				strcpy(term,optarg); 
				break;
			case ('v'): printf("%s version %s\n",argv[0],SEE_VERSION); return 0;
			default: usage(argv[0]); return 2;
		}
	}

	if (Debug>0) fprintf(stderr,"%s pid %d (Debug level %d)\n", argv[0], getpid(), Debug);

/* check for running server */
	if (Config->sock[0]) {
		switch (check_seesock()) {
			case -66:
				Config->sock[0] = 0;
				break;
			case -11: 
				puts("!defunct seesocket won't unlink");
				break;
			case -1: 
				if (Seefile) {
					switch (send_remote(term)) {
						case (-3): puts("Request failed."); break;
						default: break;
					}
					return 20; 
				}
				break;
			case 0: 
				if ((server = loclsckt(Config->sock)) < 3) server = -666;
			default: break;
		}
		if (server > 2) tglServer(NULL,server);
	}


/***** BEGIN GTK ******/
	gtk_init (&argc, &argv);

	MainWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	g_signal_connect (G_OBJECT (MainWindow), "delete_event", G_CALLBACK (handlequit), NULL);
	g_signal_connect (G_OBJECT (MainWindow), "destroy", G_CALLBACK (gtk_main_quit), NULL);
	g_signal_connect (G_OBJECT (MainWindow), "window-state-event", G_CALLBACK (nomax), NULL);

	Nfont = pango_font_description_copy(MainWindow->style->font_desc);
	pango_font_description_set_weight(Nfont,PANGO_WEIGHT_HEAVY);

	v_box = gtk_vbox_new (FALSE, 0);
	gtk_container_add (GTK_CONTAINER (MainWindow), v_box);

/*load pngs*/
	sprintf(image, "%s/seeon.png", SDIR);
	watchon=gtk_image_new_from_file(image);   
	g_object_ref(G_OBJECT(watchon));	
	ServOn=gtk_image_new_from_file(image);   
	g_object_ref(G_OBJECT(ServOn));	
	sprintf(image, "%s/seeoff.png",SDIR);
	ServOff=gtk_image_new_from_file(image);   
	g_object_ref(G_OBJECT(ServOff));	
	watchoff=gtk_image_new_from_file(image);   
	g_object_ref(G_OBJECT(watchoff));	
	
/*menu*/
	MMenu = gtk_menu_new();
	mainmenu();

/* top nav bar */
	Nbar.hbx = gtk_hbox_new (FALSE, 0);
	gtk_box_pack_start(GTK_BOX (v_box), Nbar.hbx, FALSE, FALSE, 0);
	Nbar.back = gtk_button_new_from_stock(GTK_STOCK_GO_BACK);
	gtk_box_pack_start(GTK_BOX (Nbar.hbx), Nbar.back, FALSE, FALSE, 0);
	g_signal_connect (G_OBJECT (Nbar.back), "clicked", G_CALLBACK (shiftFilelist), (gpointer)1);
	Nbar.current = gtk_button_new();
	gtk_box_pack_start(GTK_BOX (Nbar.hbx), Nbar.current, FALSE, FALSE, 0);
	g_signal_connect (G_OBJECT (Nbar.current), "clicked", G_CALLBACK (showlist), NULL);
	Nbar.forward = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD);
	gtk_box_pack_start(GTK_BOX (Nbar.hbx), Nbar.forward, FALSE, FALSE, 0);
	g_signal_connect (G_OBJECT (Nbar.forward), "clicked", G_CALLBACK (shiftFilelist), (gpointer)-1);
	Nbar.select = NULL;		/* set in showlist() */

/* the text area */
	Scroll = gtk_scrolled_window_new(NULL,NULL);
	gtk_box_pack_start(GTK_BOX (v_box), Scroll, FALSE, FALSE, 0);
	vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(Scroll));
	hadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(Scroll));
	TxT = gtk_text_view_new();
	gtk_widget_set_size_request(GTK_WIDGET (Scroll), Config->width, Config->height);
	gtk_text_view_set_editable(GTK_TEXT_VIEW(TxT),FALSE);
	changewrapmode(NULL, Config->wrap);
	gtk_container_add(GTK_CONTAINER(Scroll), TxT);
	g_signal_connect (G_OBJECT (TxT), "button_press_event", G_CALLBACK (mousevent), NULL);
	/*DnD*/
	gtk_drag_dest_set(TxT,GTK_DEST_DEFAULT_ALL,NULL,0,GDK_ACTION_COPY);
	gtk_drag_dest_add_text_targets(TxT);
	gtk_drag_dest_add_uri_targets(TxT);
	g_signal_connect(TxT,"drag-drop",G_CALLBACK(DnDdrop),NULL);
	g_signal_connect(TxT,"drag-motion",G_CALLBACK(DnDmotion),NULL);
	g_signal_connect(TxT,"drag-data-received",G_CALLBACK(DnDreceive),NULL);
	g_signal_connect (TxT, "drag-leave",G_CALLBACK(DnDleave),NULL);
	if (Config->txtfnt) { 
		Tfont = pango_font_description_from_string(Config->txtfnt);
		gtk_widget_modify_font(TxT,Tfont);
		pango_font_description_free(Tfont); 
	}
	if (Config->tbcolor) {
		if (gdk_color_parse(Config->tbcolor, &textback)) gtk_widget_modify_base(TxT, GTK_STATE_NORMAL, &textback);
		free((char*)Config->tbcolor); 
	}

/* the text buffer, inc tags */
	Tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW (TxT));
	italred = gtk_text_buffer_create_tag (Tbuf, "italred", "foreground","#ff0000", "style",PANGO_STYLE_ITALIC, NULL);
	boldblue = gtk_text_buffer_create_tag (Tbuf, "boldblue", "foreground","#0000bb", "weight",PANGO_WEIGHT_BOLD, NULL);
	hlink = gtk_text_buffer_create_tag (Tbuf, "hyperlinks", "foreground","#00aa00", "underline",PANGO_UNDERLINE_DOUBLE, NULL);
	pushed = gtk_text_buffer_create_tag (Tbuf, "pushed", "foreground","#ffff00", "background","#aa00aa", NULL);
	hlite = gtk_text_buffer_create_tag (Tbuf, "highlights", "foreground","#aa00aa", "background","#ffff00", NULL);
	lnnm = gtk_text_buffer_create_tag (Tbuf, "line_numbers", "foreground","#00ffaa", NULL);
	ctitle = gtk_text_buffer_create_tag (Tbuf, "center_title", "justification",GTK_JUSTIFY_CENTER, "weight",700, NULL);

/* bottom bar */
	hbox_0 = gtk_hbox_new (FALSE, 0);
	gtk_box_pack_end (GTK_BOX (v_box), hbox_0, TRUE, TRUE, PAD);

/* blinking server toggle */
	ServTgl = gtk_button_new();
	gtk_container_add(GTK_CONTAINER(ServTgl),ServOff);
	Sff.wgt=ServTgl; 
	Sff.image1=ServOff; 
	Sff.image2=ServOn; 
	Sff.sw=0;
	gtk_button_set_relief(GTK_BUTTON(ServTgl),GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX (hbox_0), ServTgl, FALSE, FALSE, 0);
	g_signal_connect (G_OBJECT (ServTgl), "clicked", G_CALLBACK (tglServer), (gpointer)0);
	g_signal_connect (G_OBJECT (TxT), "key_press_event", G_CALLBACK (TxTKpress), NULL);
	g_signal_connect(ServTgl,"drag-data-received",G_CALLBACK(DnDreceive),NULL);

	mainframe = gtk_frame_new(NULL);
	gtk_box_pack_start (GTK_BOX (hbox_0), mainframe, TRUE, TRUE, PAD);
	h_box = gtk_hbox_new (FALSE, 0);
	gtk_container_add (GTK_CONTAINER (mainframe), h_box);

	pushTgl = gtk_toggle_button_new_with_label ("push");
	gtk_box_pack_start(GTK_BOX (h_box), pushTgl, TRUE, TRUE, PAD);

	numLbl = gtk_label_new("0");
	gtk_box_pack_start(GTK_BOX (h_box), numLbl, FALSE, FALSE, PAD);
	gtk_widget_modify_font(numLbl,Nfont);
	pango_font_description_free(Nfont);
	gtk_label_set_width_chars(GTK_LABEL(numLbl),6);

/* the user entry */
	Fent = gtk_entry_new();
	gtk_entry_set_max_length(GTK_ENTRY(Fent),4096);
	gtk_entry_set_has_frame(GTK_ENTRY(Fent),TRUE);
	gtk_entry_set_width_chars(GTK_ENTRY(Fent),27);
	gtk_box_pack_start(GTK_BOX (h_box), Fent, TRUE, TRUE, PAD);
	g_signal_connect (G_OBJECT (Fent), "key_press_event", G_CALLBACK (FentKpress), NULL);

/* search toggles */
	CaseTgl = gtk_toggle_button_new_with_label ("case");
	gtk_box_pack_start(GTK_BOX (h_box), CaseTgl, TRUE, TRUE, PAD);
	
	regxTgl = gtk_toggle_button_new_with_label ("regexp");
	gtk_box_pack_start(GTK_BOX (h_box), regxTgl, TRUE, TRUE, PAD);

/* blinking watch toggle */
	watchTgl = gtk_button_new();
	gtk_container_add(GTK_CONTAINER(watchTgl),watchoff);
	wff.wgt=watchTgl; 
	wff.image1=watchoff; 
	wff.image2=watchon; 
	wff.sw=0;
	gtk_button_set_relief(GTK_BUTTON(watchTgl),GTK_RELIEF_NONE);
	gtk_box_pack_end (GTK_BOX (hbox_0), watchTgl, FALSE, FALSE, PAD);
	g_signal_connect (G_OBJECT (watchTgl), "clicked", G_CALLBACK (watchfile), NULL);

	gtk_widget_show(ServOn);	
	gtk_widget_show(ServOff);	
	gtk_widget_show(watchon);	
	gtk_widget_show(watchoff);	
	gtk_widget_show_all(MainWindow);
/* then because the widgets must be "realized" first: */
	cursor = gdk_cursor_new(GDK_BOX_SPIRAL);  	
	gdk_window_set_cursor(MainWindow->window, cursor);
	gdk_cursor_unref(cursor);	

	crosshair = gdk_cursor_new(GDK_CROSSHAIR);  	
	gdk_window_set_cursor (gtk_text_view_get_window (GTK_TEXT_VIEW(TxT), GTK_TEXT_WINDOW_TEXT), crosshair);   
	gdk_cursor_unref(crosshair);

	gtk_window_set_title(GTK_WINDOW (MainWindow), argv[0]);

		/* load file, prefix path */	
	if (Seefile) {
		if ((Seefile->type == 'R')) opt = loadfile(0);
		else opt = loadman(0);
		if (opt && Config->filelist) update_filelist(NULL,0);
		if (opt && term) {
			gtk_entry_set_text(GTK_ENTRY(Fent), term);
			searchlight();
		}
		if (term) free(term);
	}
	else showconfig(Config);

	if (server == -666) (error_popup("Could not start server."));

	gtk_main ();
	return 0;
}


void addtohistory (char *line) {	/* histC is the number of lines in history */
	int i;				/* Chist is the current line */
	for (i=0; i<histC; i++) {
		if (strcmp(Fhist[i],line)==0) {
			Chist=i; 
			return; 
		}
	}	/* entry exits already */

	Fhist = realloc(Fhist,(histC+1)*sizeof(char*));			
	if (!(Fhist)) {
		error_popup("WARNING: Out of memory");
		exit (30);
	}
	Fhist[histC] = ec_malloc(strlen(line)+1);
	strcpy(Fhist[histC],line);
	histC++;
	Chist=histC; 
}


void apropos () {	/* called from mainmenu()...clickable entries are returned by mousevent() */
	char *string, *tok;
	const gchar *term = gtk_entry_get_text(GTK_ENTRY(Fent));
	int len = strlen(term);
	FILE *fstIN;
	GtkTextIter end;
	GdkCursor *hand=gdk_cursor_new(GDK_HAND2);
	
	if (len == 0) return;
	string=ec_malloc(len+23);
	sprintf(string, "Apropos search for \"%s\"?",term);
	if((confirm_popup("Confirm",string)) == -6) {
		free(string);
		return;
	}
	free(string);
	addtohistory((char*)term);
	
	string=ec_malloc(len+11);
	sprintf(string, "apropos \"%s\"", term);
	if ((fstIN = popen(string, "r")) == NULL) {
		free(string);
		error_popup("apropos failed"); 
		return;}
	free(string);
	clear(1);	
	gtk_text_buffer_get_end_iter(Tbuf,&end);
	while ((string=linein(fstIN)) != NULL) {
		tok=strtok(string," ");
            	gtk_text_buffer_insert_with_tags(Tbuf,&end,(const gchar*)tok,-1,hlink,NULL);
		tok=strtok(NULL," ");
            	gtk_text_buffer_insert_with_tags(Tbuf,&end,(const gchar*)tok,-1,lnnm,NULL);
		tok=strtok(NULL,"");
		gtk_text_buffer_insert(Tbuf,&end,(const gchar*)tok,-1);
		free(string);
	}
	if ((pclose(fstIN)) != 0) error_popup("pclose fail in apropos()");
	gdk_window_set_cursor (gtk_text_view_get_window (GTK_TEXT_VIEW(TxT), GTK_TEXT_WINDOW_TEXT), hand);   
	gdk_cursor_unref(hand);	
}


void applytag (int Ts, int Te, char *tag) {   /* called from dobmks() */
	GtkTextIter tagS, tagE;
	if (Debug>0) g_print("applytag()...");
	gtk_text_buffer_get_iter_at_offset(Tbuf,&tagS,Ts);
	gtk_text_buffer_get_iter_at_offset(Tbuf,&tagE,Te);
	gtk_text_buffer_apply_tag_by_name(Tbuf, tag, &tagS, &tagE); 
}


gboolean blinktoggle (forblink *ff) {
	if (ff->sw==0) {
		gtk_container_remove(GTK_CONTAINER(ff->wgt), ff->image1);
		gtk_container_add(GTK_CONTAINER(ff->wgt), ff->image2);
		ff->sw=1;
	} else {	
		gtk_container_remove(GTK_CONTAINER(ff->wgt), ff->image2);
		gtk_container_add(GTK_CONTAINER(ff->wgt), ff->image1);
		ff->sw=0;}
	return TRUE;
}


void bookmark_list () {		/* called from mainmenu() */
	int i, ii;
	GtkListStore *liststore = gtk_list_store_new(2,G_TYPE_INT,G_TYPE_STRING);
	GtkWidget *subwin = gtk_window_new(GTK_WINDOW_POPUP), 
		*bmkLV = gtk_tree_view_new_with_model(GTK_TREE_MODEL(liststore)),
		*frame = gtk_frame_new("Bookmarks"),
		*flab = gtk_frame_get_label_widget(GTK_FRAME(frame)),
		*BMVbox = gtk_vbox_new (FALSE, PAD),
		*BMHbox = gtk_hbox_new (FALSE, PAD),
		*DisBT = gtk_button_new_with_label("Dismiss");
	GtkCellRenderer *render;	
	GtkTreeViewColumn *column;	
	GtkTreeIter Litr;
	PangoAttrList *atrb=pango_attr_list_new();
	PangoAttribute *ubold=pango_attr_weight_new(PANGO_WEIGHT_ULTRABOLD);
	gchar *ptr, snippet[32];

	if (!(Seefile)) return;
	if (Debug>0) g_print("bookmark_list() %d...\n",bookmarks[0]);	
	
	gtk_window_set_transient_for(GTK_WINDOW(subwin),GTK_WINDOW(MainWindow));   
	gtk_window_set_position(GTK_WINDOW(subwin), GTK_WIN_POS_CENTER_ON_PARENT);	
	gtk_container_add(GTK_CONTAINER (subwin), frame);
	gtk_container_set_border_width(GTK_CONTAINER(frame),PAD);
	gtk_container_add(GTK_CONTAINER (frame), BMVbox);
	ubold->start_index=0;
	ubold->end_index=10;
	pango_attr_list_insert(atrb,ubold);
	gtk_label_set_attributes(GTK_LABEL(flab),atrb);
	
	gtk_box_pack_start(GTK_BOX (BMVbox), bmkLV, FALSE, FALSE, PAD);
	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(bmkLV), FALSE);
	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(bmkLV), TRUE);
	render = gtk_cell_renderer_text_new();
	g_object_set(G_OBJECT(render), "foreground", "Red", "foreground-set", TRUE,NULL);
	column = gtk_tree_view_column_new_with_attributes("LN", render, "text", 0, NULL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(bmkLV), column);
	render = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes("text", render, "text", 1, NULL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(bmkLV), column);
	g_signal_connect(bmkLV, "row-activated", G_CALLBACK(usebmk), subwin);
	g_signal_connect(bmkLV, "button-press-event", G_CALLBACK(delbmk), liststore);

	gtk_box_pack_start(GTK_BOX (BMVbox), BMHbox, FALSE, FALSE, PAD);
	
	gtk_box_pack_start(GTK_BOX (BMHbox), DisBT, FALSE, FALSE, PAD);
	g_signal_connect(DisBT, "clicked", G_CALLBACK(destroy_widget), subwin);

	if (bookmarks[0]>1) sortintray(&bookmarks[1],bookmarks[0]);
	for (i=1;i<=bookmarks[0];i++) {
		if (bookmarks[i]==0) continue;	/* was deleted via delbmk() */
		if (!(ptr=textline(bookmarks[i]))) sprintf(snippet, "LINE NUMBER INVALID");
		else for (ii=0;ii<31;ii++) {		/* create index line */
			snippet[ii]=ptr[ii];
		}
		snippet[ii]='\0';
		if (Debug>1) g_print("\tsnippet=\"%s\"",snippet);	
		gtk_list_store_append(liststore,&Litr);
		gtk_list_store_set(liststore, &Litr, 0,bookmarks[i], 1,snippet, -1);		
	}

	gtk_widget_show_all(subwin);	
}


void changewrapmode (GtkWidget *RButton, char mode) {
	GtkWrapMode macro;
	switch (mode) {
		case 'W':
			macro = GTK_WRAP_WORD;
			break;
		case 'E':
			macro = GTK_WRAP_CHAR;
			break;
		default:
			macro = GTK_WRAP_NONE;
			break;
	}
	if (RButton) Config->wrap = mode;
	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(TxT),macro);
}


void clear (int opt) {		
	/* nb. that there is no record of the tags (except for the seedata) */
	gtk_text_buffer_get_bounds(Tbuf,&start,&finish);
	gtk_text_buffer_delete(Tbuf,&start,&finish);
	if (FileCon) {
		free(FileCon);
		FileCon=NULL;
	}
	if ((opt) && (Seefile)) {
		free(Seefile);
		Seefile=NULL;
	}
	if (bookmarks) {
		free(bookmarks);
		bookmarks=NULL;
	}
	SFsz=0;
	LNsw=0;
	gtk_button_set_label(GTK_BUTTON(Nbar.current),"");
	if (Debug>0) { 
		g_print("clear(%d) done\n",opt); 
		fflush(stdout); 
	}
}


void closeFL_window (GtkWidget *ignored, gpointer *sbwin) {
	gtk_widget_destroy(GTK_WIDGET(sbwin));
	Nbar.select = NULL;
}


int confirm_popup (char *title, char *messg) {
	int respv;
	GtkWidget *textLbl, *dialgBx = gtk_dialog_new_with_buttons (title,GTK_WINDOW(MainWindow),
		GTK_DIALOG_NO_SEPARATOR,GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,GTK_STOCK_OK,GTK_RESPONSE_OK,NULL);
	if (Settitle) gtk_window_set_title(GTK_WINDOW (MainWindow), "Waiting for confirmation");
	gtk_window_set_position(GTK_WINDOW(dialgBx), GTK_WIN_POS_CENTER_ON_PARENT);	
	textLbl = gtk_label_new_with_mnemonic(messg);
	gtk_label_set_justify(GTK_LABEL(textLbl),GTK_JUSTIFY_CENTER);
	gtk_box_pack_start(GTK_BOX (GTK_DIALOG(dialgBx)->vbox), textLbl, FALSE, FALSE, 0);
	gtk_widget_show(textLbl);
	respv = gtk_dialog_run(GTK_DIALOG(dialgBx));
	gtk_widget_destroy(dialgBx);
	gtk_window_set_title(GTK_WINDOW (MainWindow), "...okay");
	return respv;	/* -6 if "Cancel", -5 if "OK" */
}	


void copytoX (GtkWidget *ignored, int opt) {
	GdkDisplay *display = gdk_display_get_default();
	GtkClipboard *clipboard = gtk_clipboard_get_for_display(display,GDK_SELECTION_CLIPBOARD);
	if (opt) { gtk_text_buffer_get_bounds(Tbuf,&start,&finish);
		gtk_text_buffer_select_range(Tbuf,(const GtkTextIter*)&start,(const GtkTextIter*)&finish); }
	gtk_text_buffer_copy_clipboard(Tbuf,clipboard); 
}


void cursor_tofound (GtkWidget *widget, int way) {
	gint oset, i, sw=0;
	GtkTextMark *mk=gtk_text_buffer_get_insert(Tbuf); 
	GtkTextIter itr;
	if (totalF == 0) return;
	gtk_widget_grab_focus(TxT);
	gtk_text_buffer_get_iter_at_mark(Tbuf,&itr,mk); 

	if (LNsw==1) {	
		numberlines();	
		sw = 1;
	}	

	oset = gtk_text_iter_get_offset(&itr);

	if (way==0) {			/* forward */
		for (i=0; i <= (totalF*2)-1; i+=2) {
			if (TF[i] > oset) { 
				gtk_text_buffer_get_iter_at_offset(Tbuf,&itr,TF[i]);
				gtk_text_buffer_place_cursor(Tbuf,&itr);
				gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(TxT),&itr,0.0,TRUE,0.5,0.5);
				break; 
			}
		}
	} else {			/* back */
		for (i=(totalF*2)-1; i>=0;i-=2) {
			if (TF[i] < oset) { 
				gtk_text_buffer_get_iter_at_offset(Tbuf,&itr,TF[i]);
				gtk_text_buffer_place_cursor(Tbuf,&itr);
				gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(TxT),&itr,0.0,TRUE,0.5,0.5);
				break; 
			}
		}
	}

	if (sw == 1) numberlines();
}

gint cursorline() {	/* gets cursor position to preserve in Filelist */
	GtkTextIter where; 
	gint pos, line;
	
	g_object_get(Tbuf,"cursor-position",&pos,NULL);
	gtk_text_buffer_get_iter_at_offset(Tbuf, &where, pos);
	line = gtk_text_iter_get_line(&where);
	
	return line;
}


gboolean delbmk (GtkWidget *treeview, GdkEventButton *MoBt, GtkListStore *liststore) { /* called from bookmarks() */	
	GtkTreeIter itr;
	GtkTreeModel *Tmod=gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
	GtkTreeSelection *selected=gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
	int ln, i;

	if (MoBt->button == 2) {
		gtk_tree_selection_get_selected(selected,&Tmod,&itr);
		gtk_tree_model_get(Tmod, &itr, 0, &ln, -1);
		for (i=1;i<=bookmarks[0];i++) if (bookmarks[i] == ln) { bookmarks[i]=0; bookmarks[0]--; break;}
		gtk_list_store_set(liststore, &itr, 0,0, -1);
		gtk_list_store_set(liststore, &itr, 1,"", -1);
		savebmks(NULL,NULL);
		return TRUE;			
	} else return FALSE;
} 


void destroy_widget (GtkWidget *widget, GtkWidget *dead) {
	gtk_widget_destroy(dead);
} 


gboolean DnDdrop (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer *NA) {
        GdkAtom target_type;

	if (Debug>2) { g_print("\tDnDdrop()\t"); fflush(stdout); }

	if (context-> targets) {
/* adapted from "TestDnD - main.c : Simple tutorial for GTK+ Drag-N-Drop" Copyright (C) 2005 Ryan McDougall */
/* GNU General Public License http://live.gnome.org/GnomeLove/DragNDropTutorial */
		target_type = GDK_POINTER_TO_ATOM (g_list_nth_data (context-> targets, 0)); /* Choose the best target type */
		gtk_drag_get_data (
			TxT,         /* we will receive 'drag-data-received' signal */
			context,        /* represents the current state of the DnD */
			target_type,    /* the target type we want */
			time            /* our time stamp */
		);
	}
	else return FALSE;  	/* cancel */
	return TRUE;
}


void DnDleave (GtkWidget *widget, GdkDragContext *context, guint time, gpointer *NA) {
	gtk_text_view_set_editable(GTK_TEXT_VIEW(TxT),FALSE);	/* read-only fowls drag n' drop */
	if (Debug>2) { g_print("\tDnDleave()\t"); fflush(stdout); }
	DND=0;
}


gboolean DnDmotion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, 
GtkSelectionData *seld, guint ttype, guint time, gpointer *NA) {
	if (DND) return TRUE;
	if (Debug>2) { g_print("\tDnDmotion()\t"); fflush(stdout); }
	gtk_text_view_set_editable(GTK_TEXT_VIEW(TxT),TRUE);	/* read-only fowls drag n' drop */
	DND=1;
	return TRUE;
}


void DnDreceive (GtkWidget *widget, GdkDragContext *context, gint x, gint y, 
GtkSelectionData *data, guint ttype, guint time, gpointer *NA) {
	gboolean got=TRUE;	/* FALSE will bounce inappropriate data */
	gchar *ptr=(char*)data->data;
	if (!(ptr)) { 
		gtk_drag_finish (context, FALSE, FALSE, time); 
		return; 
	}

	if (Debug>1) fprintf(stderr,"\nDnDrecieve() \"%s\"\n",ptr); 

	if ((strncmp(ptr,"file:///",8)!=0) || (strlen(ptr)>MPTH)) got=FALSE;
	gtk_drag_finish (context, got, FALSE, time); /* we are responsible for this completion */
	if (got) { 
		ptr+=(7*sizeof(char)); 
		ptr=defluff(ptr); 
		loadnew(ptr,NULL,0); 
		free(ptr); 
	}
}


void dobmks () {	/* loads bookmarks, called from loadman() or loadfile() */
	int Ts, Te;	/* also qv. placebmk() and savebmks()	*/
	int i=1, type;
	char tmpname[FLESZ], *line=NULL, *tok, messg[FLESZ], DL[]="*\n";
	if (Seefile->type == 'R') line=returnline(Config->seedata,Seefile->path); 
	else {
		sprintf(tmpname,"%s(%s)",Seefile->path,Seefile->sec);
		line=returnline(Config->seedata,tmpname);
	}

	if (line) {
		tok=strtok(line,DL); 
		if (tok[0]=='0') {  /* ie. no bookmarks */
			bookmarks=ec_malloc(sizeof(int));
			bookmarks[0]=0;
			if (!(tok=strtok(NULL,DL))) return;		
		} else {
			bookmarks=ec_malloc(atoi(tok)+1);
			bookmarks[0]=atoi(tok);
			while ((tok=strtok(NULL,DL)) && (tok[0]!='R') && (tok[0]!='B')) {
				bookmarks[i]=atoi(tok);
				if (Debug>1) g_print("domks() #=%d i=%d bm=%d\n",bookmarks[0],i,bookmarks[i]);
				i++;
			}
			if (!(tok)) return; 
		}
	/* now the highlights */		
		if (tok[0]=='R') type=0;
		else if (tok[0]=='B') type=1;
		else {
			sprintf(messg, "Corrupted entry in\n%s!",Config->seedata);
			error_popup(messg);
			return; 
		}
		while ((tok=strtok(NULL,DL))) {
			if (tok[0]=='R') type=0;
			else if (tok[0]=='B') type=1;
			else {
				Ts=atoi(tok);	/* these numbers should be paired */
				if (!(Te=atoi(strtok(NULL,DL)))) return;  /* missing one? ...oh well */ 
				if (type==1) applytag(Ts,Te,"boldblue");
				else applytag(Ts,Te,"italred"); 
			}
			if (Debug>1) g_print("domks() type=%d Ts=%d Te=%d\n",type,Ts,Te); 
		}		 
		free(line); 
	} else {
		bookmarks=ec_malloc(sizeof(int));
		bookmarks[0]=0;
	}
	if (Debug>0) g_print("end domks()\n"); 
}


void error_popup (char *messg) {
	char title[12]=" See/Error ";
	GtkWidget *textLbl, *dialgBx = gtk_dialog_new_with_buttons (title,GTK_WINDOW(MainWindow),GTK_DIALOG_MODAL,GTK_STOCK_OK,GTK_RESPONSE_OK,NULL);
	gtk_window_set_title(GTK_WINDOW (MainWindow), "!Error");
	textLbl = gtk_label_new_with_mnemonic(messg);
	gtk_label_set_justify(GTK_LABEL(textLbl),GTK_JUSTIFY_CENTER);
	gtk_box_pack_start(GTK_BOX (GTK_DIALOG(dialgBx)->vbox), textLbl, FALSE, FALSE, 0);
	gtk_widget_show(textLbl);
	gtk_dialog_run(GTK_DIALOG(dialgBx));
	gtk_widget_destroy(dialgBx);
	gtk_window_set_title(GTK_WINDOW (MainWindow), "...okay");
}	


void exec_proc () {
	GtkTextIter begin, end;
	gboolean sel=gtk_text_buffer_get_selection_bounds(Tbuf,&begin,&end);
	int i, len;
	FILE *pin, *tfp;
	const gchar *term = gtk_entry_get_text(GTK_ENTRY(Fent)), *homedir;
	char *tfile, flag=0, *text=NULL, *cmmd, *line;
	seefile *old;

	if ((!(term)) || ((strlen(term))==0)) return;
	
	addtohistory((char*)term);
	cmmd=ec_malloc(strlen(term)+16);
	sprintf(cmmd,"Execute:\n%s",term);
	if (confirm_popup("See Operation",cmmd)==-6) { free(cmmd); return; }
	free(cmmd);

	if (Debug>0) { g_print("exec_proc() +%s+",term); fflush(stdout); }

			/* write temp file if necessary */
	if ((strstr(term,"SEEBUF"))) {
		homedir = g_get_home_dir();
		tfile = ec_malloc(strlen(homedir)+16);
		sprintf(tfile,"%s/.seeTMP",homedir);
		if (!(tfp=fopen(tfile,"w"))) { 
			error_popup("Could not\ncreate temp file!"); 
			return; 
		}
		cmmd=strsub(term,tfile);
		if ((!(sel)) && (!(FileCon))) {
			gtk_text_buffer_get_bounds(Tbuf,&start,&finish);
			FileCon=gtk_text_buffer_get_text(Tbuf,&start,&finish,FALSE);
			text=FileCon;
		}
		else if (sel) {
			text=gtk_text_buffer_get_text(Tbuf,&begin,&end,FALSE); 
			flag++; 
		}
		else text=FileCon;
		len=strlen(text);
		for (i=0;i<len;i++) fputc(text[i],tfp);
		flag++;			
		fclose(tfp); } 		
	else {  cmmd=ec_malloc(strlen(term)+8); 
		strcpy(cmmd,term); }
	if (Config->bRedirect) strcat(cmmd," 2>&1");  /* redirect stderr */
			/* save our position in current file if necessary */
	if (Seefile) {
		old = SFcopy(Seefile);
		i = cursorline();
		update_filelist(old, i);
		free(old);
	}
			/* execute and pipe output into text buffer */
	clear(0);
	if (Debug>1) { g_print("...cmmd=\"%s\"...",cmmd); fflush(stdout); }	
	pin=popen(cmmd,"r");
	gtk_text_buffer_get_end_iter(Tbuf,&finish);
	while ((line=linein(pin))) {
		gtk_text_buffer_insert(Tbuf,&finish,(const gchar*)line,-1);
		while(gtk_events_pending()) gtk_main_iteration();	
		free(line); }
	line=ec_malloc(64);
	sprintf(line, "Process returned %d",pclose(pin));
	gtk_window_set_title(GTK_WINDOW (MainWindow), line);
	free(line);
		
	if (FileCon) free(FileCon); FileCon=NULL;

	free (cmmd);
	if (flag>0) { unlink(tfile); free(tfile); }
	if (flag==2) free(text);
	if (Debug>0) { 
		g_print("...exec_proc() DONE\n"); 
		fflush(stdout); 
	}
}


void exeFSlisting (char *listing) {	/* from fileselect() or shiftFilelist() */
	int lastline = 0;
	char *ptr, sec[SECLEN+1] = "\0", tplate[16];
	if (!(ptr = strrchr(listing,'|'))) return; // bad line in filelist

	ptr[0] = '\0';
	sprintf(tplate,"(%%%d[^)]",SECLEN);
	sscanf(++ptr,tplate,sec);
	ptr = strrchr(ptr,'$');
	if (ptr) sscanf(ptr,"$%d",&lastline);

	if (sec[0]) loadnew(listing,sec,lastline);
	else loadnew(listing,NULL,lastline);
}


gboolean FentKpress (GtkWidget *widget, GdkEventKey *kyprs, gpointer *data) {
	switch(kyprs->keyval) {
		case GDK_Up:		/* command history */
			if (histC==0) return TRUE; 
			if (Chist <= 0) Chist=histC-1;
			else Chist--;
			gtk_entry_set_text(GTK_ENTRY(Fent),Fhist[Chist]);
			return TRUE;
	 	case GDK_Down:
			if (histC==0) return TRUE; 
			Chist++;
			if (Chist >= histC) { 
				gtk_entry_set_text(GTK_ENTRY(Fent),"");
				Chist = -1;
				return TRUE;} 
			gtk_entry_set_text(GTK_ENTRY(Fent),Fhist[Chist]);
			return TRUE;
		case GDK_Return:
			searchlight();
			return TRUE;
		default:
			return FALSE; 
	}		/* otherwise there will be no typing in Fent */
	return FALSE;
}


void file_out (void) {		/* callable from mainmenu() if Config->copyto exists */
	GtkTextIter begin, end;
	gboolean sel=gtk_text_buffer_get_selection_bounds(Tbuf,&begin,&end);
	int ip;
	char messg[MPTH+256], name[MPTH], *rp, flag=0, *ptr;
	const gchar *term = gtk_entry_get_text(GTK_ENTRY(Fent));

	ip = strlen(term);
	if ((!ip) || (ip+strlen(Config->copyto)>MPTH)) { 
		error_popup("You must give a proper file name\nin the text entry."); 
		return;
	}
	addtohistory((char*)term);

	if (Debug>0) { g_print("file_out() +%s+",term); fflush(stdout); }

/* confirm */
	sprintf(name, "%s/%s", Config->copyto,term);
	if (sel) sprintf(messg,"Copy selected text to\n\"%s/%s\"?",Config->copyto,term);
	else sprintf(messg,"Copy text buffer content to\n\"%s/%s\"?",Config->copyto,term);
	if (confirm_popup("See Operation",messg)==-6) return;
	/* get the text */	
	if ((!(sel)) && (!(FileCon))){
		gtk_text_buffer_get_bounds(Tbuf,&start,&finish);
		FileCon=gtk_text_buffer_get_text(Tbuf,&start,&finish,FALSE);
		flag=1;
	}
	if (sel) { ptr=gtk_text_buffer_get_text(Tbuf,&begin,&end,FALSE); flag=2; }
	else ptr=FileCon;
				/* write to file */
	if ((rp=writeout(ptr,name,&ip))) {
		sprintf(messg, "Copy failed:\n%s",rp);
		error_popup(messg); 
		return; 
	}
	Settitle=0;
	if (ip!=strlen(ptr)) gtk_window_set_title(GTK_WINDOW (MainWindow), "Copy Incomplete!");
	else gtk_window_set_title(GTK_WINDOW (MainWindow), "Copy Complete");
	loadnew(name,NULL,0); 
	Settitle=1;
	if (flag==1) { free(FileCon); FileCon=NULL; }
	if (flag==2) free(ptr);
	if (Debug>0) { g_print("...file_out() DONE"); fflush(stdout); }
}


void fileselect (GtkTreeView *treeview, GtkTreePath *treepath, GtkTreeViewColumn *treecol, GtkWidget *swin) {
/* from "row-activated" callback in showlist() */
	int len, i, lend;
	char list[27][FLESZ], *ptr;
	gchar *file;
	GtkTreeModel *Tmod=gtk_tree_view_get_model(treeview);
	GtkTreeIter itr;

	Nbar.select = NULL;
	if (gtk_tree_model_get_iter (Tmod, &itr, treepath)) gtk_tree_model_get(Tmod, &itr, 0, &file, -1);
	else return;
	lend = strlen(file);
	if ((ptr=strrchr(file,' '))) ptr[0] = '|';

	gtk_widget_destroy(swin);	 

	len = loadlist(list);

	for (i=0;i<len;i++)
		if (strncmp(file,list[i],lend) == 0) break; 

	if (Debug>0) g_print("fileselect(): %s %d %d\n", file, len, i);
	free(file);

	if (i==len) return; // file not listed 

	CurFS = 1;
	exeFSlisting(list[i]);
}


int getagcoords (GtkTextTag *tag, char *tagline) {	/* called from savebmks */
	int cc, total=0;
	char coord[16];
	GtkTextIter itr;

	if (Debug>0) g_print("getagcoords() %s | ",tagline);	
	gtk_text_buffer_get_iter_at_line(Tbuf,&itr,0);
	if  ((gtk_text_iter_forward_to_tag_toggle(&itr,tag)) == TRUE) {
		cc=gtk_text_iter_get_offset(&itr);
		if (Debug>1) g_print("%d -- ",cc);	
		if (tag == boldblue) sprintf(tagline,"*B*%d*",cc);
		else if (tag == italred) sprintf(tagline, "*R*%d*",cc);
		total=strlen(tagline);
		while ((gtk_text_iter_forward_to_tag_toggle(&itr,tag)) == TRUE) {
			cc=gtk_text_iter_get_offset(&itr);
			if (Debug>1) g_print("%d -- ",cc);
			sprintf(coord, "%d*", cc);
			if ((total+=strlen(coord))>=4096) break;
			strcat(tagline,coord); }
	}	
	return total;
}


void gotobmk () { gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(TxT),GTK_TEXT_MARK(bmk),0.0,TRUE,0.0,0.5); }


void gotoCursor (GtkWidget *ignored) {
/* called from mainmenu() ctrl-g */
	GtkTextMark *mk=gtk_text_buffer_get_insert(Tbuf);
	GtkTextIter iter;
	gtk_text_buffer_get_iter_at_mark(Tbuf,&iter,mk);
	scrollToIter(&iter);
}


void grabfocus (GtkWidget *ignored) {
	gtk_entry_set_text(GTK_ENTRY(Fent),"");
	gtk_widget_grab_focus(Fent);
}

void handlequit() {        /* delete, ie. "close window" event callback, and from mainmenu() "quit" */
	int lastline = cursorline();
	seefile *old;
	pid_t pid=getpid();

	tglServer(NULL,-1);
	if (Seefile) {
		old = SFcopy(Seefile);
		free(Seefile);
		Seefile = NULL;
		update_filelist(old, lastline);
		free(old);
	}
 	if (Debug>0) { g_print("______pid %d handlequit(), exiting_______\n\n",pid); fflush(stdout); }
	gtk_widget_destroy(MainWindow);
	gtk_main_quit();
}


void highlight (GtkWidget *widget, gchar *tag) { /* called from mainmenu() */  
	GtkTextIter begin, end;
	gtk_text_buffer_get_selection_bounds(Tbuf, &begin, &end);
	gtk_text_buffer_apply_tag_by_name(Tbuf, tag, &begin, &end); 
	savebmks(NULL,NULL);
}


void help (void) { loadnew("seetxt","1",0); } 


seefile *initSF (char *path, char type, char *sec) {
	char *ptr;
	int len;
	seefile *r = ec_malloc(sizeof(seefile));
	memset(r,0,sizeof(seefile));

	if (Debug) fprintf(stderr,"initSF() %s %c\n",path,type);

	strncpy(r->path,path,MPTH);
	r->type = type;
	if (type == 'R') {
		ptr = strrchr(path,'/');
		if (!ptr) {
			error_popup("No / prefixed to Regular file path!");
			return NULL;
		}
		strncpy(r->name,++ptr,NAMELEN);
		r->sec[0] = '\0';
	} else if (sec) {
		len = strlen(sec);
		if (len > SECLEN) len=SECLEN;
		strncpy(r->name,path,NAMELEN-len-2);
		if (sec[0]) {
			strcat(r->name,"(");
			strncat(r->name,sec,len);
			strcat(r->name,")");
			strncpy(r->sec,sec,SECLEN);
		}
	} else {
		strncpy(r->name,path,NAMELEN-1);
		r->sec[0] = '\0';
	}

	r->name[NAMELEN] = '\0';

	return r;
}


void list2view (GtkListStore* ls) {
	GtkTreeIter itr;
	char list[27][FLESZ], *ptr;
	int i, len = loadlist(list);
	if (!len) return;

	for (i=0;i<len;i++) {
		gtk_list_store_append(ls,&itr);
		if ((ptr=strrchr(list[i],'$'))) ptr[0]='\0';
		if ((ptr=strrchr(list[i],'|'))) ptr[0]=' ';
		gtk_list_store_set(ls,&itr,0,list[i],-1);
	}
	setFL_selected();
}


int loadfile (int setline) {		/* called from main(), loadnew() or reload() */ 
	int len=filelen(Seefile->path), i, lastline=0;
	char messg[NAMELEN+128];
	if (Debug>0) g_print("loadfile() '%s',%d bytes...\n",Seefile->path,len);
	
	if (len<=0) {
		if (Debug>0) { g_print("loadfile() -- filelen() returned %d (can't load)\n",len); fflush(stdout); }
		if (len==0) sprintf(messg, "\"%s\"\nhas no content",Seefile->path);
		else sprintf(messg, "Can't open\n%s", Seefile->path);
		error_popup(messg);
		if (len!=0) return 0; 
	}

	lastline=cursorline();
	clear(0);
	FileCon = ec_malloc(len+1);
	gtk_text_buffer_get_bounds(Tbuf,&start,&finish);

	if (len>Config->tailat) {
		if (!(loadlarge(len))) {
			clear(1);
			return 1;
		}
	} else {	
		if ((i=fdreadin(Seefile->path,FileCon,-1,len,0))!=len) {
			if (Debug>1) g_print("\tshort read (stat %d, read %d)\n",len,i);
			if (i==0) sprintf(messg, "Permission denied for\n%s", Seefile->path);
			else sprintf(messg, "Read short on\n%s", Seefile->path);
			error_popup(messg); 
		}
		if (!(g_utf8_validate((const gchar*)FileCon,-1,NULL))) error_popup("Can't Validate Text\n(Error #4)");
		else gtk_text_buffer_set_text(Tbuf,FileCon,len); 
	}

	if (Config->seedata) dobmks();	/* needs text in buffer*/
	else { 
		bookmarks=ec_malloc(sizeof(int)); 
		bookmarks[0]=0; 
	}
	gtk_window_set_title(GTK_WINDOW (MainWindow), Seefile->name);
	gtk_text_buffer_get_bounds(Tbuf,&start,&finish);
	gtk_text_buffer_place_cursor(Tbuf,&start);
	scrolltoline(NULL, setline);

	sprintf(messg,"%d %s",CurFS,Seefile->name);
	gtk_button_set_label(GTK_BUTTON(Nbar.current),messg);

	SFsz=strlen(FileCon);

	return 1;
}


gboolean loadlarge (int len) {	 /* called from loadfile() to buffer large files in chunks to display */
	int total=0;
	float inc=1.0f/((float)len/100000.0f), sofar=0.0f; 
	gchar buffer[100001], title[MPTH+128];
	GtkWidget *subwin=gtk_window_new(GTK_WINDOW_POPUP),
		*frame = gtk_frame_new("Loading File"),
		*svbox=gtk_vbox_new(FALSE,0),
		*caption=gtk_label_new(Seefile->name),
		*pbar=gtk_progress_bar_new(),
		*cancelBT = gtk_button_new_with_label("cancel");

	gtk_window_set_transient_for(GTK_WINDOW(subwin),GTK_WINDOW(MainWindow));   
	gtk_window_set_position(GTK_WINDOW(subwin), GTK_WIN_POS_CENTER_ON_PARENT);	
	sprintf(title, "Loading \"%s\"...", Seefile->name);
	gtk_window_set_title(GTK_WINDOW(MainWindow),title);
	
	gtk_container_add(GTK_CONTAINER(subwin),frame);
	gtk_container_add(GTK_CONTAINER(frame),svbox);
	gtk_box_pack_start(GTK_BOX(svbox),caption,FALSE,FALSE,PAD);
	gtk_box_pack_start(GTK_BOX(svbox),pbar,FALSE,FALSE,PAD);
	gtk_box_pack_start(GTK_BOX(svbox),cancelBT,FALSE,FALSE,PAD);
	g_signal_connect(cancelBT, "clicked", G_CALLBACK(destroy_widget), subwin);
	/* so CANCEL will destroy and leave a NULL pointer for the while loop: */
	g_signal_connect(subwin, "destroy", G_CALLBACK(gtk_widget_destroyed), &subwin);	
	g_signal_connect(subwin, "destroy", G_CALLBACK(gtk_widget_destroyed), &pbar);	
	gtk_widget_show_all(subwin);	
	
	FileCon[0]='\0';
	while ((subwin) && (total<len)) {
		total+=fdreadin(Seefile->path,buffer,-1,100000,total);
		if (!(g_utf8_validate((const gchar*)buffer,-1,NULL))) {
			gtk_widget_destroy(subwin);
			error_popup("Can't Validate Text\n(Error #4)");
			clear(1);
			break; 
		}
		gtk_text_buffer_insert(Tbuf,&finish,buffer,-1);
		strcat(FileCon,buffer); 
		sofar+=inc;
		if (!(pbar)) break; 
		if (sofar<=1.0f) gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pbar),sofar);
		while(gtk_events_pending()) gtk_main_iteration();
	}

	if (total==len) { 
		gtk_widget_destroy(subwin); 
		return TRUE; 
	} else return FALSE;
}


int loadlist (char list[27][FLESZ]) {
	int i=0;
	char *line, *bad = NULL, messg[MPTH+1024];
	FILE *fstRO;

	if (Debug>1) fprintf(stderr,"loadlist()...");
	memset(list,0,27*(FLESZ));

	if (!(Config->filelist)) {
		error_popup("You don't have a filelist defined.\nCheck your ~/.seeconfig");
		return 0; 
	}
	if (!(fstRO=fopen(Config->filelist,"r"))) {
		if (errno != ENOENT) {
			sprintf(messg,"Could not open filelist:\n%s",strerror(errno));
			error_popup(messg);
		}
		return 0; 
	}
	while (streamline(fstRO,&line,FLESZ)>0) {
		if (i==27 || !strlen(line)) break;
		if (!strrchr(line,'|')) {
			if (!bad) bad = line;
			else free(line);
			continue;
		}
		strcpy(list[i],line);
		free(line);
		i++;
	}
	fclose(fstRO);

	if (bad) {
		sprintf(messg,"Filelist entry \"%s\" is not formatted correctly.\n",bad);
		strcat(messg,"If you are using a filelist from a version prior to 0.70,\n"
			"see the manpage under FILELIST or delete all contents.");
		error_popup(messg);
		free(bad);
	}

	return i;
}


int loadman (int setline) {	/* called by main(), loadnew() or reload() */
	int len, i;
	char byte, cmmd[MPTH+256], *line, *ptr, messg[NAMELEN+256];
	FILE *pin;
	if (Debug>0) g_print("loadman()...%s (%s)\n",Seefile->path,Seefile->sec);
	
		/* check for page (and get section if necessary) */
	if ((!Seefile->sec[0]) || (Seefile->sec[0]=='*')) sprintf(cmmd,"man -w %s",Seefile->path);   	/* no section OR out-of-tree */
	else sprintf(cmmd,"man -w %s %s",Seefile->sec,Seefile->path);				/* with section */

	if (Debug>2) fprintf(stderr,"\tcmmd: %s\n",cmmd);

	line=ec_malloc(1024);
	if (!(pin = popen(cmmd, "r"))) {
		sprintf(messg, "\"%s\" failed", cmmd);
		error_popup(messg);
		return 0; 
	}
	for (i=0;i<1024;i++) {
		if (fread(&byte,1,1,pin)!=1) break;
		/* possible "blank" line? */
		if ((i==0) && (byte=='\n')) { 
			i=-1; 
			continue; 
		}
		/* just get one line */
		if ((i>0) && (byte=='\n')) { 
			line[i]='\0'; 
			break; 
		}
		else line[i]=byte;
	}

	if (Debug>1) fprintf(stderr,"\t++%s++\n",line);

	/* if man check returns an error, opt out */
	if (pclose(pin)!=0) {
		if (Seefile->sec[0]) sprintf(cmmd,"No manual entry for\n\"man %s %s\"",Seefile->sec,Seefile->path);
		else sprintf(cmmd, "No manual entry for \n\"man %s\"",Seefile->path);
		error_popup(cmmd);
		return 0;
	}

	clear(0);
			/* prep man command */
	if (!Seefile->sec[0]) {
		ptr=strrchr(line,'/');
		ptr[0]=0;
		ptr=strrchr(line,'/');
		sscanf(ptr, "/man%s", Seefile->sec);
		strcat(Seefile->name,"(");
		strcat(Seefile->name,Seefile->sec);
		strcat(Seefile->name,")");
	}
	if (Seefile->sec[0] == '*') sprintf(cmmd, "man %s | col -b -x", Seefile->path);
	else sprintf(cmmd, "man %s %s | col -b -x", Seefile->sec, Seefile->path);
	free(line);	

	sprintf(messg, "Loading %s...", Seefile->name);	
	gtk_window_set_title(GTK_WINDOW (MainWindow), messg);
	while(gtk_events_pending()) gtk_main_iteration();	

		/* now pipe man | col -b output */	
	if ((pin = popen(cmmd, "r")) == NULL) {
		sprintf(messg, "\"%s\" failed", cmmd);
		error_popup(messg);
		clear(1);
		return 0; 
	}
	line=linein(pin);
	len=strexd(0,line);
	while ((line=linein(pin)) != NULL) {
		len=strexd(len,line);
		free(line); 
	}
	pclose(pin);

	if (strlen(EXDline) < 25) {		/* can happen with man pages that used to exist but have been removed */
		sprintf(cmmd, "No manual entry for \n\"man %s\"",Seefile->path);
		error_popup(cmmd);
		return 0;
	}

	FileCon = ec_malloc(len+1);
	strcpy(FileCon,EXDline);
	free(EXDline);
	gtk_text_buffer_set_text(Tbuf,FileCon,len);

	sprintf(messg, "Manual Page for \"%s\"", Seefile->name);
	gtk_window_set_title(GTK_WINDOW (MainWindow), messg);
	gtk_text_buffer_get_bounds(Tbuf,&start,&finish);
	gtk_text_buffer_place_cursor(Tbuf,&start);
	scrolltoline(NULL,setline);
	
		/* load bookmarks & mark-up */ 	
	if (Config->seedata) dobmks();
	else { 
		bookmarks=ec_malloc(sizeof(int)); 
		bookmarks[0]=0; 
	}

	sprintf(messg,"%d %s",CurFS,Seefile->name);
	gtk_button_set_label(GTK_BUTTON(Nbar.current),messg);

	return 1;
}


int loadnew (char *file, char *sec, int lastline) {  /* called by exeFSlisting(), DnDreceive(), or takecall() */
	char messg[256];
	int curpos, success;
	seefile *old = NULL;

	if (strlen(file)>MPTH-1) { 
		gtk_window_set_title(GTK_WINDOW (MainWindow), "Filename Too Long"); 
		return -1; 
	}

	if (watchSW) watchfile(); 
	if (LNsw) numberlines();

	if ((FileCon) && (Config->bConfirm == 0)) { 
		if (file[0] != '/') sprintf(messg, "Load manual page for\n%s?",file);
		else sprintf(messg, "Load \n%s?", file);
		if (confirm_popup ("SEE Request",messg) == -6) return -1;
	}

	if (Debug>0) g_print("loadnew() %s \"%s\" %d\n",file,sec,lastline);
	gtk_window_present(GTK_WINDOW(MainWindow));

	if (Seefile) {
		old = SFcopy(Seefile);
		free(Seefile);
	}

	if (sec) Seefile = initSF(file,'M',sec);
	else Seefile = initSF(file,'R',sec);
	if (!Seefile) {
		Seefile = old;
		free(old);
		return 27;
	}

	curpos = cursorline(); 	/* this is for the Filelist */

	if (sec || file[0] != '/') success = loadman(lastline);
	else success = loadfile(lastline);

	if (!success) {
		free(Seefile);
		Seefile = old;
		return 0;
	}

	if (Config->filelist) update_filelist(old,curpos); 
	if (old) free(old);

	return 0;
}


void mainmenu () {
	GtkWidget *item1, *item2, *item3, *item4, *item5, *item6, *item7, *item8, *item9, *item17, *item18, 
		*item10, *item11, *item12, *item13, *item14, *item15, *item16, *item19, *sp1, *sp2, *notshown;
	GtkAccelGroup *keymacros = gtk_accel_group_new();
	gtk_window_add_accel_group(GTK_WINDOW(MainWindow), keymacros);
	gtk_menu_set_accel_group(GTK_MENU(MMenu),keymacros);	

	notshown = gtk_menu_item_new();
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), notshown);
	g_signal_connect(G_OBJECT(notshown), "activate",G_CALLBACK(grabfocus), NULL);
	gtk_widget_add_accelerator(notshown, "activate", keymacros, GDK_slash, GDK_CONTROL_MASK, 0);

	item12 = gtk_menu_item_new_with_label("file list");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item12);
	g_signal_connect(G_OBJECT(item12), "activate",G_CALLBACK(showlist), NULL);
	gtk_widget_add_accelerator(item12, "activate", keymacros, GDK_f, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
	
	if (Config->seedata) {
		item6 = gtk_menu_item_new_with_label("see bookmarks");
		gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item6);
		g_signal_connect(G_OBJECT(item6), "activate",G_CALLBACK(bookmark_list), NULL);
		gtk_widget_add_accelerator(item6, "activate", keymacros, GDK_s, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

		item5 = gtk_menu_item_new_with_label("place bookmark");
		gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item5);
		g_signal_connect(G_OBJECT(item5), "activate",G_CALLBACK(placebmk), NULL);
		gtk_widget_add_accelerator(item5, "activate", keymacros, GDK_m, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
	}
	item4 = gtk_menu_item_new_with_label("reload");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item4);
	g_signal_connect(G_OBJECT(item4), "activate",G_CALLBACK(reload), NULL);
	gtk_widget_add_accelerator(item4, "activate", keymacros, GDK_l, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

	item17 = gtk_menu_item_new_with_label("copy selection");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item17);
	g_signal_connect(G_OBJECT(item17), "activate",G_CALLBACK(copytoX), 0);
	gtk_widget_add_accelerator(item17, "activate", keymacros, GDK_c, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
	
	item18 = gtk_menu_item_new_with_label("copy all");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item18);
	g_signal_connect(G_OBJECT(item18), "activate",G_CALLBACK(copytoX), (int*)1);
	gtk_widget_add_accelerator(item18, "activate", keymacros, GDK_c, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
	
	item11 = gtk_menu_item_new_with_label("apropos search");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item11);
	g_signal_connect(G_OBJECT(item11), "activate",G_CALLBACK(apropos), NULL);
	gtk_widget_add_accelerator(item11, "activate", keymacros, GDK_a, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

	item13 = gtk_menu_item_new_with_label("previous found");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item13);
	g_signal_connect(G_OBJECT(item13), "activate",G_CALLBACK(cursor_tofound), (int*)1);
	gtk_widget_add_accelerator(item13, "activate", keymacros, GDK_p, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

	item14 = gtk_menu_item_new_with_label("next find");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item14);
	g_signal_connect(G_OBJECT(item14), "activate",G_CALLBACK(cursor_tofound), 0);
	gtk_widget_add_accelerator(item14, "activate", keymacros, GDK_n, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

	item19 = gtk_menu_item_new_with_label("goto cursor");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item19);
	g_signal_connect(G_OBJECT(item19), "activate",G_CALLBACK(gotoCursor), NULL);
	gtk_widget_add_accelerator(item19 , "activate", keymacros, GDK_g, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

	item3 = gtk_menu_item_new_with_label("(un)number lines");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item3);
	g_signal_connect(G_OBJECT(item3), "activate",G_CALLBACK(numberlines), NULL);
	gtk_widget_add_accelerator(item3, "activate", keymacros, GDK_3, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

	item7 = gtk_menu_item_new_with_label("bold blue");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item7);
	g_signal_connect(G_OBJECT(item7), "activate",G_CALLBACK(highlight), "boldblue");
	gtk_widget_add_accelerator(item7, "activate", keymacros, GDK_h, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

	item8 = gtk_menu_item_new_with_label("italic red");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item8);
	g_signal_connect(G_OBJECT(item8), "activate",G_CALLBACK(highlight), "italred");
	gtk_widget_add_accelerator(item8, "activate", keymacros, GDK_h, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);

	item9 = gtk_menu_item_new_with_label("untag");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item9);
	g_signal_connect(G_OBJECT(item9), "activate",G_CALLBACK(removetags), NULL);
	gtk_widget_add_accelerator(item9, "activate", keymacros, GDK_u, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
	
	item10 = gtk_menu_item_new_with_label("wrap mode");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item10);
	g_signal_connect(G_OBJECT(item10), "activate",G_CALLBACK(setwrapmode), NULL);
	gtk_widget_add_accelerator(item10, "activate", keymacros, GDK_w, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

	item1 = gtk_menu_item_new_with_label("send to editor");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item1);
	g_signal_connect(G_OBJECT(item1), "activate",G_CALLBACK(toedit), NULL);
	gtk_widget_add_accelerator(item1, "activate", keymacros, GDK_e, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
	
	if (Config->copyto && strcmp(Config->copyto,"Could not open!")) {
		sp1 = gtk_menu_item_new_with_label("copy out");
		gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), sp1);
		g_signal_connect(G_OBJECT(sp1), "activate",G_CALLBACK(file_out),NULL);
		gtk_widget_add_accelerator(sp1, "activate", keymacros, GDK_o, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
		
		sp2 = gtk_menu_item_new_with_label("execute");
		gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), sp2);
		g_signal_connect(G_OBJECT(sp2), "activate",G_CALLBACK(exec_proc),NULL);
		gtk_widget_add_accelerator(sp2, "activate", keymacros, GDK_x, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
	}

	item15 = gtk_menu_item_new_with_label("reconfigure");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item15);
	g_signal_connect(G_OBJECT(item15), "activate",G_CALLBACK(reconfigure),NULL);
	gtk_widget_add_accelerator(item15, "activate", keymacros, GDK_F2, 0, GTK_ACCEL_VISIBLE);

	item16 = gtk_menu_item_new_with_label("help");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item16);
	g_signal_connect(G_OBJECT(item16), "activate",G_CALLBACK(help),NULL);
	gtk_widget_add_accelerator(item16, "activate", keymacros, GDK_F1, 0, GTK_ACCEL_VISIBLE);
	
	item2 = gtk_menu_item_new_with_label("quit");
	gtk_menu_shell_append(GTK_MENU_SHELL(MMenu), item2);
	g_signal_connect(G_OBJECT(item2), "activate",G_CALLBACK(handlequit), NULL);
	gtk_widget_add_accelerator(item2, "activate", keymacros, GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
	
	gtk_widget_show_all(MMenu);
}	
	

gboolean mousevent (GtkWidget *widget, GdkEventButton *MoBt, gpointer *data) {
	GtkTextIter togo, there;  
	gchar *word, *sec;
	char fname[4096];
	GdkCursor *text=gdk_cursor_new(GDK_CROSSHAIR);

	if (MoBt->button == 3) {
			gtk_menu_popup(GTK_MENU(MMenu),NULL,NULL,NULL,NULL,0,GDK_CURRENT_TIME);
			return TRUE; 
	}
	
	if (MoBt->button == 1) { 
		if (MoBt->type != GDK_2BUTTON_PRESS) return FALSE;
		gtk_text_buffer_get_iter_at_mark (Tbuf, &there, gtk_text_buffer_get_insert (Tbuf));
		if ((gtk_text_iter_has_tag(&there,hlink)) != TRUE) return FALSE;
		if ((gtk_text_iter_begins_tag(&there,hlink)) != TRUE) gtk_text_iter_backward_to_tag_toggle(&there,hlink);
		togo=there;
		gtk_text_iter_forward_to_tag_toggle(&togo,hlink);
		word=gtk_text_buffer_get_text(Tbuf,&there,&togo,FALSE);

				/* the if's are for the "configuration" hyperlinks */
		if (strcmp(word,"manual page")==0) {
			loadnew("seetxt","1",0);
			return TRUE; 
		}
		if (strncmp(word,"the example",11)==0) {
			sprintf(fname,"%s/.seeconfig",SDIR);
			loadnew(fname,NULL,0); 
			return TRUE; 
		}
		if (word[0]=='/') {
			strncpy(fname,word,4095);
			loadnew(fname,NULL,0);
			return TRUE; 
		}

		gtk_text_iter_forward_find_char(&there,(GtkTextCharPredicate)testuni,(short int*)40,NULL);
		gtk_text_iter_forward_find_char(&togo,(GtkTextCharPredicate)testuni,(short int*)41,NULL);
		sec=gtk_text_buffer_get_text(Tbuf,&there,&togo,FALSE);
		gtk_text_buffer_set_text(Tbuf," ",1);
		loadnew(word,&sec[1],0);
		free(word); 
		free(sec);
		gdk_window_set_cursor (gtk_text_view_get_window (GTK_TEXT_VIEW(TxT), GTK_TEXT_WINDOW_TEXT), text);   
		gdk_cursor_unref(text);	
		return TRUE; 
	}
	return FALSE; 
}	


gboolean nomax (GtkWidget *widget, GdkEventWindowState *ptr, gpointer *data) {
	unsigned char newstate=ptr->new_window_state;
	if (newstate&=4) gtk_window_unmaximize(GTK_WINDOW(MainWindow));
	if (newstate&=16) gtk_window_unfullscreen(GTK_WINDOW(MainWindow));
	return TRUE;
}


int nextfind (char *haystack, char *needle) {  /* used by searchlight() */
	int nlen=strlen(needle), hslen=strlen(haystack), i, ii;
	char copy[nlen+1], UC=0;
	
	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(CaseTgl))) UC=1;
	if (UC==0) for (i=0;i<nlen;i++) copy[i]=tolower(needle[i]); 	
	else strcpy(copy,needle);
	if (Debug>2) fprintf(stderr,"nextfind() %ld, needle=%s\n",haystack-FileCon,needle);
	
	for (i=0;i<hslen;i++) {
		if (((UC==0) && (tolower(haystack[i])==copy[0])) || ((UC==1) && haystack[i]==copy[0])) {
			for (ii=1;ii<nlen;ii++) {
				if (((UC==0) && (tolower(haystack[i+ii])!=copy[ii])) || ((UC==1) && haystack[i+ii]!=copy[ii])) 
					break;
				else if (ii==nlen-1) return i; 
			}
		}
	}
	return -1;
}
	

void numberlines () {
	GtkTextIter lstrt, Nend; 
	gchar lnum[6];
	int i, nlen, nofl;
	if (!(FileCon)) return;
	nofl = buflen(FileCon);
	if (nofl>99999) return;
	if (LNsw==0) {	LNsw=1;
		for (i=0; i<nofl; i++) {
			sprintf(lnum, "%5d ", i+1); 
			nlen = strlen(lnum);
			gtk_text_buffer_get_iter_at_line(Tbuf,&lstrt,i);
			gtk_text_buffer_insert_with_tags(Tbuf,&lstrt,lnum,nlen,lnnm,NULL);
	}} else { LNsw=0;
		for (i=0; i<nofl; i++) {
			gtk_text_buffer_get_iter_at_line(Tbuf,&lstrt,i);
			gtk_text_buffer_get_iter_at_line_offset(Tbuf,&Nend,i,5);
			gtk_text_buffer_delete(Tbuf,&lstrt,&Nend);
		}
	}
}


void placebmk () {
	int num = bookmarks[0]+1, ln=cursorline(),i;
	char messg[25];
	if (!(Seefile)) return;
	for (i=1;i<=bookmarks[0];i++) if (bookmarks[i]==ln+1) return; 
	bookmarks=realloc(bookmarks, num+1);
	bookmarks[num]=ln+1; bookmarks[0]++;
	sprintf(messg, "Bookmark set at line %d", ln+1);
	gtk_window_set_title(GTK_WINDOW (MainWindow), messg);
	savebmks(NULL,NULL);
}


forconfig *reconfigure(GtkWidget *ignored) {	/* called from main() or mainmenu() */
	int DBL, i, tmp, servon = 0;
	PangoFontDescription *Tfont;
	FILE *fstRO;
	DIR *test;
	const gchar *homedir = g_get_home_dir();
	char configfile[MPTH], *tok, *line;
	time_t now=time(NULL);
	struct tm *tp=localtime(&now);
	pid_t pid=getpid();
	GdkColor textback;
	seefile *old;
	forconfig *r = ec_malloc(sizeof(forconfig));
	memset(r,0,sizeof(forconfig));

	if (Gio && Config && Config->sock[0]) {
		tglServer(NULL,-1);		/* Config->sock may change */
		servon = 1;
	}


/* free old config */
	if (ignored && Config) {
		if (Config->txtfnt) free(Config->txtfnt); 
		if (Config->tbcolor) free((char*)Config->tbcolor);
		if (Config->filelist) free(Config->filelist);
		if (Config->editor) free(Config->editor);
		if (Config->copyto) free(Config->copyto);
		if (Config->seedata) free(Config->seedata);
		free(Config);
	}

/* defaults */
	r->width = 700;
	r->height = 700;
	r->wrap = 'W';
	r->bConfirm = 0;
	r->bRedirect = 1;
	r->filelist = ec_malloc(strlen(SDIR)+16);
	strcpy(r->filelist,SDIR);
	strcat(r->filelist,"/filelist");
	r->editor = strdup("gedit"); 
	if (strlen(homedir) <= 97) sprintf(r->sock, "%s/.seesock",homedir);
	tmp = file_check(r->sock);
	if (!(tmp == -2 || tmp == 49152)) r->sock[0] = '\0';
	r->seedata = ec_malloc(strlen(homedir)+10);
	sprintf(r->seedata,"%s/.seedata",homedir);

/* supersession by configfile */
	sprintf(configfile,"%s/.seeconfig",homedir);
	if ((fstRO = fopen(configfile, "r"))) {
		while ((line = linein(fstRO)) != NULL) {
			tok = strtok(line,":");
			if (!strcasecmp(tok,"text font")) {
				if ((tok = strtok(NULL,":\n"))) {
					while (tok[0]==' ') tok++;
					r->txtfnt = ec_malloc(strlen(tok)+1);
					strcpy(r->txtfnt,tok); 
				}
			} else if (!strcasecmp(tok,"watch interval")) {
				if ((tok=strtok(NULL,":\n"))) r->watchtime = strtol((const char*)tok,NULL,10);
			} else if (!strcasecmp(tok,"dimensions")) {
				if ((tok=strtok(NULL,":\n"))) {
					tmp=sscanf(tok,"%d%[x ]%d",&i,configfile,&DBL);		
					if (tmp==0) continue;
					if ((i>0) && (i<9999)) r->width=i;
					if (tmp==1) continue;
					if ((DBL>0) && (DBL<9999)) r->height=DBL;
				}
			} else if (!strcasecmp(tok,"seedata")) {
				if ((tok=strtok(NULL,": \n"))) {
					tmp = strlen(tok);
					if (tmp>MPTH) continue;
					free(r->seedata);
					r->seedata=ec_malloc(tmp+1);
					strcpy(r->seedata,tok);
				}
			} else if (!strcasecmp(tok,"filelist")) {
				if ((tok=strtok(NULL,": \n"))) {
					free(r->filelist);
					r->filelist = strdup(tok);
				}
			} else if (!strncmp(tok,"no confirm",10)) r->bConfirm = 1;
			else if (!strcasecmp(tok,"seesocket")) {
				if (!(tok=strtok(NULL,": \n"))) continue;
				if (strlen(tok) > 107) continue;
				tmp = file_check(tok);
				if (!(tmp == -2 || tmp == 49152)) continue;
				strncpy(r->sock,tok,107);
			} else if (!strcasecmp(tok,"Debuglog")) {   /* development use */
				if (!(tok=strtok(NULL,": \n"))) continue;
				if ((DBL=open(tok,O_CREAT|O_WRONLY,S_IREAD|S_IWRITE))<3) continue;
				printf("switching stdout & stderr to %s",tok);
				dup2(DBL,1); 
				dup2(DBL,2); /* stdout, stderr to file */
				lseek(DBL,0,SEEK_END);
				printf("\n******* pid %d -- %d:%02d *******\n",pid,tp->tm_hour,tp->tm_min);
			} else if (!strcasecmp(tok,"editor")) {
				if (!(tok=strtok(NULL,"\n"))) continue;
				while (tok[0]==' ') tok++;
				free(r->editor);
				r->editor = strdup(tok); 
			} else if (!strcasecmp(tok,"background")) {
				if (!(tok=strtok(NULL,": \n"))) continue;
				r->tbcolor=ec_malloc(strlen(tok)+1);
				strcpy((char*)r->tbcolor,tok); 
			} else if (!strcasecmp(tok,"tail at")) {
				if (!(tok=strtok(NULL,": \n"))) continue;
				r->tailat=strtol((const char*)tok,NULL,10);
			} else if (!strcasecmp(tok,"wrap")) {
				if (!(tok=strtok(NULL,": \n"))) continue;
				if (!strcasecmp(tok,"exact")) r->wrap = 'E';
				else if (!strcasecmp(tok,"word")) r->wrap = 'W';
				else if (!strcasecmp(tok,"none")) r->wrap = 'N';
			} else if (!strcasecmp(tok,"copy to")) {
				if (!(tok=strtok(NULL,": \n"))) continue;
				if (!(test = opendir((const char*)tok))) {
					r->copyto = strdup("Could not open!");
					continue;
				}
				closedir(test);	
				r->copyto = strdup(tok);
			} else if (!strncmp(tok,"no redirect",11)) r->bRedirect=0;
			free(line); 
		}
		fclose(fstRO);
	} else {
		sprintf(configfile,"No %s/.seeconfig found.",homedir);	
		puts(configfile);
		if (ignored) gtk_window_set_title(GTK_WINDOW (MainWindow), configfile);
	}

	if (!ignored) return r;  /* when called from main(), nb: GTK is not running yet! */

/* when called from F2/mainmenu() */
	if (r->txtfnt) { 
		Tfont = pango_font_description_from_string(r->txtfnt);
		gtk_widget_modify_font(TxT,Tfont);
		pango_font_description_free(Tfont); 
	}
	gtk_widget_set_size_request(GTK_WIDGET (TxT), r->width, r->height);
	if (r->tbcolor) {
		if (gdk_color_parse(r->tbcolor, &textback)) gtk_widget_modify_base(TxT, GTK_STATE_NORMAL, &textback);
	}
	changewrapmode(NULL,r->wrap);
	Config = r;	/* Can't set this via return for widget callback */

	if (Seefile) { 		/* save our position in current file if necessary */
		old = SFcopy(Seefile);
		free(Seefile);
		Seefile = NULL;
		i=cursorline();
		update_filelist(old, i);
		free(old);
	}

	clear(1);
	showconfig(r);
	if (servon && Config->sock[0]) tglServer(NULL,0);

	return NULL;
}


void removetags (GtkWidget *widget, gpointer *data) {
	GtkTextIter Ts, Te;
	gtk_text_buffer_get_selection_bounds(Tbuf, &Ts, &Te);
	gtk_text_buffer_remove_tag(Tbuf, boldblue, &Ts, &Te);
	gtk_text_buffer_remove_tag(Tbuf, italred, &Ts, &Te);
	savebmks(NULL,NULL);
}


gboolean reload () {	/* called from watchfile() or mainmenu() */
	gint lnum;

	if (!(Seefile)) { 
		if (watchSW) watchfile(); /* toggle left on */
		return TRUE; 
	}

	if (Debug>1) { g_print("reload() %s\n", Seefile->path); fflush(stdout); }

	if ((Seefile->type == 'R') && (filelen(Seefile->path)>Config->tailat)) {
		gtk_window_set_title(GTK_WINDOW (MainWindow), "large file: tailed, not reloaded");
		return tailfile();
	}

	lnum=cursorline(); 

	gtk_text_buffer_get_bounds(Tbuf,&start,&finish);
	gtk_text_buffer_delete(Tbuf,&start,&finish);

	if (Seefile->type == 'R') loadfile(lnum);
	else loadman(lnum);

	if (watchSW==0) return FALSE;
	return TRUE;
}


void rescanFileList () {
	GtkTreeView *tv = gtk_tree_selection_get_tree_view(Nbar.select);
	GtkListStore *ls = (GtkListStore*)gtk_tree_view_get_model(tv);
	gtk_list_store_clear(ls);
	list2view(ls);
}


void savebmks (GtkWidget *widget, GtkWidget *subwin) {	/* called from bookmark_list() or placebmk() */
	int i;
	FILE *fstRO, *fstW; 
	const gchar *homedir=g_get_home_dir();
	char *line, tmp[strlen(homedir)+8], seeline[8192], tagline[4096], *sptr, num[7];

	if (subwin) gtk_widget_destroy(subwin);  /* close bookmarks window */
	if (!Seefile) return;

	if (Seefile->type == 'R') sptr = Seefile->path;
	else sptr = Seefile->name;
	sprintf(tmp,"%s/.SeeTP",homedir);	/* use homedir rather than /tmp to not intefere with other users */	
		
		/* some possible problems */
	if (!Config->seedata) {
		gtk_window_set_title(GTK_WINDOW (MainWindow), "No personal \"seedata\" file, your bookmarks cannot be saved");
		return;
	}
	if ((fstRO = fopen(Config->seedata, "r")) == NULL) {
		gtk_window_set_title(GTK_WINDOW (MainWindow), "Could not open your \"seedata\" file!");
		return;
	}
	if ((fstW = fopen(tmp, "w")) == NULL) {
		gtk_window_set_title(GTK_WINDOW (MainWindow), "Could not create temp file, your bookmarks will not be saved");
		fclose(fstRO);
		return;
	}
		
		/* write tmp file */
	while ((line=linein(fstRO)) != NULL) {
		if (line[0]=='\n') { 
			free(line); 
			continue; 
		}
		if (strncmp(line,sptr,strlen(sptr)) != 0) fprintf(fstW,"%s",line);
		free(line);
	}
	fclose(fstRO);
		
		/* compose new line using global integer array "bookmarks" */
	sprintf(seeline, "%s*%d",sptr,bookmarks[0]);	/* bookmarks[0] is the number of bookmarks */
	if (bookmarks[0]>0) for (i=1; i<=bookmarks[0]; i++) {
		if (bookmarks[i]==0) continue;	/* was deleted */
		sprintf(num,"*%d",bookmarks[i]);
		strcat(seeline,num); 
	}
	if (Debug>0) g_print("savebmks()...\n"); 
	if (LNsw == 1) {
		numberlines(); 
		LNsw=1;
	}
	if (getagcoords(boldblue,tagline)>0) strcat(seeline,tagline);
	if (getagcoords(italred,tagline)>0) strcat(seeline,tagline);
	if (LNsw == 1) {
		LNsw=0;
		numberlines();
	}
		
		/* write new seedata */
	fprintf(fstW,"%s\n",seeline);
	fclose(fstW);
	if ((copytmp(tmp,Config->seedata)) != 0) error_popup("in bookmarks(): copytmp()\nfclose or unlink failed");
	gtk_window_set_title(GTK_WINDOW (MainWindow), "Bookmarks saved");
}


void scrollToIter (GtkTextIter *iter) {
/* used by gotoCursor() */
	GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(Scroll)),
		*hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(Scroll));
	GdkRectangle goal;
	gdouble x, y,
		hpage = gtk_adjustment_get_page_size(hadj),
		vpage = gtk_adjustment_get_page_size(vadj),
		maxx = gtk_adjustment_get_upper(hadj) - hpage,
		maxy = gtk_adjustment_get_upper(vadj) - vpage;

	gtk_text_view_get_iter_location(GTK_TEXT_VIEW(TxT),iter,&goal);
	x = goal.x;
	y = goal.y - vpage/2;	/* center vertically */

	if (x<hpage-50.0) x = 0;		/* align left if possible */
	else if (x>maxx) x = maxx;
	if (y>maxy) y = maxy;

	gtk_adjustment_set_value(hadj, x);
	gtk_adjustment_set_value(vadj, y);
}


void scrolltoline (GtkWidget *widget, gint line) {
	GtkTextIter where;
	GtkTextMark *goal;
	gtk_text_buffer_get_iter_at_line(Tbuf,&where,line);
	goal=gtk_text_buffer_create_mark(Tbuf,"goal",&where,FALSE);
	gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(TxT),GTK_TEXT_MARK(goal),0.0,TRUE,0.0,0.5);
	gtk_text_buffer_place_cursor(Tbuf,&where); 	
}


void searchlight () {	/* find and hilite, called from main() or FentKpress() */
	GdkDisplay *display = gdk_display_get_default();
	GtkTextIter begin, end, togo;
	char tonumLbl[7], title[128];
	char *ptr, *FCend, flag=0;
	const gchar *fthis = gtk_entry_get_text(GTK_ENTRY(Fent));
	int slen=strlen(fthis), x=0, i, sw=0, cc=0, tmp;	
	if (slen == 0) return;

	gtk_text_buffer_get_bounds(Tbuf,&start,&finish);
	if (!(FileCon)) {	/* if not an actual file in buffer */
		gtk_text_buffer_get_bounds(Tbuf,&start,&finish);
		FileCon=gtk_text_buffer_get_text(Tbuf,&start,&finish,FALSE); 
		flag=1; 
	}
	ptr=FileCon;
	FCend=FileCon+strlen(FileCon);

	if (Debug>0) g_print("searchlight()...\n");

	addtohistory((char*)fthis);
	
	strcpy(title, "Searching for ");
	strncat(title, fthis, 112);
	gtk_window_set_title(GTK_WINDOW(MainWindow),title);
	gdk_display_flush(display);
	
	if (LNsw==1) {	/* get around the line numbers */
		numberlines();	
		sw = 1;
	}
	gtk_text_buffer_remove_tag (Tbuf, hlite, &start, &finish);	
			
		/* if "push" toggled */
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pushTgl))) {
		gtk_text_buffer_remove_tag (Tbuf, pushed, &start, &finish);	
		for (i=0; i<=(totalF*2)-1; i+=2) {
			gtk_text_buffer_get_iter_at_offset(Tbuf,&begin,TF[i]);
			gtk_text_buffer_get_iter_at_offset(Tbuf,&end,TF[i+1]);
			gtk_text_buffer_apply_tag(Tbuf, pushed, &begin, &end);
		}
	}
		/* the search (regexp or normal) */
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(regxTgl))) {
		while ((slen=regexp(ptr,(char *)fthis))>0) {
			TF[x] = utfadj(FileCon,ptr-FileCon+rgxp.bgn);
			TF[x+1] = TF[x] + slen;
			ptr+=rgxp.end;  /* the UNADJUSTED place */
			if (Debug>2) g_print("+%d=%p\t",rgxp.end,ptr);
			x += 2; 
			if ((x<1000) && ((x%100)==0)) {
				sprintf(tonumLbl,"%d",x/2);
				gtk_label_set_text(GTK_LABEL(numLbl), tonumLbl);			
				while (gtk_events_pending()) gtk_main_iteration(); } 
			if ((x>=1000) && ((x%200)==0)) {
				sprintf(tonumLbl,"%d",x/2);
				gtk_label_set_text(GTK_LABEL(numLbl), tonumLbl);			
				while (gtk_events_pending()) gtk_main_iteration(); } 
			if ((x>=198000) || (ptr>=FCend)) break; }
	} else { 
		while ((tmp=nextfind(ptr, (char*)fthis))>-1) {
			TF[x]=utfadj(FileCon,tmp+cc);
			TF[x+1] = TF[x] + slen;
			cc+=tmp+slen;
			ptr=&FileCon[cc];		
			x += 2; 
			if ((x<1000) && ((x%100)==0)) {
				sprintf(tonumLbl,"%d",x/2);
				gtk_label_set_text(GTK_LABEL(numLbl), tonumLbl);			
				while (gtk_events_pending()) gtk_main_iteration(); } 
			if ((x>=1000) && ((x%200)==0)) {
				sprintf(tonumLbl,"%d",x/2);
				gtk_label_set_text(GTK_LABEL(numLbl), tonumLbl);			
				while (gtk_events_pending()) gtk_main_iteration(); } 
			if ((x>=198000) || (ptr>=FCend)) break; 
		}
	}
	if (flag) { 
		free(FileCon); 
		FileCon=NULL; 
	}
		/* add visible tags */
	for (i=0; i<x; i+=2) {
		gtk_text_buffer_get_iter_at_offset(Tbuf,&begin,TF[i]);
		gtk_text_buffer_get_iter_at_offset(Tbuf,&end,TF[i+1]);
		gtk_text_buffer_apply_tag(Tbuf, hlite, &begin, &end);
	}
	totalF = x/2;	
	sprintf(tonumLbl,"%d",totalF);
	gtk_label_set_text(GTK_LABEL(numLbl), tonumLbl);			
	if (totalF > 0) { 
		gtk_text_buffer_get_iter_at_offset(Tbuf,&togo,TF[0]);
		gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(TxT),&togo,0.0,TRUE,0.5,0.5);
		gtk_text_buffer_place_cursor(Tbuf,&togo); 
		//scrollToIter(&togo);
	}
	if (sw == 1) numberlines();
	gtk_window_set_title(GTK_WINDOW(MainWindow),Seefile->name);
}


void setNULL (GtkWidget *ignored, gpointer *ptr) { ptr = NULL; }

void setFL_selected () {
	gchar str[4];
	GtkTreePath *path;

	sprintf(str,"%d",CurFS-1);
	path = gtk_tree_path_new_from_string(str);
	gtk_tree_selection_select_path(Nbar.select,path);
}

void setwrapmode () {
	GtkWidget *sbwin = gtk_window_new(GTK_WINDOW_POPUP), 
		*frame = gtk_frame_new("Wrap Mode"),
		*sbVbox = gtk_vbox_new (FALSE, 0),
		*CloseBT = gtk_button_new_with_label("close"),
		*rbutt = gtk_radio_button_new_with_label (NULL, "no wrap");
	GSList *RBgroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutt));
	GtkWrapMode current = gtk_text_view_get_wrap_mode(GTK_TEXT_VIEW(TxT));

	gtk_window_set_transient_for(GTK_WINDOW(sbwin),GTK_WINDOW(MainWindow));
	gtk_window_set_position(GTK_WINDOW(sbwin), GTK_WIN_POS_CENTER_ON_PARENT);
	gtk_container_add(GTK_CONTAINER (sbwin), frame);
	gtk_container_add(GTK_CONTAINER (frame), sbVbox);
	
	if (current == GTK_WRAP_NONE) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rbutt),TRUE);
	g_signal_connect(rbutt, "toggled", G_CALLBACK(changewrapmode), (gpointer)'N');
	gtk_box_pack_start(GTK_BOX (sbVbox), rbutt, FALSE, FALSE, 0);

		/* no need for seperate widgets for each radio button */
	rbutt = gtk_radio_button_new_with_label (RBgroup, "wrap on word");
	RBgroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutt));
	if (current == GTK_WRAP_WORD) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rbutt),TRUE);
	g_signal_connect(rbutt, "toggled", G_CALLBACK(changewrapmode), (gpointer)'W');
	gtk_box_pack_start(GTK_BOX (sbVbox), rbutt, FALSE, FALSE, 0);
	
	rbutt = gtk_radio_button_new_with_label (RBgroup, "exact wrap");
	RBgroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutt));
	if (current == GTK_WRAP_CHAR) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rbutt),TRUE);
	g_signal_connect(rbutt, "toggled", G_CALLBACK(changewrapmode), (gpointer)'E');
	gtk_box_pack_start(GTK_BOX (sbVbox), rbutt, FALSE, FALSE, 0);

	gtk_box_pack_end(GTK_BOX (sbVbox), CloseBT, FALSE, FALSE, 0);
	g_signal_connect(CloseBT, "clicked", G_CALLBACK(destroy_widget), GTK_WIDGET(sbwin));
	
	gtk_widget_show_all(sbwin);	
}


seefile *SFcopy (seefile *orig) {
	seefile *r = ec_malloc(sizeof(seefile));
	memcpy(r,orig,sizeof(seefile));
	return r;
}


void shiftFilelist (GtkWidget *ignored, int val) {
	char list[27][FLESZ];
	int len = loadlist(list);

	if (!len || !Seefile) return;

	/* val will be 1 or -1 */
	if (val == 1) { if (CurFS == len) return; }
	else if (CurFS == 1) return;

	CurFS += val;
	exeFSlisting(list[CurFS-1]);
	if (Nbar.select) setFL_selected();
}


void showconfig(forconfig *cfg) {	/* called from main() or reconfigure () */
	gchar text[1024];
	GtkTextIter iter;
//	const gchar *homedir=g_get_home_dir();
	gtk_text_buffer_get_end_iter(Tbuf,&iter);
	sprintf(text, "\n___Configuration___\n\n"); 
	gtk_text_buffer_insert_with_tags(Tbuf,&iter,(const gchar*)text,-1,ctitle,NULL);
	
	if (cfg->txtfnt) { 
		sprintf(text, "\n\ttext area font: %s\n",cfg->txtfnt);
	}
	else strcpy(text, "\n\tno text font selected.\n");
	gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
	
	sprintf(text, "\twatch interval set to %d seconds\n",cfg->watchtime);
	gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
	
	sprintf(text,"\ttext area width=%dpx height=%dpx\n\n",cfg->width,cfg->height);
	gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
	
	if (cfg->seedata) {
		sprintf(text, "\t\"seedata\" file path ");
		gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
		strcpy(text,cfg->seedata);
		gtk_text_buffer_insert_with_tags(Tbuf,&iter,(const gchar*)text,-1,hlink,NULL); 
	} else {
		sprintf(text, "\t...no \"seedata\" file in use."); 
		gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1); 
	}

	if (cfg->filelist) {
		sprintf(text, "\n\tfilelist path ");
		gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
		strcpy(text,cfg->filelist);
		gtk_text_buffer_insert_with_tags(Tbuf,&iter,(const gchar*)text,-1,hlink,NULL); 
	} else {
		sprintf(text, "\n\t...no filelist in use.\n");
		gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1); 
	}

	if (cfg->sock[0]) {
		sprintf(text, "\n\t\"seesocket\" location: %s\n\n",cfg->sock);
		gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
	} else {
		sprintf(text,
			"\n\nSORRY:Your home directory path, or the path you provided "
			"for the \"seesocket\", is too long, or a regular file exists with the same name.  Local "
			"sockets must have a name, including the fullpath, shorter than 108 characters.\n\n"
		);
		gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
	}

	sprintf(text,"\tYour editor command: \"%s\"\n",cfg->editor);
	gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);

	if (cfg->copyto) {
		if (!strcmp(cfg->copyto,"Could not open!")) {
			sprintf(text, "\tSORRY: Could not open your \"copy to\" directory.\n");
			gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
		} else {
			sprintf(text, "\n\tYour \"copy to\" directory: %s\n", Config->copyto);
			gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
			if (!cfg->bRedirect) { 
				sprintf(text,"\tstderr redirection turned off.\n");
				gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1); 
			}
		}
	} else {
		strcpy(text, "\n\tNo \"copy to\" directory defined.\n");
		gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
	}
	
	sprintf(text,"\n\t\"Tail at\" boundary: %d bytes\n",Config->tailat);
	gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);

	if (cfg->bConfirm) sprintf(text,"\n\t\"Confirm load\" turned off.\n");
	else sprintf(text,"\n\tConfirm load is on.\n");
	gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
	
	sprintf(text,"\n  For more information, see under \n  \"CONFIGURATION\" in the ");
	gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
	sprintf(text,"manual page");
	gtk_text_buffer_insert_with_tags(Tbuf,&iter,(const gchar*)text,-1,hlink,NULL);
	sprintf(text,".\n");
	gtk_text_buffer_insert(Tbuf,&iter,(const gchar*)text,-1);
}


void showlist() {
	GtkListStore *liststore = gtk_list_store_new(1,G_TYPE_STRING);
	GtkWidget *sbwin = gtk_window_new(GTK_WINDOW_TOPLEVEL), 
		*frame = gtk_frame_new("Current Filelist"),
		*flab = gtk_frame_get_label_widget(GTK_FRAME(frame)),
		*sbox = gtk_vbox_new (FALSE, 0),
		*FLLV = gtk_tree_view_new_with_model(GTK_TREE_MODEL(liststore)),
		*cancelBT = gtk_button_new_with_label("close filelist");
	GtkCellRenderer *render;	
	GtkTreeViewColumn *column;	
	PangoAttrList *atrb=pango_attr_list_new();
	PangoAttribute *ubold=pango_attr_weight_new(PANGO_WEIGHT_ULTRABOLD);

	if (Nbar.select)  return;

	gtk_window_set_transient_for(GTK_WINDOW(sbwin),GTK_WINDOW(MainWindow));
	gtk_window_set_position(GTK_WINDOW(sbwin), GTK_WIN_POS_MOUSE);
	gtk_window_set_decorated(GTK_WINDOW(sbwin),TRUE);
	g_signal_connect (G_OBJECT (sbwin), "delete_event", G_CALLBACK (setNULL), Nbar.select);
	g_signal_connect (G_OBJECT (sbwin), "destroy", G_CALLBACK (setNULL), Nbar.select);
	gtk_container_add(GTK_CONTAINER(sbwin),frame);
	gtk_container_set_border_width(GTK_CONTAINER(frame),PAD);
	ubold->start_index=0;
	ubold->end_index=16;
	pango_attr_list_insert(atrb,ubold);
	gtk_label_set_attributes(GTK_LABEL(flab),atrb);
	
	gtk_container_add(GTK_CONTAINER(frame),sbox);
	gtk_box_pack_start(GTK_BOX(sbox),FLLV,FALSE,FALSE,0);
	
	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(FLLV), FALSE);
	render=gtk_cell_renderer_text_new();	
	column=gtk_tree_view_column_new_with_attributes("filelist",render,"text",0,NULL);	
	gtk_tree_view_append_column(GTK_TREE_VIEW(FLLV),column);
	g_signal_connect(FLLV, "row-activated", G_CALLBACK(fileselect), sbwin);
	Nbar.select = gtk_tree_view_get_selection(GTK_TREE_VIEW(FLLV));
	list2view(liststore);

	gtk_box_pack_end(GTK_BOX (sbox), cancelBT, FALSE, FALSE, 0);
	g_signal_connect(cancelBT, "clicked", G_CALLBACK(closeFL_window), sbwin);

	gtk_widget_show_all(sbwin);
}


gboolean tailfile () {  /* used on large files by watchfile() and reload() */
	char *line, *tmp;
	FILE *fin;
	int now=filelen(Seefile->path);

	if (now<=SFsz) return TRUE;
	if (Debug>0) g_print("tailfile() %d %d\n", SFsz, now);
	if (!(tmp=realloc(FileCon,now+1))) {
		error_popup("Out of memory!");
		return TRUE; 
	}
	else FileCon=tmp;
	fin=fopen(Seefile->path,"r");
	fseeko(fin,SFsz,SEEK_SET);
	gtk_text_buffer_get_bounds(Tbuf,&start,&finish);
	while ((line=linein(fin))) {
		gtk_text_buffer_insert(Tbuf,&finish,line,-1);
		strcat(FileCon,line); 
		free(line); 
	}
	fclose(fin);
	scrolltoline(NULL,buflen(FileCon));
	SFsz=strlen(FileCon);
	return TRUE;
}


gboolean testuni (gunichar CHR, short int chr) {
	if (CHR == chr) return TRUE;
	else return FALSE;
}


gchar *textline (int line) {	/* used by bookmark_list() */
	gchar *rptr=NULL;
	int cc=0, lc=1, lstart=0, len=strlen(FileCon);
	while (cc<len) {
		if (FileCon[cc]=='\n') { lc++; lstart=cc+1; } 
		if (lc==line) { rptr=FileCon+lstart; break; }
		cc++;
	}
	return rptr;
}


void toedit () {
	int pid;
	char string[MPTH+16];

	if ((!Seefile) || (Seefile->type != 'R') || !Config->editor) return;

	pid=fork();
	if (pid==0) {
		sprintf(string,"%s %s", Config->editor, Seefile->path);
		system(string);
		exit (0);
	} else {
		sprintf(string, "%s sent to %s", Seefile->name, Config->editor);
		gtk_window_set_title(GTK_WINDOW (MainWindow), string); 
	}
}


gboolean TxTKpress (GtkWidget *widget, GdkEventKey *kyprs, gpointer *data) {
	gint ln, nofl, lend, lffs;
	GtkTextMark *curpos; 
	GtkTextIter from, moveto;	
	switch(kyprs->keyval) {				
		case GDK_Home:
			if (kyprs->state==GDK_CONTROL_MASK) {
				scrolltoline(NULL,1);			
				return TRUE; 
			}
			else return FALSE;			
	 	case GDK_End:
			if ((FileCon) && (kyprs->state==GDK_CONTROL_MASK)) {
				nofl = buflen(FileCon);
				scrolltoline(NULL,nofl-1);			
				return TRUE; 
			}
			else return FALSE;	
		/* the next two skip the cursor forward and back 27 chars at a time via ALT */
		case GDK_Left:		
			if (kyprs->state==GDK_MOD1_MASK) {
				curpos=gtk_text_buffer_get_insert(Tbuf);
				gtk_text_buffer_get_iter_at_mark(Tbuf,&from,curpos);
				ln=gtk_text_iter_get_line(&from);
				gtk_text_buffer_get_iter_at_line(Tbuf,&moveto,ln);
				gtk_text_buffer_place_cursor(Tbuf,&moveto);
				gtk_text_view_scroll_to_iter((GtkTextView*)TxT, &moveto, 0, FALSE, 0, 0);
				return TRUE; 
			}
			else return FALSE;	
		case GDK_Right:
			if (kyprs->state==GDK_MOD1_MASK) {
				curpos=gtk_text_buffer_get_insert(Tbuf);
				gtk_text_buffer_get_iter_at_mark(Tbuf,&from,curpos);
				lend=gtk_text_iter_get_chars_in_line(&from)-1;
				lffs=gtk_text_iter_get_line_offset(&from);
				ln=gtk_text_iter_get_line(&from);
				if ((lffs+27)>lend) gtk_text_buffer_get_iter_at_line_offset(Tbuf,&moveto,ln,lend);
				else {
					lffs+=27;
					gtk_text_buffer_get_iter_at_line_offset(Tbuf,&moveto,ln,lffs);
				}
				gtk_text_buffer_place_cursor(Tbuf,&moveto);
				gtk_text_view_scroll_to_iter((GtkTextView*)TxT, &moveto, 0, FALSE, 0, 0);
				return TRUE; 
			}
			else return FALSE;	
		default:
			return FALSE; 
	}		
}


int update_filelist (seefile *old, int lastline) {  
/* used by loadnew(), handlequit(), file_out(), exec_proc() and reconfigure() */
	char list[27][FLESZ], newListing[FLESZ]="\0", oldListing[FLESZ]="\0";
	int len = loadlist(list),
		i, newsz, oldsz;
	FILE *fstW;

	if (!(fstW=fopen(Config->filelist,"w"))) {
		error_popup("Unable to update filelist!\nError #3\n(see manpage/ctrl-h)");
		return -2; 
	}

	if (Debug>0) g_print("update_filelist() old=%p lastline = %d listlen=%d\n",(void*)old,lastline,len);

	if (len>=27) len=26;

	if (Seefile) {	/* new file is being loaded */
		if (Seefile->sec[0]) sprintf(newListing,"%s|(%s)",Seefile->path,Seefile->sec);
		else sprintf(newListing,"%s|",Seefile->path); 
		if (CurFS == 1) fprintf(fstW,"%s\n",newListing); /* now at top of filelist */ 
	}

	if (old) {
		if (old->sec[0]) sprintf(oldListing,"%s|(%s)",old->path,old->sec);
		else sprintf(oldListing,"%s|",old->path);
	}

	newsz = strlen(newListing);
	oldsz = strlen(oldListing);
	for (i=0;i<len;i++) {	/* only one entry per filename */
		if (newListing[0] && (!strncmp(list[i],newListing,newsz)) && CurFS == 1) continue;
		if (oldListing[0] && (!strncmp(list[i],oldListing,oldsz))) fprintf(fstW,"%s$%d\n",oldListing,lastline);
		else fprintf(fstW,"%s\n",list[i]); 
	}
	fclose(fstW);

	if (Nbar.select) rescanFileList();

	return 0;
}


void usage (char *name) {
	if (strcmp (name,"seetxt")) {
		printf("Usage: %s [manpage] [-s section] [-x term] [-d level]\n",name);
		puts("-s\tman page section number (eg, -s 1 or -s 3p)"); 
	} else printf("Usage: %s [filename] [-x term] [-d level]\n",name);
	printf(	"-x\tinitial search term\n"
		"-d\t1,2 or 3, turn on debugging to stdout\n"
		"-K\tfree server space/socket\n"
		"-v\tshow version\n"
		"-h\tprints this message\n"
		"All arguments are optional.\n"
		"If used, the filename or manpage should always be the first argument.\n\n"
	);
}


void usebmk (GtkTreeView *treeview, GtkTreePath *treepath, GtkTreeViewColumn *treecol, GtkWidget *swin) {
	int ln;
	GtkTreeModel *Tmod=gtk_tree_view_get_model(treeview);
	GtkTreeIter itr;
	if (gtk_tree_model_get_iter (Tmod, &itr, treepath)) gtk_tree_model_get(Tmod, &itr, 0, &ln, -1);
	if (ln != 0) scrolltoline(NULL,ln-1);
	gtk_widget_destroy(swin);
}


void watchfile () {
	static guint watch_TO, blink_TO;
	gboolean (*func)() = reload;
	if (!(watchSW) && (!Seefile)) return;
	if (SFsz>Config->tailat) func=tailfile;
	if (watchSW==0) {
		watch_TO=g_timeout_add((Config->watchtime*1000), (GSourceFunc)func,NULL);
		blink_TO=g_timeout_add((500), (GSourceFunc)blinktoggle,&wff);
		watchSW=1;
	} else {
		watchSW=0;
		g_source_remove(watch_TO);	
		g_source_remove(blink_TO);	
		if (wff.sw==1) {
			gtk_container_remove(GTK_CONTAINER(watchTgl), watchon);	
			gtk_container_add(GTK_CONTAINER(watchTgl), watchoff);
			wff.sw=0;
		}
	}
}
