PROMPTREMINDERS -- to be periodically reminded of things

File:		<lispusers>PromptReminders.tty
Revised: 	Feb 21, 1983, and Apr 15, 1984, by JonL White


    One may want to be periodically "reminded" of important things by 
a message which is aggressively "winked" and "flashed" in a prompt 
window (or on primary output for systems without bitmap display, such 
as Interlisp-10 and Interlisp/VAX).   It will desist the wink/flash 
"hassling" only after it has been acknowledged by user response, or 
after a pre-set interval of "hassling" time has elapsed.  Such is 
implemented using an Interlisp-D background process (or for the non-
Interlisp-D systems, the facility of PROMPTCHARFORMS).

    If the MESSAGE given for the reminder (see description of the 
function SETREMINDER below) is a listp, then when the reminder 
"goes off", that listp will be EVAL'd  rather than any of the "winking",
"flashing", or "hassling" mentioned above.

    In Interlisp-D, the global variable REMINDERSTREAM holds the window 
(or, indeed, stream!) that the "winking/flashing" is to occur; if not
set by the user, it will default to PROMPTWINDOW.  After the "hassling"
has completed, the window (if indeed REMINDERSTREAM holds a window) will
be closed, depending on the value of CLOSEREMINDERSTREAMFLG.

    REMINDERS is a filepkg type, so that they may be easily saved on 
files, and so that the general typed-definition facilities may be used. 
On any file which uses the REMINDERS filepkgcom, it is advisable to 
precede this command with a  command
    (FILES (SYSLOAD COMPILED FROM LISPUSERS) PROMPTREMINDERS) 
since this package is not in the initial Lisp loadup.   When initially 
defining a reminder, it is preferable for the user to call SETREMINDER 
rather than PUTDEF;  but HASDEF is the accepted way to ask if some name 
currently defines a "reminder", and DELDEF is the accepted way to cancel 
an existing "reminder".

    In the first example below, the user wants to be reminded every 30 
minutes that he ought to be using MAKEFILE to save his work;  in the 
second example, he merely wants to be told once, at precisely 4:00PM to 
call home;  in the third, he merely checks every 10 minutes to see if 
there is a process called LISTFILES.    Examples:

    (SETREMINDER NIL (ITIMES 30 60) "Have you MADEFILE recently?")

    (SETREMINDER 'WOOF NIL "Don't forget to inform wife of dinner plans."
                 "8-Jan-83 4:00PM")

    (SETREMINDER NIL 600 
            '(PROGN (AND (FIND.PROCESS 'LISTFILES) (add FREQ 1))
                    (add TOTAL 1)))




                            Functions: 

    (SETREMINDER NAME PERIOD MESSAGE INITIALDELAY EXPIRATION)
            This will create and install a "reminder" with the name NAME 
        (NIL given for a name will be replaced by a gensym), which will 
        be executed every PERIOD number of seconds by winking the string
        MESSAGE into the prompt window; if MESSAGE is null, then NAME is
        winked; if MESSAGE is a listp, then it is EVAL'd and no "winking"
        takes place.  "Winking" means alternately printing the message and 
        clearing the window in which it was printed, at a rate designed to 
        attract the eye's attention.  
            The first such execution will occur at PERIOD seconds after 
        the call to SETREMINDER unless INITIALDELAY is non-NIL, in which
        case that time will be used; a fixp value for INITIALDELAY is
        interpreted as an offset in seconds from the time of the call to 
        SETREMINDER, and a stringp value is an absolute date/time string.
            If PERIOD is null, then the reminder is to be run precisely
        once.  If EXPIRATION is non-null, then a fixp means that that 
        number of seconds after the first execution, the timer will be
        deleted;  a stringp means a precise date/time at which to delete
        the timer.
            Optional 6th and 7th arguments  -- called REMINDINGDURATION  
        and WINKINGDURATION -- permit one to vary the amount of time spent 
        in one cycle of the wink/flash loop, and the amount of time spent 
        winking before initiating a "flash".  The attention-attracting 
        action will continue for REMINDINGDURATION seconds (default: the
        value of the global variable DEFAULT.REMINDER.DURATION which is
        initialized to 60), or until the user types something on the 
        keyboard; care is taken not to consume the typed character.  
        Type-ahead does not release the winking.  In case the user has 
        become "drowsy", or otherwise fails to notice the winking, then 
        every WINKINGDURATION seconds (default: the value of the global 
        variable DEFAULT.REMINDER.WINKINGDURATION which is initialized 
        to 10) during the "reminding", the whole display videocolor will 
        be wagged back and forth a few times, which effects a most obnoxious 
        stimulus (for non-bitmap systems, this just types some <bell>'s).

            Returns the name (note above when NIL is supplied for the 
        name).


    (ACTIVEREMINDERNAMES) 
            No arguments; self-explanatory.

    (REMINDER.NEXTREMINDDATE NAME)
            Returns the time (in GDATE format) at which the next reminding 
        from the named reminder will occur; NIL if NAME isn't a REMINDERS.
    (REMINDER.NEXTREMINDDATE NAME Date/Time.string)
            Sets the time at which the reminder is next to be executed.

    (REMINDER.EXPIRATIONDATE NAME)
            Returns the time (in GDATE format) at which the reminder will 
        be automatically deleted.
    (REMINDER.EXPIRATIONDATE NAME Date/Time.string)
            Sets the expiration time.

    (INSPECTREMINDER NAME)
            In Interlisp-D, this will call INSPECT on the definition of 
        the named reminder; in other systems, it merely calls SHOWDEF.



                  Function in Interlisp-D only:

    (UNTILKEYDOWNP FN INTERVAL.SECS DURATION.SECS 
                                    subCycleDuration.secs subCycleFN)
            For a period of up to DURATION.SECS seconds, the function FN 
        will be "run" (i.e., applied to no arguments) every INTERVAL.SECS 
        seconds; both of these time durations may be floatp's, and hence 
        specify fractional parts of a second.  The process is stopped 
        whenever there is any change in the state of the keyboard; except 
        that CTRL, LOCK, LSHIFT, RSHIFT, and LEFT and RIGHT (tow of the 
        three mouse buttons) don't count.
            The function FN, of course, may save "state" in order to do 
        different things cyclically on the various "runnings".  But a 
        common structure is to have a major cycle and a minor cycle for 
        which some activity is to be performed.  The two optional 
        arguments, subCycleDuration.secs and subCycleFN, allow for this 
        second function to be "run" at the end of the subCycle duration 
        time.  
            For example, INTERVAL.SECS may be 0.5 in order to cause a 
        "winking" of a message in the PROMPTWINDOW every second or so, and 
        subCycleDuration.secs may be 10.0 in order to cause a "flashing" 
        of the whole screen every 10 seconds or so.  In this case, the 
        major cycle is the "fast rate winking" every second, whereas the 
        minor cycle is the "slow rate flashing" every 10 secs.