;**************************************************************************
;*                                                                        *
;*    BOOKMARK.M              Larry DeMar         August 12, 1988         *
;*                                                                        *
;*    This facility manipulates Brief bookmarks in a "circular"           *
;*    fashion such that as new bookmarks are dropped they are added       *
;*    to the queue, and going from bookmark to bookmark may be done       *
;*    using "earliar_mark" and "later_mark" commands (similar to          *
;*    the way BRIEF buffers are traversed using Alt-n and Alt-minus).     *
;*                                                                        *
;*    These macros use the 10 bookmark system internal to Brief.          *
;*    This means that the bookmarks dropped will be saved and             *
;*    restored by the "restore" macro if used.  It also means that        *
;*    you may view (and goto) your bookmarks using <Alt-j><Tab>.          *
;*                                                                        *
;*    The purpose of this facility was to eliminate the need to           *
;*    deal with bookmarks by number.  Each time you drop a bookmark       *
;*    it is assigned the next successive number (first drop = 1,          *
;*    second drop = 2 etc.).  When any of the 4 macros in this            *
;*    package are called, the dropped bookmarks will be packed into       *
;*    the lowest positions available.  (This will only have an effect     *
;*    if a buffer containing bookmarks is deleted, if all of              *
;*    the files aren't available for restore, or if "out of order"        *
;*    bookmarks are dropped using Brief's <Alt-1> <Alt-2> etc.)           *
;*                                                                        *
;*    The 4 macros are:                                                   *
;*                                                                        *
;*    LED_DROP_MARK:    This will drop a bookmark at the current          *
;*                      cursor position.  It will assign the              *
;*                      next available bookmark number and                *
;*                      print a confirmation message (along with          *
;*                      a beep).  I like the confirmation beep            *
;*                      for dropping a mark....if you don't, its          *
;*                      the "(beep)" instruction in do_drop.              *
;*                                                                        *
;*                      If all 10 marks are in use, mark 1 will           *
;*                      be deleted, the other 9 will be shited            *
;*                      down 1 and the current cursor location            *
;*                      will become mark 10.                              *
;*                                                                        *
;*                      Marks dropped by this system (regardless          *
;*                      of buffer deletions and partial restores)         *
;*                      will always be in historical order (the           *
;*                      newest mark will have the highest number).        *
;*                                                                        *
;*    LED_HIGHEST_MARK: This will take you to the last bookmark           *
;*                      that was dropped (the newest).                    *
;*                                                                        *
;*    LED_EARLIER_MARK: This will take you to the next earlier mark       *
;*                      from the one last visited.  Note that alternate   *
;*                      use of LED_HIGHEST_MARK and LED_EARLIER_MARK      *
;*                      will take you back and forth between the 2        *
;*                      newest marks.  After going to mark 1,             *
;*                      LED_EARLIER_MARK takes you back to the highest    *
;*                      mark.                                             *
;*                                                                        *
;*    LED_LATER_MARK:   This will take you to the next later mark         *
;*                      from the one last visited.  After going           *
;*                      to the highest mark this takes you to             *
;*                      mark 1.  I don't use this, but provided           *
;*                      it for completeness. Going to the                 *
;*                      most recent mark and working your way             *
;*                      back works really well.                           *
;*                                                                        *
;*    Of course to make this useful at all, you need to assign these      *
;*    macros to keys.  Provide the following autoload statement in        *
;*    your initials macro:                                                *
;*                                                                        *
;*       (autoload "bookmark.cm" "led_drop_mark" "led_highest_mark"       *
;*                                  "led_later_mark" "led_earlier_mark")  *
;*                                                                        *
;*    and some key assignments (mine are based on an old editor I         *
;*    used to use.....I'm sure you can think of better mnemonic           *
;*    assignments):                                                       *
;*                                                                        *
;*       ;Ctrl-V drops a new bookmark.                                    *
;*       (assign_to_key "<Ctrl-v>" "led_drop_mark")                       *
;*                                                                        *
;*       ;Ctrl-Y goes to the last one dropped.                            *
;*       (assign_to_key "<Ctrl-y>" "led_highest_mark")                    *
;*                                                                        *
;*       ;Ctrl-E cycles back to earlier marks.                            *
;*       (assign_to_key "<Ctrl-e>" "led_earlier_mark")                    *
;*                                                                        *
;*       ;Ctrl-J cycles forward to later marks.                           *
;*       (assign_to_key "<Ctrl-j>" "led_later_mark")                      *
;*                                                                        *
;*    If anyone uses this and likes it, or has ideas for                  *
;*    improvement, please let me know on the Brief BBS.                   *
;*                                                                        *
;*                   Larry DeMar                                          *
;*                                                                        *
;**************************************************************************

;**************************************************************************
;*                                                                        *
;*    LED_DROP_MARK                                                       *
;*                                                                        *
;*    This macro will drop a new mark.  FIND_FIRST_UNUSED will            *
;*    pack all existing marks and return the number we should             *
;*    use.  If all 10 marks are in use then we discard #1                 *
;*    and copy down marks 2-10.                                           *
;*                                                                        *
;**************************************************************************
(macro led_drop_mark
   (
      (int goto_pointer)
      (global goto_pointer)
      (int mark_num)
      (if (= mark_num (find_first_unused))
          (
             (do_drop mark_num)
             (return 0)
          )
      )
;**************************************************************************
;*                                                                        *
;*    Now we need to copy all bookmarks down a step                       *
;*                                                                        *
;**************************************************************************
      (int i)
      (while (< i 9)
         (copy_bookmark (+ i 2) (++ i))      ;from -- to
      )
      (do_drop 10)         ;now drop #10
   )
)

;**************************************************************************
;*                                                                        *
;*    LED_HIGHEST_MARK                                                    *
;*                                                                        *
;*    This takes us to the highest mark.  FIND_HIGHEST packs              *
;*    all the marks and returns the highest used mark.                    *
;*    (0 if there are no marks).                                          *
;*                                                                        *
;**************************************************************************
(macro led_highest_mark
   (
      (int last)
      (if (= last (find_highest))            ;get highest book mark
         (
            (position_bookmark last)
            (= goto_pointer last)
         )
         ;else
         (
            (beep)
            (message "No bookmarks!")
         )
      )
   )
)

;**************************************************************************
;*                                                                        *
;*    LED_EARLIER_MARK                                                    *
;*                                                                        *
;*    This takes us to the mark before the last one operated on           *
;*    (previously droped in history).  After visiting mark 1, the         *
;*    highest mark will be set again.                                     *
;*                                                                        *
;**************************************************************************
(macro led_earlier_mark
   (
      (int last)

      (= last (find_highest))                ;get the highest mark.

      (if (|| (> goto_pointer last)    ;if goto pointer out of range
              (! goto_pointer)         ;or if goto_pointer undefined
          )
          (  (led_highest_mark)
             (return 0)
          )
      )
      (if (-- goto_pointer)
            (position_bookmark goto_pointer)
         ;else
            (led_highest_mark)            ;we wrapped around....back to highest
      )
   )
)

;**************************************************************************
;*                                                                        *
;*    LED_LATER_MARK                                                      *
;*                                                                        *
;*    Move to next mark (higher.....later in history), or go to           *
;*    first if you're on highest.                                         *
;*                                                                        *
;**************************************************************************
(macro led_later_mark
   (
      (int last)
      (if (!  (&& goto_pointer (= last (find_highest)))) ;pointer zero or no macros?
          (  (led_highest_mark)              ;if un-initialized return highest
             (return 0)                      ;if none, this will report the error.
          )
      )

      (if (> (++ goto_pointer) last)    ;if goto pointer left out after packing
         (= goto_pointer 1)
      )
      (position_bookmark goto_pointer)
   )
)

;**************************************************************************
;*                                                                        *
;*    COPY_BOOKMARK                                                       *
;*                                                                        *
;*    This macro copies a bookmark from 1 position to another.            *
;*    Its calling sequence is (copy_bookmark <source number>              *
;*                                          <destination number>)         *
;*                                                                        *
;**************************************************************************
(macro copy_bookmark
   (
      (int buf line col)
      (int from to)
      (get_parm 0 from)
      (get_parm 1 to)
      (goto_bookmark from buf line col)  ;get #2
      (drop_bookmark to "y" buf line col)
   )
)

;**************************************************************************
;*                                                                        *
;*    DO_DROP                                                             *
;*                                                                        *
;*    This does the work to drop bookmark for its parameter number.       *
;*                                                                        *
;**************************************************************************
(macro do_drop
   (
      (int drop_num)
      (get_parm 0 drop_num)
      (drop_bookmark drop_num "y")
      (message "bookmark %d dropped" drop_num) 
                                  ;
      (beep)                      ;THIS IS THE LINE TO REMOVE IF YOU DON'T
                                  ;WANT THE BEEP WHEN MARKS ARE DROPPED.
                                  ;
      (= goto_pointer drop_num)   ;"this is where we should goto"
   )
)

;**************************************************************************
;*                                                                        *
;*    FIND_FIRST_UNUSED                                                   *
;*                                                                        *
;*    This macro finds the first unused bookmark                          *
;*    and returns the mark number 1-9.  If all bookmarks have             *
;*    been used, then it returns 0 (false).                               *
;*                                                                        *
;*    This macro is called somewhere in the sequence of all 4             *
;*    user level commands.  It has the side effect of packing             *
;*    all bookmarks into the lowest available slots.                      *
;*                                                                        *
;**************************************************************************
(macro find_first_unused
   (
      (int buf line col val)
      (int mark_to_check)
      (= val 1)                                 ;make while go once.
      (while val
         (
            (++ mark_to_check)                  ;start with 1
            (if (== mark_to_check 11)
                (return 0)
            )
            (= val (goto_bookmark mark_to_check buf line col))
         )
      )
;**************************************************************************
;*                                                                        *
;*       we found a vacant slot.....see if there are any marks above us.  *
;*                                                                        *
;**************************************************************************
      (int valid_one)
      (while (= valid_one (find_above mark_to_check))
         (
            (copy_bookmark valid_one mark_to_check)   ;copy guy above us
            (delete_bookmark valid_one)               ;get rid of that one
            (++ mark_to_check)                        ;move to next spot
         )
      )
      (return mark_to_check)
   )
)
            
;**************************************************************************
;*                                                                        *
;*    FIND_ABOVE                                                          *
;*                                                                        *
;*    This is a utility for FIND_FIRST_UNUSED.  Once it finds the         *
;*    lowest unused, it needs to compress the rest of the bookmarks.      *
;*    We get as a parameter the number of the lowest vacant slot.         *
;*    We need to return the number of the lowest USED slot above          *
;*    the number passed to us (or zero for none).                         *
;*                                                                        *
;**************************************************************************
(macro find_above
   (
      (int pointer)
      (int buf line col)
      (get_parm 0 pointer)
      (while  (< (++ pointer) 11)
         (if (goto_bookmark pointer buf line col)
            (return pointer)
         )
      )
      (return 0)
   )
)

;**************************************************************************
;*                                                                        *
;*    DELETE_BOOKMARK                                                     *
;*                                                                        *
;*    This macro "deletes" a bookmark by assigning buffer, line           *
;*    and column to ZERO......THIS SEEMS TO WORK, BUT IS NOT              *
;*    SUPPORTED BY THE DOCUMENTATION...(There is no way given             *
;*    to "delete" a bookmark.)                                            *
;*                                                                        *
;**************************************************************************
(macro delete_bookmark
   (
      (int bm)
      (get_parm 0 bm)
      (drop_bookmark bm "y" 0 0 0)
   )
)

;**************************************************************************
;*                                                                        *
;*    POSITION_BOOKMARK                                                   *
;*                                                                        *
;*    This takes us to the bookmark number specified in the first         *
;*    argument.  The messages are in case it takes awhile to get          *
;*    there.                                                              *
;*                                                                        *
;**************************************************************************
(macro position_bookmark
   (
      (int mark_num)
      (get_parm 0 mark_num)
      (message "Positioning Bookmark %d..." mark_num)
      (goto_bookmark mark_num)
      (message "Bookmark %d restored." mark_num)
   )
)

;**************************************************************************
;*                                                                        *
;*    FIND_HIGHEST                                                        *
;*                                                                        *
;*    This routine is called to find the highest bookmark                 *
;*    in use (after compression).  It returns 0 if there are              *
;*    no bookmarks.                                                       *
;*                                                                        *
;**************************************************************************
(macro find_highest
   (
      (int highest)
      (if (! (= highest (find_first_unused)))   ;this is first UNUSED
          (= highest 11)
      )
      (return (-- highest))         ;send back last USED
   )
)
