;**
;**		BRIEF -- Basic Reconfigurable Interactive Editing Facility
;**
;**		Written by Dave Nanian and Michael Strickman.
;**
;**		Revision history:
;**		-----------------
;**		8 January 1986				Changed reform to use below rather than "<".
;**										Fixed up delete_macro error checking of margin.
;**

;**
;**		wp.m:
;**
;**		This file contains a set of macros that can help with very simple word
;**	processing applications.  The functions supported are word_wrap, centering,
;**	and paragraph reformatting, with a flexible right margin.  By default,
;**	word_wrap is turned on when this package is loaded.  The other functions
;**	can be invoked as they are needed, or can be assigned to any keys you
;**	choose.
;**
;**		These functions are NOT intended to replace a full-featured word processor.
;**	They are useful for letters and other short documents, and as a supplement
;**	to a text formatter.
;**

#define	ADD_CHAR_MACRO		0
#define	NEW_FILE_MACRO		1
#define	DEFAULT_MARGIN		70

#define	LEGAL_FILES			".me.doc.txt.n.hlp."

;**
;**	wp:
;**
;**		This macro starts the package by setting up word_wrap and initializing
;**	the margin.
;**

(macro wp
	(
		(int		margin_val)

		(global	margin_val)

		(if (first_time)
			(
				(= margin_val DEFAULT_MARGIN)
				(register_macro NEW_FILE_MACRO "_wp_new_file")
				(_wp_new_file)
				(message "Word processing initialized.")
			)
		)
	)
)

;**
;**		center:
;**
;**		Centers the current line in the work area (the area between column 1
;**	and the current margin).
;**

(macro center
	(
		(string	text)

		(if (== margin_val 0)
			(wp)
		)
		(search_back "<[ \\t]@\\c[~ \\t]")
		(= text (read))
		(= text (substr text 1 (- (strlen text) 1)))
		(beginning_of_line)
		(delete_to_eol)
		(move_rel 0 (/ (- margin_val (strlen text)) 2))
		(insert text)
	)
)

;**
;**		word_wrap:
;**
;**		This macro is automatically invoked after each time that a character
;**	is typed.  It moves the cursor to the next line when it is past the 
;**	margin, taking the current word with it if necessary.
;**
;**		Automatic invokation is specified in the "wp" macro below, by calling
;**	the "register_macro" function.
;**

(macro _word_wrap
	(
		(int		col)

		(inq_position NULL col)

		(if (> col margin_val)
			(
				(if (== margin_val 0)
					(wp)
				;else
					(
						(prev_char)
			
						(if (! (index " \t" (read 1)))
							(
								(int	new_col)
				
								(search_back "<|[ \\t][~ \\t]")
								(inq_position NULL new_col)
				
								(if (== new_col 1)
									(end_of_line)
								)
								(insert "\n")
				
								(if (!= new_col 1)
									(
										(delete_char)
										(move_rel 0 (- (- col new_col) 1))
									)
								)
							)
						;else
							(next_char)
						)
					)
				)
			)
		)
	)
)
				
;**
;**		margin:
;**
;**		Resets the current margin, prompting the user if necessary.
;**

(macro margin
	(get_parm 0 margin_val "Enter margin: ")
)		

;**
;**		reform:
;**
;**		Reformats the currently marked area a line at a time.  The main
;**	loop goes through each line, adjusting it to the margin as necessary.
;**	If the line is too long, it splits it at the last space before the
;**	margin; it it's too short, it keeps joining the following lines to it,
;**	or parts of them if they are too long, until the line fits the margin.
;**

(macro reform
	(
		(string		last_char
						tab_reply
		)
		(int			old_buffer
						temp_buffer
						cur_line
						cur_col
						prev_line
						prev_col
						not_done
		)
		(if (== margin_val 0)
			(wp)
		)
		(= tab_reply (use_tab_char "n"))

		(if (! (inq_marked))
			(message "No marked area.")
		;else
			(
				(message "Reformatting marked paragraph...")

				;** Transfer marked area into a fresh buffer, then delete it.

				(inq_position cur_line cur_col)
				(swap_anchor)
				(inq_position prev_line prev_col)

				(if (|| (below cur_line prev_line)
							(&& (== cur_line prev_line) (below cur_col prev_col)))
					(
						(int	temp)

						(= temp cur_line)
						(= cur_line prev_line)
						(= prev_line temp)
						(= temp cur_col)
						(= cur_col prev_col)
						(= prev_col temp)
					)
				)
				(= old_buffer (inq_buffer))
				(set_buffer (= temp_buffer (create_buffer "Word Process" NULL 1)))
				(transfer old_buffer prev_line prev_col cur_line cur_col)
				(set_buffer old_buffer)
				(delete_block)
				(set_buffer temp_buffer)
				(top_of_buffer)

				;** Loop through buffer a line at a time, shrinking or expanding
				;** each line as necessary.

				(while (! (inq_position))
					(
                  (end_of_line)
						(inq_position NULL cur_col)

						;** If line is too long, split it.

						(if (> cur_col margin_val)
							(
								(move_abs 0 (+ margin_val 1))
								(search_back "[ \t]")
								(insert "\n")

								(while (== " " (read 1))
									(delete_char)
								)
								(up)
							)
						;else line might need to be merged with following lines.
							(
								(= not_done 1)

								(while not_done
									(
										(left)
										(= last_char (read 1))

										;** Skip current character and leave two spaces after 
										;** .:!?

										(+= cur_col (!= last_char " "))
										(+= cur_col (> (index ".:!?" last_char) 0))

										;** Stop if we don't need any more after punc.

										(if (>= cur_col margin_val)
											(= not_done 0)
										;else try to fill more of line
											(
												(down)
                                    (end_of_line)
												(inq_position prev_line prev_col)

												;** If we need the entire next line, prepare for join.

												(if (<= prev_col (- margin_val cur_col))
													(
                                          (beginning_of_line)

														(if (== (read) "\n")
															(= not_done 0)
														)
														(up)
													)
												;else if we need just a chunk of it
													(
														;** Find the maximum amt. we can take.
	
														(move_abs 0 (+ 1 (- margin_val cur_col)))
														(search_back "[ \t][~ \t]")
														(inq_position cur_line)
		
														;** If the first word on the next line is
														;** too long, we're done with the line.
		
														(if (!= cur_line prev_line)
															(= not_done 0)
														;else
															(
																;** Split off line and clean up start of next.

																(insert "\n")
																(delete_char)
					
																;** Go back up and prepare to join.
					
																(move_rel -2 0)
															)
														)	;Endif first word is too long
													)
												) ;Endif (now have old line + chunk)

												;** Now join the lines together if needed.

												(if not_done 
													(
														(move_abs 0 cur_col)
														(delete_char)
													)
												)

												;** Prepare to loop again.

												(end_of_line)
												(inq_position NULL cur_col)
											)
										) ;Endif line needs to be extended
									)
								) ;Endwhile line isn't full
							)
						) ;Endif (Line is now ready to insert)

						;** Read line into string and insert in other buffer.

						(beginning_of_line)
						(= last_char (read))
						(set_buffer old_buffer)

                  (if (!= last_char "\n")
   						(insert last_char)
						)
						(refresh)
						(set_buffer temp_buffer)
						(delete_line)

						(if (== (read) "\n")
							(delete_line)
						)
					)
				) ;Endwhile there are lines to insert.

				;** Get rid of the scrap buffer and insert the remaining text.

				(set_buffer old_buffer)
				(delete_buffer temp_buffer)
				(message "Reformatting complete.")
			)
		)
		(use_tab_char tab_reply)
	)
)

;**
;**		_wp_new_file:
;**
;**		This macro is called whenever a new file is edited.  It checks to
;**	see if the extension is one of the ones we can do word processing on.
;**	If so, we register the _word_wrap macro.  Otherwise, we get rid of it.
;**

(macro _wp_new_file
	(
		(string		extension)

		(inq_names NULL extension)

		(if (index LEGAL_FILES (+ (+ "." extension) "."))
			(register_macro ADD_CHAR_MACRO "_word_wrap")
		;else
 			(unregister_macro ADD_CHAR_MACRO "_word_wrap")
		)
	)
)
