footnote.t

documentation
#charset "us-ascii"

/* 
 *   FOOTNOTE EXTENSION
 *
 *   Copyright (c) 2000, 2006 Michael J. Roberts.  All Rights Reserved. 
 *   
 *   TADS 3 Library - footnotes
 *   
 *   This module defines objects related to footnotes.  
 *
 *   Slightly modified for use with adv3Lite by Eric Eve
 */

/* include the library header */
#include "advlite.h"


/* ------------------------------------------------------------------------ */
/*
 *   Footnote - this allows footnote references to be generated in
 *   displayed text, and the user to retrieve the contents of the footnote
 *   on demand.
 *   
 *   Create an instance of Footnote for each footnote.  For each footnote
 *   object, define the "desc" property as a double-quoted string (or
 *   method) displaying the footnote's contents.
 *   
 *   To display a footnote reference in a passage of text, call
 *   <<x.noteRef>>, where x is the footnote object in question.  That's all
 *   you have to do - we'll automatically assign the footnote a sequential
 *   number (so that footnote references are always seen by the player in
 *   ascending order, regardless of the order in which the player
 *   encounters the sources of the footnotes), and the NOTE command will
 *   automatically figure out which footnote object is involved for a given
 *   footnote number.
 *   
 *   This class also serves as a daemon notification object to receive
 *   per-command daemon calls.  The first time we show a footnote
 *   reference, we'll show an explanation of how footnotes work.  
 *
 *   [ONLY IN FOOTNOTE EXTENSION]
 */
class Footnote: object
    /* 
     *   Display the contents of the footnote - this will be called when
     *   the user asks to show the footnote with the "NOTE n" command.
     *   Each instance must provide suitable text here.  
     */
    desc = ""

    /*
     *   Get a reference to the footnote for use in a passage of text.
     *   This returns a single-quoted string to display as a reference to
     *   the footnote.  
     */
    noteRef
    {
               
        /* 
         *   if we haven't already assigned a number to this footnote,
         *   assign one now 
         */
        if (footnoteNum == nil)
        {
            /* 
             *   Allocate a new footnote number and remember it as our
             *   own.  Note that we want the last footnote number for all
             *   footnotes, so use the Footnote class property
             *   lastFootnote. 
             */
            footnoteNum = ++(Footnote.lastFootnote);

            /* 
             *   add myself to the class's list of numbered notes, so we
             *   can find this footnote easily again given its number 
             */
            Footnote.numberedFootnotes.append(self);

            /* note that we've generated a footnote reference */
            Footnote.everShownFootnote = true;
        }

        /* 
         *   If we're allowed to show footnotes, return the library
         *   message text to display given the note number.  If all
         *   footnotes are being hidden, or if we're only showing new
         *   footnotes and we've already read this one, return an empty
         *   string.  
         */
        switch(footnoteSettings.showFootnotes)
        {
        case FootnotesFull:
            /* we're showing all footnotes unconditionally */
            return footnoteRef(footnoteNum);

        case FootnotesMedium:
            /* we're only showing unread footnotes */
            return footnoteRead ? '' : footnoteRef(footnoteNum);

        case FootnotesOff:
            /* we're hiding all footnotes unconditionally */
            return '';
        }

        /* 
         *   in case the status is invalid and we fall through, return an
         *   empty string as a last resort
         */
        return '';
    }

    /* get the string to display for a footnote reference */
    footnoteRef(num)
    {
        /* set up a hyperlink for the note that enters the "note n" command */
        return BMsg(footnote ref, '<sup>[<<aHref('footnote ' + num, 
                                                 toString(num))>>]</sup>');
    }
    
    
    /*
     *   Display a footnote given its number.  If there is no such
     *   footnote, we'll display an error message saying so.  (This is a
     *   class method, so it should be called directly on Footnote, not on
     *   instances of Footnote.)  
     */
    showFootnote(num)
    {
        /* 
         *   if there's a footnote for this number, display it; otherwise,
         *   display an error explaining that the footnote number is
         *   invalid 
         */
        if (num >= 1 && num <= lastFootnote)
        {
            local fn;

            /* 
             *   it's a valid footnote number - get the footnote object
             *   from our vector of footnotes, simply using the footnote
             *   number as an index into the vector
             */
            fn = numberedFootnotes[num];

            /* show its description by calling 'desc' method */
            fn.desc;

            /* note that this footnote text has been read */
            fn.footnoteRead = true;
        }
        else
        {
            /* there is no such footnote */
             DMsg(no such footnote, '<.parser>The story has never referred to
                 any such footnote.<./parser> ');
        }
    }

    /* SettingsItem tracking our current status */
    footnoteSettings = footnoteSettingsItem

    /* 
     *   my footnote number - this is assigned the first time I'm
     *   referenced; initially we have no number, since we don't want to
     *   assign a number until the note is first referenced 
     */
    footnoteNum = nil

    /* 
     *   Flag: this footnote's full text has been displayed.  This refers
     *   to the text of the footnote itself, not the reference, so this is
     *   only set when the "FOOTNOTE n" command is used to read this
     *   footnote.  
     */
    footnoteRead = nil

    /*
     *   Static property: the highest footnote number currently in use.
     *   We start this at zero, because zero is never a valid footnote
     *   number.  
     */
    lastFootnote = 0

    /*
     *   Static property: a vector of all footnotes which have had numbers
     *   assigned.  We use this to find a footnote object given its note
     *   number.  
     */
    numberedFootnotes = static new Vector(20)

    /* static property: we've never shown a footnote reference before */
    everShownFootnote = nil

    /* static property: per-command-prompt daemon entrypoint */
    checkNotification()
    {
        /*
         *   If we've ever shown a footnote, show the footnote
         *   notification now.  Note that we know we've never shown a
         *   notification before simply because we're still running - we
         *   remove this daemon as soon as it shows its notification.  
         */
        if (everShownFootnote)
        {
            /* show the first footnote notification */
            DMsg(first footnote,  'A number in [square brackets] like the one
                above refers to a footnote, which you can read by typing
                FOOTNOTE followed by the number: <<aHref('footnote 1',
                    'FOOTNOTE 1', 'Show footnote [1]')>>, for example.
                Footnotes usually contain added background information that
                might be interesting but isn&rsquo;t essential to the story. If
                you&rsquo;d prefer not to see footnotes at all, you can control
                their appearance by typing <<aHref('footnotes', 'FOOTNOTES', 
                                                   'Control footnote
        appearance')>>.');

            /* 
             *   We only want to show this notification once in the whole
             *   game, so we can cancel this daemon now.  Since we're the
             *   event that's running, we can just tell the event manager
             *   to remove the current event from receiving further
             *   notifications.  
             */
            eventManager.removeCurrentEvent();
        }
    }
;

/* our FOOTNOTES settings item [FOOTNOTE EXTENSION]*/
footnoteSettingsItem: object
    /* our current status - the factory default is "medium" */
    showFootnotes = FootnotesMedium
   
    /* 
     * get the setting's external file string representation 
     * [FOOTNOTE EXTENSION]  
     */
    settingToText()
    {
        switch(showFootnotes)
        {
        case FootnotesMedium:
            return 'medium';
            
        case FootnotesFull:
            return 'full';
            
        default:
            return 'off';
        }
    }

    settingFromText(str)
    {
        /* convert to lower-case and strip off spaces */
        if (rexMatch('<space>*(<alpha>+)', str.toLower()) != nil)
            str = rexGroup(1)[3];
        
        /* check the keyword */
        switch (str)
        {
        case 'off':
            showFootnotes = FootnotesOff;
            break;
            
        case 'medium':
            showFootnotes = FootnotesMedium;
            break;
            
        case 'full':
            showFootnotes = FootnotesFull;
            break;
        }
    }
;

/* 
 * pre-initialization - set up the footnote explanation daemon 
 * [FOOTNOTE EXTENSION]
 */
footnotePreinit: PreinitObject
    execute()
    {
        /* since we're available, register as the global footnote handler */
        libGlobal.footnoteClass = Footnote;

        /* initialize the footnote notification daemon */
        new PromptDaemon(Footnote, &checkNotification);
    }
;

/*  
 *  This VerbRule is defined directly in the FOOTNOTE
 *  extension.
 *
 *  Note to translators: the following VerbRules are defined directly in
 *  the FOOTNOTES extension file since it would be awkward to put them
 *  anywhere else. When translating, define an additional language-specific
 *  file (e.g. footnotes_fr.t or footnotes_de.t) and in it include (a)
 *  your language-specific modifications to the following VerbRules
 *  (using modify VerbRule) and (b) a CustomMessages object containing
 *  translations of any DMsg and BMsg text used in this extension. Then
 *  instruct users to include your language-specific file after this one.
 *  [FOOTNOTE EXTENSION]
 */
VerbRule(Footnote)
    ('footnote' | 'note') numericDobj
    : VerbProduction
    action = FootnoteAction
    verbPhrase = 'show/showing a footnote'
;


VerbRule(FootnotesFull)
    'footnotes' 'full'
    : VerbProduction
    action = FootnotesFullAction
    verbPhrase = 'enable/enabling all footnotes'
;

VerbRule(FootnotesMedium)
    'footnotes' 'medium'
    : VerbProduction
    action = FootnotesMediumAction
    verbPhrase = 'enable/enabling new footnotes'
;

VerbRule(FootnotesOff)
    'footnotes' 'off'
    : VerbProduction
    action = FootnotesOffAction
    verbPhrase = 'hide/hiding footnotes'
;

VerbRule(FootnotesStatus)
    'footnotes'
    : VerbProduction
    action = FootnotesStatus
    verbPhrase = 'show/showing footnote status'
;




/*
 *   Footnote -   [FOOTNOTE EXTENSION]
 */
DefineSystemAction(FootnoteAction)
    execAction(c)
    {
        /* ask the Footnote class to do the work */
        if (libGlobal.footnoteClass != nil)
        {
            local num = c.dobj.numVal;
            
            libGlobal.footnoteClass.showFootnote(num);            
        }
        else
            commandNotPresent();
    }

    /* there's no point in including this in undo */
    includeInUndo = nil
;


/* base class for FOOTNOTES xxx commands [FOOTNOTE EXTENSION]*/
DefineSystemAction(Footnotes)
    execAction(c)
    {
        if (libGlobal.footnoteClass != nil)
        {
            /* set my footnote status in the global setting */
            libGlobal.footnoteClass.footnoteSettings.showFootnotes =
                showFootnotes;

            /* acknowledge it */
            acknowledgeFootnoteStatus(showFootnotes);
        }
        else
            commandNotPresent();
    }

    /* 
     *   the footnote status I set when this command is activated - this
     *   must be overridden by each subclass 
     */
    showFootnotes = nil
    
    acknowledgeFootnoteStatus(stat)
    {
        DMsg(acknowledge footnote status, '<.parser>The setting is now {1}.
            <./parser>', shortFootnoteStatus(stat));
    }

    /* show the footnote status, in short form */
    shortFootnoteStatus(stat)
    {
        local msg = BMsg(footnotes, 'FOOTNOTES ');
        
        msg += (stat == FootnotesOff ? BMsg(footnote off, 'OFF')
          : stat == FootnotesMedium ? BMsg(footnote medium, 'MEDIUM')
          : BMsg(footnote full, 'FULL'));
        
        return msg;
    }
;

DefineAction(FootnotesFullAction, Footnotes)
    showFootnotes = FootnotesFull
;

DefineAction(FootnotesMediumAction, Footnotes)
    showFootnotes = FootnotesMedium
;

DefineAction(FootnotesOffAction, Footnotes)
    showFootnotes = FootnotesOff
;

/* [FOOTNOTE EXTENSION] */
DefineSystemAction(FootnotesStatus)
    execAction(c)
    {
        /* show the current status */
        if (libGlobal.footnoteClass != nil)
            showFootnoteStatus(libGlobal.footnoteClass.
                                            footnoteSettings.showFootnotes);
        else
            commandNotPresent();
    }

    /* there's no point in including this in undo */
    includeInUndo = nil
    
    showFootnoteStatus(stat)
    {
        "The current setting is FOOTNOTES ";
        switch(stat)
        {
        case FootnotesOff:
            DMsg(show footnotes off,
            'OFF, which hides all footnote references.
            Type <<aHref('footnotes medium', 'FOOTNOTES MEDIUM',
                         'Set footnotes to Medium')>> to
            show references to footnotes except those you&rsquo;ve
            already seen, or <<aHref('footnotes full', 'FOOTNOTES FULL',
                                     'Set footnotes to Full')>>
            to show all footnote references. ');
            break;

        case FootnotesMedium:
            DMsg(show footnotes medium,
            'MEDIUM, which shows references to unread footnotes, but
            hides references to those you&rsquo;ve already read.  Type
            <<aHref('footnotes off', 'FOOTNOTES OFF',
                    'Turn off footnotes')>> to hide
            footnote references entirely, or <<aHref(
                'footnotes full', 'FOOTNOTES FULL',
                'Set footnotes to Full')>> to show every reference, even to
            notes you&rsquo;ve already read. ');
            break;

        case FootnotesFull:
            DMsg(show footnotes full,
            'FULL, which shows every footnote reference, even to
            notes you&rsquo;ve already read.  Type <<aHref('footnotes medium',
            'FOOTNOTES MEDIUM', 'Set footnotes to Medium')>> to show
            only references to notes you
            haven&rsquo;t yet read, or <<
              aHref('footnotes off', 'FOOTNOTES OFF', 'Turn off footnotes')>>
            to hide footnote references entirely. ');
            break;
        }
    }
;

Adv3Lite Library Reference Manual
Generated on 03/07/2024 from adv3Lite version 2.1