#include "global.h"
#include "ausgabe.h"
#include "block.h"
#include "clipbrd.h"
#include "comm.h"
#include "edit.h"
#include "event.h"
#include "file.h"
#include "icon.h"
#include "memory.h"
#include "obj.h"
#include "rsc.h"
#include "set.h"
#include "tasten.h"
#include "text.h"
#include "window.h"
#include "find.h"


/* exportierte Variablen ***************************************************/

GLOBAL BOOLEAN				s_grkl, s_quant, s_wort, s_vorw, s_global, s_round,
								df_rekursiv;
GLOBAL WORD					r_modus, rp_box_x, rp_box_y;
GLOBAL UBYTE				r_str[HIST_LEN+1], s_str[HIST_LEN+1],
								s_history[HIST_ANZ][HIST_LEN+1],
								r_history[HIST_ANZ][HIST_LEN+1],
								df_name[15+1];

GLOBAL UMLAUTENCODING 	umlaut_from, umlaut_to;

/*****************************************************************************/

#define SETANZ 		5

#define M_CURSOR		0		/* Werte fr modus von start_suche */
#define M_TSTART		1
#define M_TENDE		2

LOCAL ZEILEP 	start;
LOCAL WORD		text_len, text_x,
					last_op = -1;
LOCAL LONG		text_y;
LOCAL SET		wort_set;

/* Variablen, die ber set_suchmode gesetzt werden */
/* !!! muessen bei match gesichet werden !!! */
LOCAL BOOLEAN	quantor, vorw, grkl, wort, modus, round, line_start;
LOCAL WORD		muster_len;
LOCAL UBYTE 	muster_txt[HIST_LEN+1];
LOCAL SET		group[SETANZ];
LOCAL WORD		setanz;
LOCAL WORD		delta[256];

/* lokale Prototypen *****************************************************/
LOCAL BOOLEAN	build_popup (UBYTE h[HIST_ANZ][HIST_LEN+1], POPUP *pop);
LOCAL WORD		hist_popup	(UBYTE h[HIST_ANZ][HIST_LEN+1], OBJECT *tree, WORD obj_pos, WORD obj_text);

/*=======================================================================*/

LOCAL VOID init_suche(TEXTP t_ptr, WORD modus)
{
	if (modus == M_CURSOR)
	{
		start = t_ptr->cursor_line;
		text_y = t_ptr->ypos;
		if (vorw)
		{
			text_x = t_ptr->xpos;
			text_len = start->len-t_ptr->xpos;
			if (text_len>0)
			{
				text_x++;
				text_len--;
			}
			else if (!IS_LAST(start))
			{
				NEXT(start);
				text_y++;
				text_x = 0;
				text_len = start->len;
			}
		}
		else
		{
			text_x = 0;
			text_len = t_ptr->xpos;
			if (text_len>0)
			{
				text_len--;
			}
			else if (!IS_FIRST(start))
			{
				VORG(start);
				text_y--;
				text_len = start->len;
			}
		}
	}
	else if (modus==M_TSTART)
	{
		start = FIRST(&t_ptr->text);
		text_x = 0;
		text_len = start->len;
		text_y = 0;
	}
	else
	{
		start = LAST(&t_ptr->text);
		text_x = 0;
		text_len = start->len;
		text_y = t_ptr->text.lines-1;
	}
	setcpy(wort_set,t_ptr->loc_opt->wort_set);
}

LOCAL WORD suche1(ZEILEP col, WORD x, WORD str_len, WORD *such_len,
						UBYTE*(*call)(UBYTE*,WORD,UBYTE*,WORD*))
/* vorw */
{
	UBYTE *ptr, *str, *str2;

	if (quantor)
	{
		if (x>0 && line_start)									/* Muster am Anfang finden */
			return -1;
		if (x+str_len<col->len && muster_len>=2 &&
			 muster_txt[muster_len-2]=='[' && muster_txt[muster_len-1]==0xFC)
			return -1;
	}
	ptr = TEXT(col);
	str = ptr+x;
	str2 = (*call)(str, str_len, muster_txt, such_len);
	if (str2==NULL) return -1;
	if (wort)
	{
		while (!(str2==ptr || !setin(wort_set,str2[-1])) ||
				 !(str2[*such_len]==EOS || !setin(wort_set,str2[*such_len])))
		{
			str2++;
			str_len -= (short)(str2 - str);
			str = str2;
			str2 = (*call)(str, str_len, muster_txt, such_len);
			if (str2==NULL) return -1;
		}
	}
	return (short)(str2 - ptr);
}

LOCAL WORD suche2(ZEILEP col, WORD x, WORD str_len, WORD *such_len,
						UBYTE*(*call)(UBYTE*,WORD,UBYTE*,WORD*))
/* rauf */
{
	WORD	x2, merk;

	merk = 0;
	while (TRUE)
	{
		x2 = suche1(col, x, str_len, such_len, call);
		if (x2==-1) break;
		x2++; 								/* Weiter suchen */
		str_len -= x2-x;					/* Restlnge */
		merk = x = x2;
	}
	return merk-1;
}

LOCAL UBYTE *STRSTR(UBYTE *str_anf, WORD str_len, UBYTE *mstr_anf, WORD *found_len)
/* Suche ohne Quantoren */
{
	UBYTE *mstr, *str;
	WORD	i, mstr_len = muster_len;

	*found_len = mstr_len;
	str_anf += mstr_len;
	str_len -= mstr_len;
	mstr_anf += mstr_len;
	while (str_len>=0)
	{
		str = str_anf;
		mstr = mstr_anf;
		i = mstr_len;
		while(TRUE)
		{
			if (*(--mstr)!=*(--str)) break;		/* Match fehlgeschlagen */
			if ((--i)<=0) return(str); 			/* gefunden */
			if (*(--mstr)!=*(--str)) break;		/* Match fehlgeschlagen */
			if ((--i)<=0) return(str);				/* gefunden */
			if (*(--mstr)!=*(--str)) break;		/* Match fehlgeschlagen */
			if ((--i)<=0) return(str); 			/* gefunden */
			if (*(--mstr)!=*(--str)) break;		/* Match fehlgeschlagen */
			if ((--i)<=0) return(str); 			/* gefunden */
		}
		i = delta[str_anf[-1]];
		str_anf += i;										/* weiterrcken */
		str_len -= i;
	}
	return(NULL);
}

LOCAL UBYTE *STRSTR1(UBYTE *str_anf, WORD str_len, UBYTE *mstr_anf, WORD *found_len)
/* Suche ohne Quantoren (Gross/Klein) */
{
	UBYTE *mstr, *str, s;
	WORD	i, mstr_len = muster_len;

	*found_len = mstr_len;
	str_anf += mstr_len;
	str_len -= mstr_len;
	mstr_anf += mstr_len;
	while (str_len>=0)
	{
		str = str_anf;
		mstr = mstr_anf;
		i = mstr_len;
		while (TRUE)
		{
			s = *(--str);
			if (s>='a' && s<='z') s-=32;
			else if (s=='') s = '';
			else if (s=='') s = '';
			else if (s=='') s = '';
			if (*(--mstr)!=s) break;					/* Match fehlgeschlagen */
			if ((--i)<=0) return (str);				/* gefunden */
		}
		s = str_anf[-1];
		if (s>='a' && s<='z') s-=32;
		else if (s=='') s = '';
		else if (s=='') s = '';
		else if (s=='') s = '';
		i = delta[s];
		str_anf += i;										/* weiterrcken */
		str_len -= i;
	}
	return(NULL);
}

LOCAL UBYTE *STRSTR2(UBYTE *str_anf, WORD str_len, UBYTE *mstr_anf, WORD *found_len)
/* Suche mit Quantoren (rekursiv) */
{
	UBYTE *str, *mstr, m, s;
	WORD	len, anz;

	if (line_start)										/* Muster am Anfang finden */
		anz = 1;
	else
		anz = str_len+1;									/* Anzahl der Tests */
	while ((--anz)>=0)									/* auch len=0 kann Treffer sein */
	{
		mstr = mstr_anf;									/* Muster reset */
		str = str_anf; 									/* String reset */
		len = str_len;
		while (TRUE)
		{
			m = *mstr++;									/* neue Zeichen holen */
			s = *str;
			if (m==EOS) 									/* Muster komplett gefunden */
			{
				*found_len = (short)(str - str_anf);
				return str_anf;
			}
			if (len==0)
				s = EOS;

			if (m=='*')
			{
				BOOLEAN	save = line_start;

				line_start = FALSE;
				str = STRSTR2(str, len, mstr, found_len);
				line_start = save;
				if (str==NULL)
					return NULL;
				*found_len += (short)(str - str_anf);
				return str_anf;
			}
			if (!grkl)
			{
				if (s>='a' && s<='z') s-=32;
				else if (s=='') s = '';
				else if (s=='') s = '';
				else if (s=='') s = '';
			}
			if (m=='[')
			{
				m = *mstr++;								/* nach '[' folgt ein Infozeichen */
				if (m==0xFF)							/* echtes '[' */
				{
					if (s!='[') break;
				}
				else if (m==0xFE) 							/* Wortende (letztes in Muster) */
				{
					if (len==0 || !setin(wort_set,s))
					{
						*found_len = (short)(str - str_anf);
						return str_anf;
					}
					break;
				}
				else if (m==0xFC) 						/* Zeilenende */
				{
					if (len==0)
					{
						*found_len = (short)(str - str_anf);
						return str_anf;
					}
					break;
				}
				else											/* Wildcard */
				{
					if (!setin(group[m-1],s)) break;
				}
			}
			else												/* normale Zeichen und '?' */
				if (m!=s && m!='?') break;

			if (len==0) break;
			str++; len--;									/* nchstes Zeichen */
		}
		str_anf++;											/* String ein Zeichen weiter */
		str_len--;
	}
	return NULL;
}

/* rein : start, text_x, text_len, text_y */
/* raus : start, text_x, text_len, text_y */
/* -1:Abbruch, 0:nichts gefunden 1:gefunden */

LOCAL WORD suchen2(WORD *such_len)
{
	UBYTE*	(*call)	(UBYTE*,WORD,UBYTE*,WORD*);
	LONG	y;
	ZEILEP lauf;
	WORD	step, x;

	x = text_x;
	y = text_y;
	lauf = start;
	if (muster_len==0) return 0;
	step = 70;
	if (quantor)
		call = STRSTR2;
	else if (grkl)
		call = STRSTR;
	else
		call = STRSTR1;
	if (vorw)
	{
		while (TRUE)
		{
			if ((text_x=suche1(lauf,x,text_len,such_len,call))>=0)
			{
				text_y = y;
				start = lauf;
				return 1;
			}
			NEXT(lauf); y++; x = 0; text_len = lauf->len;
			if (IS_TAIL(lauf)) return 0;
			if ((--step)==0)
			{
				if (abbruch() && note(1,BREAK)==1) return -1;
				step = 70;
			}
		}
	}
	else
	{
		while (TRUE)
		{
			if ((text_x=suche2(lauf,x,text_len,such_len,call))>=0)
			{
				text_y = y;
				start = lauf;
				return 1;
			}
			VORG(lauf);
			if (IS_HEAD(lauf)) return 0;
			y--;
			x = 0;
			text_len = lauf->len;
			if ((--step)==0)
			{
				if (abbruch() && note(1,BREAK)==1) return -1;
				step = 70;
			}
		}
	}
}

/* rein : start, text_x, text_len, text_y */
/* raus : start, text_x, text_len, text_y */
/* -1:Abbruch, 0:nichts gefunden 1:gefunden */

LOCAL WORD suchen(TEXTP t_ptr, WORD *such_len)
{
	WORD	erg;

	erg = suchen2(such_len);
	if (erg==0 && round)
	{
		WORD	m;

		mybeep();
		if (vorw)
			m = M_TSTART;
		else
			m = M_TENDE;
		init_suche(t_ptr,m);
		erg = suchen2(such_len);
	}
	return erg;
}

/* ====================================================================== */

GLOBAL VOID set_suchmode(UBYTE *Muster, BOOLEAN Grkl, BOOLEAN Quantor, BOOLEAN Vorw,
								BOOLEAN Wort, BOOLEAN Global, BOOLEAN Round)
{
	UBYTE		*ptr, *d, help[HIST_LEN+1];
	BOOLEAN	invers;

	strcpy(muster_txt, Muster);
	grkl = Grkl;
	quantor = Quantor;
	vorw = Vorw;
	wort = Wort;
	round = Round;
	setanz = 0;
	if (!grkl) str_upper(muster_txt);
	if (quantor)
	{
		d = help;
		ptr = muster_txt;
		if (*ptr=='^')
		{
			line_start = TRUE;
			ptr++;
		}
		else
			line_start = FALSE;

		for (; *ptr; ptr++)
		{
			*d++ = *ptr;
			if (*ptr=='$' && ptr[1]==EOS)
			{
				d[-1] = '[';
				*d++ = 0xFC;
			}
			else if (*ptr=='[' && setanz<SETANZ)
			{
				UBYTE *merk = ptr;

				ptr++;
				invers = FALSE;
				if (*ptr=='^')
				{
					invers = TRUE;
					ptr++;
				}
				setclr(group[setanz]);
				while(*ptr && *ptr!=']')
				{
					if (ptr[0]=='-' && ptr[-1]!='[' && ptr[1]!=']')
					{
						UBYTE i;

						ptr++;
						for (i=ptr[-2]; i<*ptr; i++)
							setincl(group[setanz],i);
					}
					else
						setincl(group[setanz],*ptr++);
				}
				if (*ptr)							/* Keine ']' gefunden */
				{
					if (invers)
						setnot(group[setanz]);
					setanz++;
					*d++ = setanz; 				/* immer einen grer (weil nie Null) */
				}
				else
				{
					*d++ = 0xFF;					/* Echtes '[' */
					ptr = merk; 					/* Neu Scannen */
				}
			}
		}
		if (wort && d>help)						/* nur Worte und berhaupt ein Muster */
		{
			*d++ = '[';
			*d++ = 0xFE;							/* Wortende */
		}
		*d = EOS;
		strcpy(muster_txt,help);
		muster_len = (short) strlen(muster_txt);
	}
	else
	{
		WORD i,j;

		muster_len = (short) strlen(muster_txt);
		for (i=0; i<256; i++) 
			delta[i] = muster_len;
		j = muster_len-1;
		for (i=0; i<j; i++) 
			delta[muster_txt[i]] = j-i;
	}
	if (Global)
	{
		if (vorw)
			modus = M_TSTART;
		else
			modus = M_TENDE;
	}
	else
			modus = M_CURSOR;
}

GLOBAL WORD start_find(TEXTP t_ptr, BOOLEAN quiet)
{
	WORD	len, erg;

	last_op = 1;
	set_mouse(HOURGLASS, NULL);
	init_suche(t_ptr, modus);			/* Suchzeiger an den Start bringen */
	erg = suchen(t_ptr,&len);
	set_mouse(ARROW, NULL);
	if (erg == 1 && !quiet)
	{
		blk_demark(t_ptr);
		t_ptr->cursor_line = start;
		t_ptr->xpos = text_x;
		t_ptr->ypos = text_y;
		make_chg(t_ptr->link,POS_CHANGE,0);
		blk_mark(t_ptr,0);
		t_ptr->xpos = text_x+len;
		blk_mark(t_ptr,1);
		restore_edit();
	}
	return erg;
}

GLOBAL WORD start_replace(TEXTP t_ptr, BOOLEAN special)
{
	UBYTE 	*ptr;
	WORD		delta, erg, loc_r_modus,
				such_len, rpl_len;
	LONG		anz;

	last_op = 2;
	set_mouse(HOURGLASS, NULL);
	rpl_len	= (short) strlen(r_str);
	anz = 0L;
	init_suche(t_ptr, modus);

	/* Nur zentrieren, wenn kein andere Pos bekannt. */
	if (rp_box_x == 0 && rp_box_y == 0)
		dial_center(repask);
	else
	{
		repask[0].ob_x = rp_box_x;
		repask[0].ob_y = rp_box_y;
	}
	loc_r_modus = r_modus;
	
	while((erg=suchen(t_ptr, &such_len))==1)
	{
		delta = rpl_len-such_len;
		if (start->len + delta > MAX_LINE_LEN)
		{
			fnote(1, TOOLONG, MAX_LINE_LEN);
			erg = -1;
			break;
		}
		text_len = start->len-text_x;
		t_ptr->cursor_line = start;
		t_ptr->xpos = text_x;
		t_ptr->ypos = text_y;
		if (loc_r_modus != RP_ALL) 			/* Optional oder einzeln */
		{
			blk_demark(t_ptr);
			make_chg(t_ptr->link,POS_CHANGE,0);
			restore_edit();
			if (loc_r_modus == RP_OPT)
			{
				WORD antw;

				blk_mark(t_ptr,0);
				t_ptr->xpos = text_x+such_len;
				blk_mark(t_ptr,1);
				restore_edit();

				antw = handle_dial(repask, 0);

				/* die Pos des Dialogs merken */
				rp_box_x = repask[0].ob_x;
				rp_box_y = repask[0].ob_y;

				if (antw == RAALL)					/* ab jetzt nicht mehr fragen */
					loc_r_modus = RP_ALL;
				else if (antw ==  RANEIN)			/* Nicht ersetzen */
				{
					if (vorw)
					{
						text_x++;
						text_len--;
					}
					else
					{
						text_len = text_x+such_len-1;
						text_x = 0;
					}
					continue;
				}
				else if (antw == RAENDE)			/* aufhren */
				{
					erg = -1;
					break;
				}
				blk_demark(t_ptr);
			}
		}
		anz ++;
		get_undo_col(t_ptr);
		ptr = REALLOC(&start, text_x, delta);
		memcpy(ptr, r_str, rpl_len);
		t_ptr->cursor_line = start;
		if (loc_r_modus != RP_ALL)
		{
			make_chg(t_ptr->link,LINE_CHANGE,t_ptr->ypos);
			restore_edit();
			if (loc_r_modus == RP_FIRST) 
				break;
		}
		if (vorw)
		{
			text_x += rpl_len;
			text_len += delta;
			text_len -= rpl_len;
		}
		else
		{
			text_len = text_x;
			text_x = 0;
		}
	}
	set_mouse(ARROW, NULL);
	if (anz > 0L)
	{
		t_ptr->moved++;
		make_chg(t_ptr->link,POS_CHANGE,0); 		/* wg. `*'	*/
		if (loc_r_modus == RP_ALL)
		{
			if (t_ptr->block && !special)
			{
				t_ptr->p1 = get_line(&t_ptr->text,t_ptr->z1);
				t_ptr->p2 = get_line(&t_ptr->text,t_ptr->z2);
				blk_demark(t_ptr);
			}
			make_chg(t_ptr->link,TOTAL_CHANGE,0);
		}
		if (loc_r_modus != RP_FIRST && !special)
		{
			UBYTE	info[30];

			sprintf(info, STRING(REPLACESTR), anz);
			set_info(t_ptr, info);
		}
		if (erg!=-1)
			erg = 1;
	}
	else
		if (erg != -1) 
			erg = 0;
	if (!special)
		restore_edit();
	return erg;
}

GLOBAL WORD do_next(TEXTP t_ptr)
{
	WORD	erg;
	BOOLEAN	vorw;

	vorw = (global_shift) ? (!s_vorw) : s_vorw;
	set_suchmode(s_str, s_grkl, s_quant, vorw, s_wort, FALSE, s_round);
	if (last_op == 1)
		erg = start_find(t_ptr,FALSE);
	else if (last_op == 2)
		erg = start_replace(t_ptr, FALSE);
	else
		erg = -1;
	return erg;
}

GLOBAL BOOLEAN filematch(CONST UBYTE *str, CONST UBYTE *m)
{
	UBYTE 	*where, muster[HIST_LEN+1];
	WORD		cs_val, i;
	BOOLEAN	gk = FALSE, old_flg[2];

	if ((str[0] == EOS) || (m[0] == EOS))
		return FALSE;
	
	if ((m[0] == '*') && (m[1] == '.'))		/* Ist m eine Extension? */
		gk = FALSE;									/* dann Gro == klein! */
	else
	{
		caseSens((UBYTE *)str, &cs_val);
		gk = (cs_val == 0);						/* Echter GRkl-Unterscheidung */
	}

	old_flg[0] = (modus != M_CURSOR);
	old_flg[1] = round;

	sprintf(muster, "^%s$", m);
	set_suchmode(muster, gk, TRUE, TRUE, FALSE, FALSE, FALSE);
	where = STRSTR2((UBYTE*)str, (short) strlen(str), muster_txt, &i);

	/* 
	 * Fr den Fall, das ein Projekt durchsucht wird, wird filematch() fr
	 * die Zuordnung der lokalen Optionen fr jede durchsuchte Datei einmal
	 * aufgerufen. Damit danach die eigentlichen Such-Parameter wieder stimmen,
	 * der folgende Befehl!
	*/
	set_suchmode(s_str, s_grkl, s_quant, s_vorw, s_wort, old_flg[0], old_flg[1]);

	return (where==str);
}

/*****************************************************************************/

LOCAL UBYTE Umlaute[8][7] =
				{ /* 	  	  	  	  	  	  	*/
				  { 0x84, 0x8E, 0x94, 0x99, 0x81, 0x9A, 0x9E},	/* Atari */
				  { 0xE4, 0xC4, 0xF6, 0xD6, 0xFC, 0xDC, 0xDF},	/* Latin */
				  { 0x8A, 0x80, 0x9A, 0x85, 0x9F, 0x86, 0xA7},	/* Mac */
				  { 0x84, 0x8E, 0x94, 0x99, 0x81, 0x9A, 0xE1},	/* PC */
				  {  'a',  'A',  'o',  'O',  'u',  'U',  's'},	/* LaTeX */
				  {  'a',  'A',  'o',  'O',  'u',  'U',  's'},	/* HTML */
				  {  'a',  'A',  'o',  'O',  'u',  'U',  's'},	/* ASCII */
				};


LOCAL VOID to_latex(UBYTE *new, WORD *j, WORD u)
{
	new[(*j)++] = '"';
	new[(*j)++] = Umlaute[umlaut_to][u];
}

LOCAL VOID to_ascii(UBYTE *new, WORD *j, WORD u)
{
	new[(*j)++] = Umlaute[umlaut_to][u];
	if (u == 6)									/*  -> ss */
		new[(*j)++] = 's';
	else
		new[(*j)++] = 'e';
}

LOCAL VOID to_html(UBYTE *new, WORD *j, WORD u)
{
	new[(*j)++] = '&';
	new[(*j)++] = Umlaute[umlaut_to][u];
	if (u == 6)									/*  -> &szlig */
	{
		new[(*j)++] = 'z';
		new[(*j)++] = 'l';
		new[(*j)++] = 'i';
		new[(*j)++] = 'g';
	}
	else											/* -> &[aouAOU]uml */
	{
		new[(*j)++] = 'u';
		new[(*j)++] = 'm';
		new[(*j)++] = 'l';
	}
	new[(*j)++] = ';';
}

LOCAL VOID to_misc(UBYTE *new, WORD *j, WORD u)
{
	new[(*j)++] = Umlaute[umlaut_to][u];
}


LOCAL WORD change_line(UBYTE *zeile, UBYTE *new, WORD max_len)
{
	WORD		i, j, u, len;
	BOOLEAN	changed = FALSE;

	i = 0;
	j = 0;
	len = (short) strlen(zeile);
	while (i < len)
	{
		if (zeile[i] > 127)
		{
			for (u = 0; u <= 7; u++)
				if (zeile[i] == Umlaute[umlaut_from][u])
					break;
	
			if (j >= max_len)
				return -2;
	
			if (u < 7)
			{
				changed = TRUE;
				if (umlaut_to == LaTeX)
					to_latex(new, &j, u);
				else if (umlaut_to == ASCII)
					to_ascii(new, &j, u);
				else if (umlaut_to == HTML)
					to_html(new, &j, u);
				else
					to_misc(new, &j, u);
			}
			else
				new[j++] = zeile[i];
		}
		else
			new[j++] = zeile[i];
		i++;
	}
	new[j] = EOS;
	if (changed)
		return (j - i);
	else
		return -1;
}


GLOBAL VOID	change_umlaute(TEXTP t_ptr)
{
	ZEILEP	lauf;
	UBYTE		new_txt[MAX_LINE_LEN];
	WORD		delta_len, x, max_len;
	LONG		l;

	lauf = FIRST(&t_ptr->text);
	if (lauf != NULL)
	{
		x = BildPos(t_ptr->xpos, t_ptr->cursor_line, TRUE, t_ptr->loc_opt->tabsize);
		start_aktion(STRING(UMLAUTSTR), FALSE, t_ptr->text.lines);
		l = 0;
		set_mouse(HOURGLASS, NULL);

		/*
		 * Fr den Fall, da die Zeile zu lang wird, wird der Platz ermittelt,
		 * der fr genau eine Umwandlung noch bentigt wird.
		*/
		switch (umlaut_to)
		{
			case LaTeX :
			case ASCII :
				max_len = (short)sizeof(new_txt) - 1;	/* Es kommt ein Zeichen dazu */
				break;
			case HTML :
				max_len = (short)sizeof(new_txt) - 6;	/* Es kommen sechs dazu */
				break;
			default:
				max_len = (short)sizeof(new_txt);		/* Kein Gefahr, keine neuen Zeichen */
				break;
		}
		while (!IS_TAIL(lauf))
		{
			delta_len = change_line(TEXT(lauf), new_txt, max_len);
			if (delta_len >= 0)
			{
				t_ptr->moved++;
				if (delta_len > 0)					/* Lnge wurde erhht */
					REALLOC(&lauf, 0, delta_len);
				strcpy(TEXT(lauf), new_txt);
			}
			else if (delta_len == -2)
			{
				fnote(1, TOOLONG, MAX_LINE_LEN);
				break;
			}
			NEXT(lauf);
			do_aktion(++l);
		}
		set_mouse(ARROW, NULL);
		end_aktion();
		t_ptr->cursor_line = get_line(&t_ptr->text, t_ptr->ypos);
		t_ptr->xpos = InterPos(x, t_ptr->cursor_line, TRUE, t_ptr->loc_opt->tabsize);
		make_chg(t_ptr->link, POS_CHANGE, 0); 		/* wg. `*'	*/
		make_chg(t_ptr->link, TOTAL_CHANGE, 0);
		t_ptr->max_line = NULL;
		restore_edit();
	}
}

/* die Dialoge *************************************************************/

LOCAL VOID insert_history(UBYTE h[HIST_ANZ][HIST_LEN+1], UBYTE *str)
{
	WORD	i,j;
	UBYTE	old_history[HIST_ANZ][HIST_LEN+1];

	/* alte History merken */
	memcpy(old_history[0], h[0], HIST_ANZ*(HIST_LEN+1));
	strcpy(h[0], str);
	j = 1;
	/* n-1 kopieren */
	for (i = 0; i < (HIST_ANZ - 1); i++)
	{
		/* jeden Eintrag nur einmal */
		if ((old_history[i][0] != EOS) && (strcmp(old_history[i], h[0])!=0))
		{
			strcpy(h[j], old_history[i]);
			j++;
		}
	}
}


LOCAL BOOLEAN build_popup(UBYTE h[HIST_ANZ][HIST_LEN+1], POPUP *pop)
{
	UBYTE str[HIST_LEN + 1];
	WORD	i;

	if (h[0][0] == EOS)
		return FALSE;

	strcpy(str, " ");
	strcat(str, h[0]);
	create_popup(pop, HIST_ANZ, HIST_LEN + 3, str);

	for (i = 1; i < HIST_ANZ; i++)
	{
		if (h[i][0] != EOS)
		{
			strcpy(str, " ");
			strcat(str, h[i]);
			append_popup(pop, str);
		}
	}
	if (pop->tree == NULL)
		return FALSE;

	for(i = 0; i < pop->akt_item + 2; i++)
		rsrc_obfix(pop->tree, i);
	dial_fix(pop->tree);

	return TRUE;
}

LOCAL WORD hist_popup(UBYTE h[HIST_ANZ][HIST_LEN+1], OBJECT* tree, WORD obj_pos, WORD obj_text)
{
	WORD	pos_x, pos_y, y, ret;
	POPUP pop;

	ret = 0;
	if (build_popup(h, &pop))
	{
		objc_offset(tree, obj_pos, &pos_x, &pos_y);
		pos_x -= (pop.tree[1].ob_width - tree[obj_pos].ob_width);
		y = popup_menu(pop.tree, 1, pos_x, pos_y, NIL, FALSE, 1);
		y -= pop.first_item;
		if (y >= 0)
		{
			objc_setstring(tree, obj_text, h[y]);
			draw_Objc(tree, obj_text, 1);
			ret = y;
		}
		free_popup(&pop);
	}
	return ret;
}


LOCAL WORD circle_popup(UBYTE h[HIST_ANZ][HIST_LEN+1], OBJECT* tree, WORD text_obj, WORD pos)
{
	if ((pos + 1 < HIST_ANZ) && (h[pos + 1][0] != EOS))
		pos++;
	else
		pos = 0;
	objc_setstring(tree, text_obj, h[pos]);
	draw_Objc(tree, text_obj, 1);
	return pos;
}


/* Suchen/Ersetzen in Texten, Suchen in Projekten */
/* 0: Abbruch 1: Suchen 2: Ersetzen */
GLOBAL WORD find_dial(BOOLEAN in_prj)
{
	WORD		antw, d, s_cycle, r_cycle, edit_obj;
	BOOLEAN	quit = FALSE, im_kreis;
	DIALINFO dial;
	EVNTDATA	evd;
	
	save_clip();

	if (in_prj)
	{
		/* Hilfe: nur Suchen	*/
		hide_objc(fhilfe, FHOK, FALSE);
		do_flags(fhilfe, FHOK, DEFAULT);
		hide_objc(fhilfe, FHNEXT,TRUE);
		undo_flags(fhilfe, FHNEXT, DEFAULT);
		hide_objc(fhilfe, FHRAUS, TRUE);

		hide_objc(replace, RPTITEL, TRUE);
		for (d = RPESTR; d <= RPALL; d++)
			disable_objc(replace, d, TRUE);
		disable_objc(replace, RPERSATZ, TRUE);
		undo_flags(replace, RPTEXT2, EDITABLE);
		disable_objc(replace, RPROUND, TRUE);
		disable_objc(replace, RPCURSOR, TRUE);

		select_objc(replace, RPGLOBAL, TRUE);
		select_objc(replace, RPCURSOR, FALSE);
		select_objc(replace, RPROUND, FALSE);
	}
	else
	{
		/* Hilfe: Suchen + Ersetzen */
		hide_objc(fhilfe, FHOK, TRUE);
		undo_flags(fhilfe, FHOK, DEFAULT);
		hide_objc(fhilfe, FHNEXT, FALSE);
		do_flags(fhilfe, FHNEXT, DEFAULT);
		hide_objc(fhilfe, FHRAUS, FALSE);

		hide_objc(replace, RPTITEL, FALSE);
		for (d = RPESTR; d <= RPALL; d++)
			disable_objc(replace, d, FALSE);
		disable_objc(replace, RPERSATZ, FALSE);
		do_flags(replace, RPTEXT2, EDITABLE);
		disable_objc(replace, RPROUND, FALSE);
		disable_objc(replace, RPCURSOR, FALSE);

		select_objc(replace, RPGLOBAL, s_global);
		select_objc(replace, RPCURSOR, !s_global);
		select_objc(replace, RPROUND, s_round);
	}

	objc_setstring(replace, RPTEXT1, s_str);
	objc_setstring(replace, RPTEXT2, r_str);
	select_objc(replace, RPGRKL, s_grkl);
	select_objc(replace, RPWILD, s_quant);
	select_objc(replace, RPWORT, s_wort);
	select_objc(replace, RPVORW, s_vorw);
	select_objc(replace, RPRUCKW, !s_vorw);
	select_objc(replace, RPFIRST, r_modus==RP_FIRST);
	select_objc(replace, RPALL, r_modus==RP_ALL);
	select_objc(replace, RPOPTION, r_modus==RP_OPT);

	/* Popups abschalten */
	disable_objc(replace, RPSHIST, (s_history[0][0] == EOS));
	disable_objc(replace, RPEHIST, ((r_history[0][0] == EOS) || in_prj));

	open_dial(replace, &dial);
	dial_draw(&dial);
	s_cycle = 0;
	r_cycle = 0;
	edit_obj = RPTEXT1;
	do
	{
		antw = dial_do(&dial, &edit_obj) & 0x7FFF;
		graf_mkstate(&evd);
		if (evd.kstate & K_LSHIFT)
		{
		 	if (antw == RPSSTR)
				antw = RPSHIST;
		 	if (antw == RPESTR)
				antw = RPEHIST;
		}
		switch (antw)
		{
			case RPSSTR :
				s_cycle = circle_popup(s_history, replace, RPTEXT1, s_cycle);
				break;
			case RPSHIST :
				s_cycle = hist_popup(s_history, replace, RPSHIST, RPTEXT1);
				break;

			case RPESTR :
				r_cycle = circle_popup(r_history, replace, RPTEXT2, r_cycle);
				break;
			case RPEHIST :
				r_cycle = hist_popup(r_history, replace, RPEHIST, RPTEXT2);
				break;

			case RPHELP :
				d = handle_dial(fhilfe, 0);
				if (d == FHNEXT)
					handle_dial(rphilfe, 0);
				break;

			default:
				quit = TRUE;
				break;
		}
		select_objc(replace, antw, FALSE);
		draw_Objc(replace, antw, 1);
	}
	while (!quit);
	dial_end(&dial);
	if (antw == RPOK || antw == RPERSATZ)
	{
		objc_getstring(replace, RPTEXT1, s_str);
		objc_getstring(replace, RPTEXT2, r_str);
		if ((strcmp(s_str, r_str) == 0) && (antw == RPERSATZ))
		{
			note(1, RPSAME);
			return 0;
		}
		s_grkl	= get_select(replace,RPGRKL);
		s_quant	= get_select(replace,RPWILD);
		s_wort	= get_select(replace,RPWORT);
		s_vorw = get_select(replace,RPVORW);
		s_global = get_select(replace,RPGLOBAL);
		s_round	= get_select(replace,RPROUND);

		if (get_select(replace,RPFIRST))
			r_modus = RP_FIRST;
		else if (get_select(replace,RPALL))
			r_modus = RP_ALL;
		else
			r_modus = RP_OPT;

		if (s_str[0] != EOS)
			insert_history(s_history, s_str);
		if (r_str[0] != EOS)
			insert_history(r_history, r_str);

		im_kreis = s_round && (antw == RPOK);	/* 'im Kreis' nur bei Suchen, nicht beim Ersetzen */
		if (in_prj)
			set_suchmode(s_str, s_grkl, s_quant, s_vorw, s_wort, TRUE, FALSE);
		else
			set_suchmode(s_str, s_grkl, s_quant, s_vorw, s_wort, s_global, im_kreis);
		return((antw == RPOK) ? 1 : 2);
	}
	return(0);
}


/* Suchen auf Disk */
GLOBAL BOOLEAN diskfind_dial (UBYTE *df_path, UBYTE *df_name)
{
	PATH		name, str;
	UBYTE 	*p;
	WORD		antw, cycle, edit_obj;
	FSEL		fsel;
	BOOLEAN	quit = FALSE;
	DIALINFO dial;
	EVNTDATA	evd;
	
	save_clip();

	if (df_path[0] != EOS)
	{
		make_shortpath(df_path, str, 50);
		fill_ptext(diskfind, DFPATH, str);
	}
	else
		fill_ptext(diskfind, DFPATH, "");
	strcpy(name,df_path);
	objc_setstring(diskfind, DFFILE, df_name);
	objc_setstring(diskfind, DFTEXT, "");
	select_objc(diskfind,DFREK,df_rekursiv);
	select_objc(diskfind,DFGRKL,s_grkl);
	select_objc(diskfind,DFWILD,s_quant);
	select_objc(diskfind,DFWORT,s_wort);

	/* Popup abschalten */
	disable_objc(diskfind, DFHIST, (s_history[0][0] == EOS));
	disable_objc(replace, RPEHIST, TRUE);

	open_dial(diskfind, &dial);
	dial_draw(&dial);
	cycle = 0;
	edit_obj = DFFILE;
	do
	{
		antw = dial_do(&dial, &edit_obj) & 0x7FFF;

		/* Der neue Popup-Auslser kann von dial_do() nicht erkannt werden! */
		graf_mkstate(&evd);
		if ((evd.kstate & K_LSHIFT) && (antw == DFSTR))
			antw = DFHIST;

		switch (antw)
		{
			case DFSTR :
				cycle = circle_popup(s_history, diskfind, DFTEXT, cycle);
				break;

			case DFHIST:
				cycle = hist_popup(s_history, diskfind, DFHIST, DFTEXT);
				break;

			case DFSELP:
				strcpy(fsel.suffix, "*.*");
				strcpy(fsel.name, "");
				select_file(&fsel, name, STRING(SELPATHSTR));
				if (name[0] != EOS)
				{
					p = strrchr (name, '\\');
					p[1] = EOS;
					make_shortpath(name, str, 20);
					fill_ptext(diskfind, DFPATH, str);
				}
				dial_draw(&dial);
				break;

			case DFHELP :
				handle_dial(dfhilfe, 0);
				break;

			default:
				quit = TRUE;
				break;
		}
		select_objc(diskfind, antw, FALSE);
		draw_Objc(diskfind, antw, 1);
	}
	while (!quit);
	dial_end(&dial);
	if (antw == DFOK)
	{
		objc_getstring(diskfind, DFTEXT, s_str);
		objc_getstring(diskfind, DFFILE, df_name);
		df_rekursiv = get_select(diskfind,DFREK);
		s_grkl	= get_select(diskfind,DFGRKL);
		s_quant	= get_select(diskfind,DFWILD);
		s_wort	= get_select(diskfind,DFWORT);
		strcpy(df_path,name);
		if (s_str[0] != EOS)
			insert_history(s_history, s_str);
		set_suchmode(s_str, s_grkl, s_quant, TRUE, s_wort, TRUE, FALSE);
		return (TRUE);
	}
	return(FALSE);
}


GLOBAL BOOLEAN umlaut_dial(VOID)
{
	BOOLEAN		ret = FALSE, quit = FALSE;
	WORD			antw, save_from, save_to, y;
	DIALINFO		dial;

	save_from = umlaut_from;
	save_to = umlaut_to;
	if (global_shift)					/* bei Shift: Quelle und Ziel vertauschen */
	{
		if (umlaut_to <= PC)			/* nur einbuchstabige knnen umgedreht werden! */
		{
			umlaut_from = save_to;
			umlaut_to = save_from;
		}
		else
		{
			mybeep();
			return FALSE;
		}
	}
	objc_setstring(umlautkonv, UVON, (UBYTE *)get_obspec(popups, UPFROMST + umlaut_from));
	objc_setstring(umlautkonv, UNACH, (UBYTE *)get_obspec(popups, UPTOST + umlaut_to));

	open_dial(umlautkonv, &dial);
	dial_draw(&dial);
	do
	{
		antw = dial_do(&dial, NULL) & 0x7FFF;
		switch (antw)
		{
			case UVSTR :
			case UVONCIRC :
			case UVON :
				if (antw == UVON)
					y = popup_select(umlautkonv, UVON, popups, UMLAUTPOP1, FALSE, DO_POPUP);
				else
					y = popup_select(umlautkonv, UVON, popups, UMLAUTPOP1, FALSE, DO_CYCLE);
				y -= UPFROMST;
				if (y >= 0)
					umlaut_from = y;
				break;
			case UNSTR :
			case UNACHCIRC :
			case UNACH :
				if (antw == UNACH)
					y = popup_select(umlautkonv, UNACH, popups, UMLAUTPOP2, FALSE, DO_POPUP);
				else
					y = popup_select(umlautkonv, UNACH, popups, UMLAUTPOP2, FALSE, DO_CYCLE);
				y -= UPTOST;
				if (y >= 0)
					umlaut_to = y;
				break;
			default:
				quit = TRUE;
				break;
		}
	}
	while (!quit);
	select_objc(umlautkonv, antw, FALSE);
	dial_end(&dial);
	if (antw == UKSTART)
	{
		if (umlaut_from == umlaut_to)
		{
			note(1, NOKONV);
			ret = FALSE;
		}
		else
			ret = TRUE;
	}
	if (ret == FALSE)
	{
		umlaut_from = save_from;
		umlaut_to = save_to;
	}
	return ret;
}
