/********************************************************************
 *                                                                  *
 *      CRISP - Custom Reduced Instruction Set Programmers Editor   *
 *                                                                  *
 *      (C) Paul Fox, 1989                                          *
 *                                                                  *
 *                                                                  *
 *    Please See COPYLEFT notice.                                   *
 *                                                                  *
 ********************************************************************/
# include	"crisp.h"

int	click_line,
	click_col,
	click_state;
extern string last_file_edited;
/**********************************************************************/
/*   Field  separator  character  used  by  the  <Alt-G>  goto__line  */
/*   macro for selecting fields.				      */
/**********************************************************************/
string	FS = "\t";

/**********************************************************************/
/*   This  variable  is  set  from  the  .crinit file and is used to  */
/*   print files.						      */
/**********************************************************************/
string	print_cmd = "";
/**********************************************************************/
/*   Macro  to  delete  the  current  buffer. We can only do this if  */
/*   its  not  displayed  in  another  window,  and  we have another  */
/*   buffer  to  replace  this  one  with.  Prompt  user if its been  */
/*   modified.							      */
/**********************************************************************/
void
delete_curr_buffer()
{	int	curbuf;
	int	newbuf;
	string	prompt, filename;
	int	old_msg_level = inq_msg_level();
		
	if (inq_views() != 1) {
		error("Buffer being displayed in another window.");
		return;
		}
	curbuf = inq_buffer();
	newbuf = next_buffer();
	if (newbuf == curbuf) {
		error("No more buffers.");
		return;
		}
	if (inq_modified()) {
		while (1) {
			if (get_parm(NULL, prompt, "Buffer has not been saved.  Delete [ynw]? ", 1) <= 0) {
				message("");
				return;
				}
			if (index("nN", prompt)) {
			  	message("Buffer not deleted.");
				return;
				}
			if (index("wW", prompt)) {
			  	set_msg_level(0);
				if (write_buffer() <= 0) {
					set_msg_level(old_msg_level);
					return;
					}
				set_msg_level(old_msg_level);
				break;
				}
			if (index("yY", prompt)) {
				break;
				}
			}
		}
	delete_buffer(curbuf);
	set_buffer(newbuf);
	inq_names(filename);
	edit_file(filename);
	display_file_name();
	
}
/**********************************************************************/
/*   Macro executed when user hits <Alt-N>.			      */
/**********************************************************************/
void
edit_next_buffer()
{	int	curbuf = inq_buffer(),
		nextbuf = next_buffer();
	string filename;
	extern string last_file_edited;

	inq_names(last_file_edited);

	if (curbuf == nextbuf) {
		error("No more buffers.");
		return;
		}

	set_buffer(nextbuf);
	inq_names(filename);
	edit_file(filename);
	display_file_name();
}
/**********************************************************************/
/*   Macro executed when user hits <Alt-E>			      */
/**********************************************************************/
void
edit__file()
{	extern string last_file_edited;
	string	s;
	int	ml = inq_msg_level();
	string	filename;

	get_parm(0, filename);
	inq_names(s);
	set_msg_level(0);
	if (filename != "") {
		if (edit_file(filename))
			last_file_edited = s;
		}
	else {
		if (edit_file())
			last_file_edited = s;
		}
	set_msg_level(ml);
	if (substr(inq_message(), 1, 4) == "Edit")
		display_file_name();
}
/**********************************************************************/
/*   This  macro  selects  the  previous  edited buffer. This is the  */
/*   last  buffer  that  was  selected,  as  distinct from the macro  */
/*   previous_alpha_buffer()   which  selects  the  previous  buffer  */
/*   (alphabetically).						      */
/**********************************************************************/
void
previous_edited_buffer()
{
	extern string last_file_edited;
	string	s;

	if (last_file_edited == "") {
		inq_names(last_file_edited);
		edit_next_buffer();
		}
	else {
		s = last_file_edited;
		inq_names(last_file_edited);
		edit_file(s);
		display_file_name();
		}
}
/**********************************************************************/
/*   This    macro    selects   the   previous   buffer   which   is  */
/*   alphabetically before the current buffer.			      */
/**********************************************************************/
void
previous_alpha_buffer()
{	int	curbuf = inq_buffer(),
		nextbuf = next_buffer(NULL, 1);
	string filename;

	inq_names(last_file_edited);

	if (curbuf == nextbuf) {
		error("No more buffers.");
		return;
		}

	set_buffer(nextbuf);
	inq_names(filename);
	edit_file(filename);
	display_file_name();
}
/**********************************************************************/
/*   Macro to perform a redo after an undo.			      */
/**********************************************************************/
void
redo()
{
	undo(0, 0, 0);
}
/**********************************************************************/
/*   Macro  executed  when  <Alt-G> selected. Allows user to go to a  */
/*   line  in  the current buffer. If line is negative, we go to the  */
/*   old  line.  If  line  ends  with  a  full-stop,  treat  it as a  */
/*   character position.					      */
/**********************************************************************/
void
goto__line()
{	int	l;
	int	colpos = FALSE;
	int	ln, ln1;
	string line;

	get_parm(NULL, line, "Goto (old) line: ");

	/***********************************************/
	/*   If   we   start  with  a  '#'  go  to  a  */
	/*   particular field in the current line.     */
	/***********************************************/
	if (substr(line, 1, 1) == "#") {
		l = cvt_to_object(substr(line, 2));
		beginning_of_line();
		inq_position(ln);
		while (l-- > 0) {
			if (re_search(NULL, FS + "+\\c") <= 0)
				break;
			}
		inq_position(ln1);
		if (ln != ln1) {
			goto_line(ln);
			error("Not enough fields on line.");
			return;
			}
		return;
		}
	/***********************************************/
	/*   If  it  starts  with  a  '|'  make  it a  */
	/*   column position.			       */
	/***********************************************/
	if (substr(line, 1, 1) == "|") {
		colpos = TRUE;
		line = substr(line, 2);
		}
	/***********************************************/
	/*   If  number  looks  like  its  hex,  then  */
	/*   treat it as so.			       */
	/***********************************************/
	if (substr(line, 1, 2) == "0x" || substr(line, 1, 2) == "0X")
		l = cvt_to_object(line);
	else
		l = atoi(line);
		
	if (colpos)
		move_abs(NULL, l);
	else if (substr(line, strlen(line)) == ".") {
		top_of_buffer();
		next_char(l);
		}
	else if (l < 0)
		goto_old_line(- l);
	else if (l)
		goto_line(l);
}
/**********************************************************************/
/*   Function to allow user to set the field separator.		      */
/**********************************************************************/
void
set_fs()
{
	if (get_parm(NULL, FS, "Field separator: ", NULL, FS) > 0) {
		if (strlen(FS) > 1)
			FS = "[" + quote_regexp(FS) + "]";
		}
}
/**********************************************************************/
/*   Macro  called  when  the  tab  key  is  hit.  If  a  region  is  */
/*   hilighted then shift it otherwise just insert a tab.	      */
/**********************************************************************/
void
insert_tab()
{
	if (inq_marked())
		shiftr();
	else
		self_insert();
}
/**********************************************************************/
/*   Macro  called  when  <Shift-Tab>  key  hit.  Unindent currently  */
/*   hilighted block if necessary otherwise go back a tab stop.	      */
/**********************************************************************/
void
insert_backtab()
{
	if (inq_marked())
		shiftl();
	else
		previous_tab();
}
/**********************************************************************/
/*   Macro  to  force input into the keyboard input buffer. Argument  */
/*   is a string in key_to_int() format.			      */
/**********************************************************************/
void
force_input(string str)
{	int	i;
	string	fn;
	
	while (str != "") {
		if (substr(str, 1, 1) == "<") {
			i = index(str, ">");
			fn = substr(str, 1, i);
			str = substr(str, i + 1);
			push_back(key_to_int(fn));
			}
		else {
			push_back(key_to_int(substr(str, 1, 1)));
			str = substr(str, 2);
			}
		}
}
/**********************************************************************/
/*   Macro  to  delete  all  blank lines from cursor position to end  */
/*   of buffer.							      */
/**********************************************************************/
void
delete_blank_lines()
{	int	num = 0;

	save_position();
	while (re_search(NULL, "^$") > 0) {
		delete_line();
		num++;
		}
	restore_position();
	message("%d blank line%s deleted.", num, num == 1 ? "" : "s");
}
/**********************************************************************/
/*   Function to delete trailing white space to all lines in buffer.  */
/**********************************************************************/
void
delete_trailing_spaces()
{
	save_position();
	top_of_buffer();
	re_translate(NULL, "[ \t]+$", "");
	restore_position();
}
/**********************************************************************/
/*   Following  macro  turns  off  the  undo  flag  for  the current  */
/*   buffer.  When  performing big editing jobs stops us running out  */
/*   of disk space and speeds things up a bit.			      */
/**********************************************************************/
void
noundo()
{	int	flags = inq_buffer_flags();

	if (flags & BF_NO_UNDO) {
		set_buffer_flags(flags & ~BF_NO_UNDO, 0);
		message("Undo enabled.");
		}
	else {
		set_buffer_flags(NULL, BF_NO_UNDO);
		message("Undo disabled.");
		}
}
/**********************************************************************/
/*   Function to turn on/off ANSI mode setting for current buffer.    */
/**********************************************************************/
void
ansi()
{	int	flags = inq_buffer_flags();

	if (flags & BF_ANSI) {
		set_buffer_flags(flags & ~BF_ANSI, 0);
		message("ANSI disabled.");
		}
	else {
		set_buffer_flags(NULL, BF_ANSI);
		message("ANSI enabled.");
		}
	refresh();
}
void
_indent()
{
	int	col;
	
	if (inq_buffer_flags() & BF_READONLY) {
		down();
		beginning_of_line();
		return;
		}

	inq_position(NULL, col);
	insert("\n");
	/***********************************************/
	/*   If  user  hits  <Enter>  at beginning of  */
	/*   line  or  theres  stuff  to the right of  */
	/*   the cursor then don't autoindent.	       */
	/***********************************************/
	if (col == 1 || trim(ltrim(read())) != "")
		return;
	save_position();

	if (search_back("[~ \t]") <= 0) {
		restore_position();
		return; 
		}

	beginning_of_line();
	search_fwd("[~ \t]");
	inq_position(NULL, col);
	restore_position();
	move_abs(NULL, col);
	tab_to_col(col);
}
/**********************************************************************/
/*   Macro  to  move  the cursor back to the previous tab stop. This  */
/*   macro  will  not  move  the  cursor beyond the beginning of the  */
/*   current line.						      */
/**********************************************************************/
void
previous_tab()
{
	int	 col,
		num,
		prev_num;
	
	/***********************************************/
	/*   If  we  are already in column 1, dont go  */
	/*   back any further.			       */
	/***********************************************/
	inq_position(NULL, col);
	if (col == 1)
		return;
	left();
	prev_num = distance_to_tab();
	while (1) {
		num = distance_to_tab();
		inq_position(NULL, col);
		if (num < prev_num) {
			right();
			break;
			}
		if (col == 1)
			break;
		prev_num = num;
		left();
		}
}

void
tab_to_col(int col)
{
	int	curcol, hard_tabs;
	beginning_of_line();
	hard_tabs = use_tab_char("y");
	use_tab_char(hard_tabs ? "y" : "n");
	if (! hard_tabs) {
		insert(" ", col - 1);
		return;
		}
	while (1) {
		inq_position(NULL, curcol);
		if (curcol >= col)
			break;
		insert("\t");
		}
	if (curcol > col) {
		backspace();
		inq_position(NULL, curcol);
		insert(" ", col - curcol);
		}
}

void
display_file_name()
{
	string 	filename;
	int	cols, len;

	inq_names(filename);
	inq_screen_size(NULL, cols);
	cols -= 43;
	len = strlen(filename);
	if (len > cols) {
		filename = substr(filename, len - cols);
		filename = "..." + filename;
		}
	message("File: %s%s", filename,
		inq_modified() ? "*" : "");
}
void
repeat()
{
	int	count,
		ch;
	string	macro_name;

	count = 0;
	while (1) {
		message("Repeat count = %d", count);
		while ((ch = read_char()) == -1)
			nothing();
		if (ch >= '0' && ch <= '9') {
			count = count * 10 + ch - '0';
			continue;
			}
		if (int_to_key(ch) == "<Esc>") {
			message("Repeat aborted.");
			return; 
			}
		if (int_to_key(ch) == "<Ctrl-r>") {
			if (count == 0)
				count = 1;
			count *= 4;
			continue; 
			}
		break;
		}
	macro_name = inq_assignment(int_to_key(ch));
	while (count > 0) {
		execute_macro(macro_name);
		-- count;
		}
}
void
home()
{	int	ch;

	switch (click_state) {
	  case	2:		top_of_window();
	  case	3:		top_of_buffer();
	  default: {
		beginning_of_line();
		click_state = 1;
		}
	  }
	inq_position(click_line, click_col);
	++ click_state;
	refresh();
	while ((ch = read_char()) == -1)
		;
	push_back(ch);
	if (ch != key_to_int("<Home>"))
		click_state = 1;
}
void
end()
{     	int	ch;

	switch (click_state) {
	  case -2:	
			end_of_window();
			end_of_line();
			break;
	  case -3:	end_of_buffer();
			break;
	  default: {
			end_of_line();
			click_state = -1; 
			}
	  }
	inq_position(click_line, click_col);
	-- click_state;
	refresh();
	while ((ch = read_char()) == -1)
		;
	push_back(ch);
	if (ch != key_to_int("<End>"))
		click_state = -1;
}
void
quote()
{
	int	key = -1;

	while (key < 0)
		key = read_char(NULL, TRUE);
	insert(key);
}

/**********************************************************************/
/*   Macro  to  append the next line to the end of the current line.  */
/*   This  is  similar in operation to the 'vi' 'J' command. Leading  */
/*   spaces at the beginning of the next line are removed.	      */
/**********************************************************************/
void
join_line()
{
	down();
	beginning_of_line();
	/***********************************************/
	/*   Delete   white  space  at  beginning  of  */
	/*   next line.				       */
	/***********************************************/
	while (index(" \t", read(1)))
		delete_char();
	/***********************************************/
	/*   Delete  white  space  at  end of current  */
	/*   line.				       */
	/***********************************************/
	up();
	drop_anchor(MK_LINE);
	re_translate(SF_GLOBAL | SF_BLOCK, "[ \t]+$", "");
	raise_anchor();
	end_of_line();
	insert(" ");
	delete_char();
}
void
delete_character()
{
	if (inq_called() != "" || inq_marked() == 0) {
		delete_char();
		return;
		}
	if (inq_marked() == MK_COLUMN)
		block_delete();
	else
		delete_block();
	message("Block deleted.");
}
/**********************************************************************/
/*   Function  to  print  the  current  buffer. We set a region over  */
/*   the  whole  file and do a write_block() allowing user to select  */
/*   the  appopriate  print spooler. If this macro is called with an  */
/*   argument,  then  we assume that we are being passed the default  */
/*   command  for  printing so we just save it away without actually  */
/*   printing.							      */
/**********************************************************************/
void
print(string arg)
{	int	old_msg_level;
	string	filename;
	int	is_marked = inq_marked();
	
	/***********************************************/
	/*   If  we've  been  called  from  the  init  */
	/*   code then save the path.		       */
	/***********************************************/
	if (arg != "") {
		arg = ltrim(arg);
		if (substr(arg, 1, 1) != "|")
			arg = "|" + arg;
		print_cmd = arg;
		return;
		}
	if (print_cmd != "")
		filename = print_cmd;
	else {
		if (get_parm(NULL, filename, "Type print command: ") <= 0 ||
		    filename == "")
			return;
		}

	if (!is_marked) {
		save_position();
		top_of_buffer();
		drop_anchor(MK_LINE);
		end_of_buffer();
		}

	old_msg_level = inq_msg_level();
	set_msg_level(1);
	
	write_block(filename);
	
	set_msg_level(old_msg_level);
	if (is_marked)
		raise_anchor();
	else {
		raise_anchor();
		restore_position();
		}
}
/**********************************************************************/
/*   Function called on exit to save the print command.		      */
/**********************************************************************/
string
get_print()
{
	return print_cmd;
}
/**********************************************************************/
/*   Create  a  tiny  movable  popup window. This is an experimental  */
/*   macro.  It  is  not  designed  to  be  usable  until  some real  */
/*   problems are resolved concerning the display manager !!!!!!!!    */
/**********************************************************************/
void
tiny()
{
	int	win;
	
	win = sized_window(10, 20, NULL);
	set_window(win);
	attach_buffer(inq_buffer());
}
