; Description
;  
; 	The general idea here is that there are 10 virtual scrap buffers;
; one of them is the current scrap buffer at all times.  The current
; scrap buffer behaves just like the single scrap buffer which is built
; into Brief.  The current scrap buffer can be switched to
; another virtual scrap buffer at any time; the contents of
; the virtual scrap buffers are maintained across the switches.  No
; buffers are actually allocated until they are needed--if you do not
; use any scrap buffer in an editing session except the built-in scrap
; buffer, no resources beyond the buffer occupied by this file are
; consumed.
; 
; 	I use <Cntrl V> + Number keys to switch between the virtual scrap
; buffers; the keys to copy, cut paste and so on I leave as I had them
; with only one scrap buffer.  A viable alternative is to let
; "set_scrap" be invoked from the command line; it will prompt for a
; scrap buffer number.  In any event, no key assignments are done
; here.
;  
; 	Note that you can edit the virtual scrap buffers, although it is
; strongly implied in the Brief docs that editing a system buffer is
; not a good idea.  There is a bug in the buf_list macro in the
; "buffers.m" file supplied with Brief v2.01; it "loops out" if the
; current buffer is a system buffer when it is invoked.  The modified
; version supplied in the file "lists.m" has the bug
; fixed, as well as several other improvements.  But beware; if you try
; and write a system buffer out to disk, Brief locks up.
; 
; 	
; 	
; 
; 
; Implementation
; 
; 	Rather than moving a text block into or out of the appropriate virtual
; scrap buffer at the time the copy, cut or paste operation is done
; (the method used by other multiple-scrap macro packages I have seen),
; in this package the current scrap buffer actually is the built-in
; scrap buffer.  When a different virtual scrap buffer is selected
; to be the current scrap buffer, the old one is saved out and the new
; one is read into the built-in scrap buffer.  This results in many
; fewer double-buffered copy operations, giving better performance.  It
; also avoids the problem of having one of the virtual scrap buffers
; getting corrupted (because it is the only one which is actually the
; built-in scrap buffer) when other buffers are accessed, as is the
; case with the VI emulator macros.  Here, the virtual scrap buffers
; are all logically equivalent.
; 
; 	To save memory and keep the number of buffers to a minimum, a
; virtual scrap buffer only gets a system buffer to use for storage
; when it is switched to (with "set_scrap") for the first time.  I have 
; been assured by Dave Nanian of Solution Systems that a buffer identifier
; will never be zero; this code depends on that fact.  Note, however,
; that if you use the "list_buffers" macro to edit the current scrap
; buffer you are not actually editing the current scrap buffer; you are
; editing an old copy of it, which will be overwritten when you switch
; to another virtual scrap buffer.  To get around this, switch to
; another virtual scrap buffer before you edit the one you want to
; edit.  One of the system buffers named "scrap" is the current
; scrap buffer (it is in fact the built-in scrap buffer of Brief), but
; their are usually several buffers with this name, and it is difficult
; and unnecessary to determine which one is the current copy.  The
; virtual scrap buffers are named "vs0", "vs1" and so on.
;
;
;
;
;
; Structure
; 
; 	The top level function is set_scrap; it deals only with scrap
; numbers, which must be in the range 0-9.  It calls save_scrap and
; read_scrap to do the work, and updates the global int "currvs". 
; It returns the new virtual scrap number, or -1 on failure.
; 
; 	save_scrap and read_scrap get passed scrap numbers; they call
; "get_vs_id" to convert them into buffer identifiers.  

; 	This module uses some 30 global integers to store info about the
; virtual scrap buffers, rather than an array in a buffer.  This was
; more-or-less an experiment, but it seems to work well.  I tend to
; assume, although I have not verified, that it is faster than using a
; buffer, with all the ascii-to-binary and vice-versa conversions
; which that entails.  The downside is that it probably takes more
; code, namely, the large switch statements found here.  
; 
; 	This module would be an ideal appliation of the "named variables"
; I describe in the document "namedvar.txt", which I have uploaded to
; the board.
;
;
;
;
;
; Bugs
; 
; 	If you switch to an empty virtual scrap buffer (one which has
; never had any text in it), pasting the scrap will insert a newline
; into the current buffer.
; 
; 	I believe I know the reason for this, but I have been unable to
; fix it.  The problem is that every buffer must have a newline in it,
; even if it has nothing else; a newline is put into a buffer when it
; is created.  The "paste" primitive knows that, if the built-in scrap
; has never been copied to, the lone newline should not be inserted. 
; It traps this situation as a special condition, displaying the
; "No scrap to insert" message.
; 
; 	When you switch to an empty virtual scrap buffer, the virtual
; scrap buffer is copied into the built-in scrap buffer; therefore the
; special condition is no longer true and the "paste" primitive does
; not trap it.  In order to trap this myself I would have to keep
; another set of flags, and provide a function to access them; that
; seems like a lot of trouble for a relatively trivial problem.
; Nevertheless, it can sometimes be annoying; if anybody finds a good
; solution, please update the file and/or leave me a message.
;
;
;
; ADDITION
; 10/24/88
;
; By inserting the following lines into your intials macro I have assigned
; the set_scrap macro to <Ctrl s>
;
; I have also added the show_scrap_buffer macro which allows you to view
; your scrap buffers in a pop_up window. <Alt n> cycles thru the scrap
; buffers and the scrap buffer that is in the window when you escape is the
; one that is made current. This is tied to <Ctrl s><Ctrl s>
;
;      (autoload "scraps" "set_scrap" "show_scrap_buffer")
;      (assign_to_key "<Ctrl s>" "set_scrap")
;      (assign_to_key "<Ctrl s><Ctrl s>" "show_scrap_buffer")
;
;
; Additions - 30/Aug/92
; ~~~~~~~~~
; With Brief 3.1, these routines did not work to my satisfaction.
; In particular, I found the following problems:
;
;	1) On exit from "show_buffer", the scrap displayed in the window
;		was not made active, as I would expect, but instead the current
;		scrap (at time of entry to "show_buffer" overwrote the buffer
;		which was last displayed.
;
;	2) The "show_buffer" routine only displayed from buffer 0 upwards,
;		until it met a non-activated buffer. If you had used buffers
;		0,1,2 & 4 but missed 3; the <Alt><n> display would only show 0 - 2
;		When it found 3 had not been activated, it would jump back to 0.
;
;	3) If all 10 buffers were in use, the "show_buffer" routine	would
;		pause an extra time after buffer 9. This was due to a logic fault
;		in "_next_virtual_scrap". When "currvs" was 9, the value of "next_vs"
;		was correctly set to buffer 0, but "currvs" itself was incremented
;		to 10. With a further <Alt><n>, "currvs" went straight through the
;		switch routine, so was reset to 0 and then continued correctly.
;
; To get over these problems, I have made the following changes:
;
;	1)	New Scrap Buffers can only be created via the "new_scrap" routine.
;		This creates the next numerically available virtual buffer.
;		It will not let you create more than 10 buffers.
;
;	2) "set_scrap" has been modified, to allow you to switch to an
;		existing buffer only. The prompt tells you the range available.
;
;	3) A check for "currvs" = 10 made in "_next_virtual_scrap" to
;		ensure that each <Alt><n> press cycles forward one buffer.
;
;	4) "show_buffer" modified in two ways:
;
;		a) Aborting the display with <Esc> or <Alt><x> restores
;			the original scrap buffer.
;
;		b)	A new option to ACCEPT the currently displayed buffer,
;			with <Enter> places the contents of the currently
;			displayed virtual buffer into the built-in scrap buffer.
;			None of the virtual buffers have their contents altered.
;
;	I have used the following key assignments in my initials macro.
;	(The <Control v> key is available with Brief V3.1)
;
;		 (autoload "scraps" "set_scrap" "new_scrap" "show_scrap_buffer")
;
;      (assign_to_key "<Ctrl v><s>" "set_scrap")
;      (assign_to_key "<Ctrl v><v>" "show_scrap_buffer")
;      (assign_to_key "<Ctrl v><n>" "new_scrap")
;      (assign_to_key "<Ctrl v><1>" "set_scrap 1")
;      (assign_to_key "<Ctrl v><2>" "set_scrap 2")
;      (assign_to_key "<Ctrl v><3>" "set_scrap 3")
;      (assign_to_key "<Ctrl v><4>" "set_scrap 4")
;      (assign_to_key "<Ctrl v><5>" "set_scrap 5")
;      (assign_to_key "<Ctrl v><6>" "set_scrap 6")
;      (assign_to_key "<Ctrl v><7>" "set_scrap 7")
;      (assign_to_key "<Ctrl v><8>" "set_scrap 8")
;      (assign_to_key "<Ctrl v><9>" "set_scrap 9")
;      (assign_to_key "<Ctrl v><0>" "set_scrap 0")
;

#include "equates.mh"
(extern pause)

(macro _init	;** modified to include new global variables
					;** "livevs"	- no of live virtual scrap buffers
					;** "swapflag"	- replace built-in scrap with virtual
	(
  		(int currvs livevs swapflag vs0_id vs1_id vs2_id vs3_id vs4_id vs5_id vs6_id vs7_id vs8_id vs9_id) 
		(global currvs livevs swapflag vs0_id vs1_id vs2_id vs3_id vs4_id vs5_id vs6_id vs7_id vs8_id vs9_id) 
		(int vs0_ins_nl vs1_ins_nl vs2_ins_nl vs3_ins_nl vs4_ins_nl vs5_ins_nl vs6_ins_nl vs7_ins_nl vs8_ins_nl vs9_ins_nl) 
		(global vs0_ins_nl vs1_ins_nl vs2_ins_nl vs3_ins_nl vs4_ins_nl vs5_ins_nl vs6_ins_nl vs7_ins_nl vs8_ins_nl vs9_ins_nl) 
		(int vs0_mt vs1_mt vs2_mt vs3_mt vs4_mt vs5_mt vs6_mt vs7_mt vs8_mt vs9_mt) 
		(global vs0_mt vs1_mt vs2_mt vs3_mt vs4_mt vs5_mt vs6_mt vs7_mt vs8_mt vs9_mt) 

		(= vs0_id (create_buffer "vs0" NULL SYSTEM))
;		(= currvs 0)
		(= livevs 0)
	)
)


(macro set_scrap	;** modified by DPG to Limit buffer access
						;** to those already opened by "new_scrap"

	(
		(int newvs)

;******** new code
;**		 ~~~~~~~~
		(string prompt dummy)

		(= prompt "Scrap Buffer (0-")
		(sprintf dummy "%d" livevs)

		(if (! (get_parm 0 newvs (+ (+ prompt dummy) ") ?")))
			(return -1)
		)
		(if (|| (< newvs 0) (> newvs livevs))
			(
				(message "Maximum of %d Scrap Buffers Accessible!" (+ livevs 1))
				(return -1)
			)
		)
;**
;******** code replaced by the above
;			 ~~~~~~~~~~~~~
;		(if (! (get_parm 0 newvs "Scrap Buffer? (0-9) "))
;			(return -1)
;		)
;		(if (|| (< newvs 0) (> newvs 9))
;			(return -1)
;		)
;********

		; Save the state info of the built-in scrap to the state info of the virtual scrap.
		(save_scrap_info)
		; Save the built-in scrap to the virtual scrap.
		(save_scrap currvs)

		(= currvs newvs)

		; Read the virtual scrap into the built-in scrap.
		(read_scrap newvs)
		; Set the state info for the built-in scrap to the state info of the virtual scrap.
		(restore_scrap_info)

		(message "Current Scrap Set to %d" currvs)
		(return currvs)
	)
)


(macro new_scrap	;** new macro by DPG
						;** automatically assigns next scrap buffer
	(
		(int newvs)
		(= newvs (+ livevs 1))	;** next buffer is one more than current

		(if (> newvs 9)			;** limit of 10 buffers (0-9)
			(
				(message "Maximum of 10 Scrap Buffers Allowed!")
				(return -1)
			)
		)

		(= livevs (+ livevs 1))	;** if setting up new buffer, increment counter

		; Save the state info of the built-in scrap to the state info of the virtual scrap.
		(save_scrap_info)
		; Save the built-in scrap to the virtual scrap.
		(save_scrap currvs)

		(= currvs newvs)

		; Read the virtual scrap into the built-in scrap.
		(read_scrap newvs)
		; Set the state info for the built-in scrap to the state info of the virtual scrap.
		(restore_scrap_info)

		(message "New Scrap Buffer Set to %d" currvs)
		(return currvs)
	)
)


(macro save_scrap_info
	(
		(switch currvs
		0 
			(inq_scrap vs0_ins_nl vs0_mt)
		1 
			(inq_scrap vs1_ins_nl vs1_mt)
		2
			(inq_scrap vs2_ins_nl vs2_mt)
		3
			(inq_scrap vs3_ins_nl vs3_mt)
		4
			(inq_scrap vs4_ins_nl vs4_mt)
		5
			(inq_scrap vs5_ins_nl vs5_mt)
		6
			(inq_scrap vs6_ins_nl vs6_mt)
		7
			(inq_scrap vs7_ins_nl vs7_mt)
		8
			(inq_scrap vs8_ins_nl vs8_mt)
		9
			(inq_scrap vs9_ins_nl vs9_mt)
		NULL
			(return 0)	; just in case
		)	; endswitch
	)
)

(macro restore_scrap_info
	(
		(switch currvs
		0 
			(set_scrap_info vs0_ins_nl vs0_mt)
		1 
			(set_scrap_info vs1_ins_nl vs1_mt)
		2
			(set_scrap_info vs2_ins_nl vs2_mt)
		3
			(set_scrap_info vs3_ins_nl vs3_mt)
		4
			(set_scrap_info vs4_ins_nl vs4_mt)
		5
			(set_scrap_info vs5_ins_nl vs5_mt)
		6
			(set_scrap_info vs6_ins_nl vs6_mt)
		7
			(set_scrap_info vs7_ins_nl vs7_mt)
		8
			(set_scrap_info vs8_ins_nl vs8_mt)
		9
			(set_scrap_info vs9_ins_nl vs9_mt)
		NULL
			(return 0)	; just in case
		)	; endswitch
	)
)

(macro get_vs_id
	(
		(int newvs)

		(if (get_parm 0 newvs)	; Error checking should have already
			(							; been done at this point
				(switch newvs		; newvs is presumed to be 0-9, inclusive
				0 
					(
						(if (! vs0_id)
							(= vs0_id (create_buffer "vs0" NULL SYSTEM))
						)
						(return vs0_id)
					)
				1 
					(
						(if (! vs1_id)
							(= vs1_id (create_buffer "vs1" NULL SYSTEM))
						)
						(return vs1_id)
					)
				2
					(
						(if (! vs2_id)
							(= vs2_id (create_buffer "vs2" NULL SYSTEM))
						)
						(return vs2_id)
					)
				3
					(
						(if (! vs3_id)
							(= vs3_id (create_buffer "vs3" NULL SYSTEM))
						)
						(return vs3_id)
					)
				4
					(
						(if (! vs4_id)
							(= vs4_id (create_buffer "vs4" NULL SYSTEM))
						)
						(return vs4_id)
					)
				5
					(
						(if (! vs5_id)
							(= vs5_id (create_buffer "vs5" NULL SYSTEM))
						)
						(return vs5_id)
					)
				6
					(
						(if (! vs6_id)
							(= vs6_id (create_buffer "vs6" NULL SYSTEM))
						)
						(return vs6_id)
					)
				7
					(
						(if (! vs7_id)
							(= vs7_id (create_buffer "vs7" NULL SYSTEM))
						)
						(return vs7_id)
					)
				8
					(
						(if (! vs8_id)
							(= vs8_id (create_buffer "vs8" NULL SYSTEM))
						)
						(return vs8_id)
					)
				9
					(
						(if (! vs9_id)
							(= vs9_id (create_buffer "vs9" NULL SYSTEM))
						)
						(return vs9_id)
					)
				NULL
					(return 0)	; just in case
				)	; endswitch
			)
		;else
			(return 0)	; no parm
		)
	)
)


; Save the contents and state of the built-in scrap buffer into one
; of the virtual scrap buffers.
(macro save_scrap
	(
		(int 		vs			; virtual scrap number (0-9)
					vs_id		; Buffer id of its buffer (!= zero)
		)
		(if (get_parm 0 vs)
			(
				(= vs_id (get_vs_id vs))
				(if vs_id	; a buffer id can never be zero
					(
						(int currbuff_id)
						(= currbuff_id (inq_buffer))
						(set_buffer vs_id)
						(top_of_buffer)
						(drop_anchor)
						(end_of_buffer)
						(delete_block)
						(raise_anchor)
						(paste)
						(set_buffer currbuff_id)
					)
				)
			)
		)
	)
)


; Read the contents and state of one of the virtual scrap buffers into the
; built-in scrap buffer.
(macro read_scrap
	(
		(int 		vs	; virtual scrap number (0-9)
					vs_id
		)
		(if (get_parm 0 vs)

			(
				(= vs_id (get_vs_id vs))
				(if vs_id	; a buffer id can never be zero
					(
						(int currbuff_id)
						(= currbuff_id (inq_buffer))
						(set_buffer vs_id)
						(top_of_buffer)
						(drop_anchor)
						(end_of_buffer)
						(copy)
						(raise_anchor)
						(set_buffer currbuff_id)
					)
				)
			)
		)
	)		
)

(macro clear_buffer
	(
		(int buff_id currbuff_id)
		(if (get_parm 0 buff_id)
			(
				(= currbuff_id (inq_buffer))
				(set_buffer buff_id)
				(top_of_buffer)
				(drop_anchor)
				(end_of_buffer)
				(delete_block)
				(raise_anchor)
				(set_buffer currbuff_id)
			)
		)
	)
)	
(macro show_scrap_buffer	;** modified by DPG to allow exit with new buffer
									;** original version did not work under Brief V3.1

   (
      (int old_buffer
           window_buffer
           new_vs
			  old_vs
      )

		(= old_vs currvs)	;** remember original buffer
      (set_scrap currvs)
      (= old_buffer (inq_buffer))

;** window title changed to include "accept/abort" option

      (create_window 15 20 70 3 "<Alt>n Next Scrap  <Enter> Accept  <ESC> Abort")
      (switch currvs
         0
            (= new_vs vs0_id)
         1
            (= new_vs vs1_id)
         2
            (= new_vs vs2_id)
         3
            (= new_vs vs3_id)
         4
            (= new_vs vs4_id)
         5
            (= new_vs vs5_id)
         6
            (= new_vs vs6_id)
         7
            (= new_vs vs7_id)
         8
            (= new_vs vs8_id)
         9
            (= new_vs vs9_id)
      )

      (set_buffer    new_vs)
      (attach_buffer new_vs)
      (top_of_buffer)
      (refresh)
      (keyboard_push)
      (assign_to_key "#18432" "up")
      (assign_to_key "#19712" "right")
      (assign_to_key "#20480" "down")
      (assign_to_key "#19200" "left")
      (assign_to_key "#18688" "page_up")
      (assign_to_key "#20736" "page_down")
      (assign_to_key "#11520" "exit")	;** <Alt><x>
      (assign_to_key "<esc>"  "exit")
      (assign_to_key "#13"    "sw_exit")	;** exit with swap
      (assign_to_key "#12544" "_next_virtual_scrap")
		(= swapflag 0)
      (process)
      (keyboard_pop)

;** option to replace or abort the displayed buffer

		(if (== swapflag 1)
			(
					; Read the NEW virtual scrap into the built-in scrap.
				(read_scrap currvs)
					; Reset the state info for the new scrap
				(restore_scrap_info)
			)
		;else
			(= currvs old_vs)	;** restore the original scrap number
		)

;** end of new code

      (set_buffer old_buffer)
      (delete_window)

		(message "Current Scrap Set to %d" currvs)
  )
)
(macro sw_exit
	(
		(= swapflag 1)
		(exit)
	)
)
(macro _next_virtual_scrap
   (
      (int next_vs)
      (switch currvs
         0
            (= next_vs vs1_id)
         1
            (= next_vs vs2_id)
         2
            (= next_vs vs3_id)
         3
            (= next_vs vs4_id)
         4
            (= next_vs vs5_id)
         5
            (= next_vs vs6_id)
         6
            (= next_vs vs7_id)
         7
            (= next_vs vs8_id)
         8
            (= next_vs vs9_id)
         9
            (= next_vs vs0_id)
      )
      (if (== next_vs 0)
         (
            (= next_vs vs0_id)
            (= currvs 0)
         )
      ;else
         (= currvs (+ currvs 1))	;** note if currvs was 9, will become 10
      )									;** ~~~~

		(if (== currvs 10)	;** added to avoid double press
			(						;** to get from 9 to 0
				(= currvs 0)
			)
		)

      (set_buffer    next_vs)
      (top_of_buffer)
      (attach_buffer next_vs)
      (refresh)
   )
)
