cfad47cfa3/t3compiler/tads3/lib/adv3/footnote.t

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#charset "us-ascii"
2
3
/* 
4
 *   Copyright (c) 2000, 2006 Michael J. Roberts.  All Rights Reserved. 
5
 *   
6
 *   TADS 3 Library - footnotes
7
 *   
8
 *   This module defines objects related to footnotes.  
9
 */
10
11
/* include the library header */
12
#include "adv3.h"
13
14
15
/* ------------------------------------------------------------------------ */
16
/*
17
 *   Footnote - this allows footnote references to be generated in
18
 *   displayed text, and the user to retrieve the contents of the footnote
19
 *   on demand.
20
 *   
21
 *   Create an instance of Footnote for each footnote.  For each footnote
22
 *   object, define the "desc" property as a double-quoted string (or
23
 *   method) displaying the footnote's contents.
24
 *   
25
 *   To display a footnote reference in a passage of text, call
26
 *   <<x.noteRef>>, where x is the footnote object in question.  That's all
27
 *   you have to do - we'll automatically assign the footnote a sequential
28
 *   number (so that footnote references are always seen by the player in
29
 *   ascending order, regardless of the order in which the player
30
 *   encounters the sources of the footnotes), and the NOTE command will
31
 *   automatically figure out which footnote object is involved for a given
32
 *   footnote number.
33
 *   
34
 *   This class also serves as a daemon notification object to receive
35
 *   per-command daemon calls.  The first time we show a footnote
36
 *   reference, we'll show an explanation of how footnotes work.  
37
 */
38
class Footnote: object
39
    /* 
40
     *   Display the contents of the footnote - this will be called when
41
     *   the user asks to show the footnote with the "NOTE n" command.
42
     *   Each instance must provide suitable text here.  
43
     */
44
    desc = ""
45
46
    /*
47
     *   Get a reference to the footnote for use in a passage of text.
48
     *   This returns a single-quoted string to display as a reference to
49
     *   the footnote.  
50
     */
51
    noteRef
52
    {
53
        /* 
54
         *   If the sensory context is blocking output, do not consider
55
         *   this a reference to the footnote at all, since the player
56
         *   won't see it.  
57
         */
58
        if (senseContext.isBlocking)
59
            return '';
60
        
61
        /* 
62
         *   if we haven't already assigned a number to this footnote,
63
         *   assign one now 
64
         */
65
        if (footnoteNum == nil)
66
        {
67
            /* 
68
             *   Allocate a new footnote number and remember it as our
69
             *   own.  Note that we want the last footnote number for all
70
             *   footnotes, so use the Footnote class property
71
             *   lastFootnote. 
72
             */
73
            footnoteNum = ++(Footnote.lastFootnote);
74
75
            /* 
76
             *   add myself to the class's list of numbered notes, so we
77
             *   can find this footnote easily again given its number 
78
             */
79
            Footnote.numberedFootnotes.append(self);
80
81
            /* note that we've generated a footnote reference */
82
            Footnote.everShownFootnote = true;
83
        }
84
85
        /* 
86
         *   If we're allowed to show footnotes, return the library
87
         *   message text to display given the note number.  If all
88
         *   footnotes are being hidden, or if we're only showing new
89
         *   footnotes and we've already read this one, return an empty
90
         *   string.  
91
         */
92
        switch(footnoteSettings.showFootnotes)
93
        {
94
        case FootnotesFull:
95
            /* we're showing all footnotes unconditionally */
96
            return gLibMessages.footnoteRef(footnoteNum);
97
98
        case FootnotesMedium:
99
            /* we're only showing unread footnotes */
100
            return footnoteRead ? '' : gLibMessages.footnoteRef(footnoteNum);
101
102
        case FootnotesOff:
103
            /* we're hiding all footnotes unconditionally */
104
            return '';
105
        }
106
107
        /* 
108
         *   in case the status is invalid and we fall through, return an
109
         *   empty string as a last resort
110
         */
111
        return '';
112
    }
113
114
    /*
115
     *   Display a footnote given its number.  If there is no such
116
     *   footnote, we'll display an error message saying so.  (This is a
117
     *   class method, so it should be called directly on Footnote, not on
118
     *   instances of Footnote.)  
119
     */
120
    showFootnote(num)
121
    {
122
        /* 
123
         *   if there's a footnote for this number, display it; otherwise,
124
         *   display an error explaining that the footnote number is
125
         *   invalid 
126
         */
127
        if (num >= 1 && num <= lastFootnote)
128
        {
129
            local fn;
130
131
            /* 
132
             *   it's a valid footnote number - get the footnote object
133
             *   from our vector of footnotes, simply using the footnote
134
             *   number as an index into the vector
135
             */
136
            fn = numberedFootnotes[num];
137
138
            /* show its description by calling 'desc' method */
139
            fn.desc;
140
141
            /* note that this footnote text has been read */
142
            fn.footnoteRead = true;
143
        }
144
        else
145
        {
146
            /* there is no such footnote */
147
            gLibMessages.noSuchFootnote(num);
148
        }
149
    }
150
151
    /* SettingsItem tracking our current status */
152
    footnoteSettings = footnoteSettingsItem
153
154
    /* 
155
     *   my footnote number - this is assigned the first time I'm
156
     *   referenced; initially we have no number, since we don't want to
157
     *   assign a number until the note is first referenced 
158
     */
159
    footnoteNum = nil
160
161
    /* 
162
     *   Flag: this footnote's full text has been displayed.  This refers
163
     *   to the text of the footnote itself, not the reference, so this is
164
     *   only set when the "FOOTNOTE n" command is used to read this
165
     *   footnote.  
166
     */
167
    footnoteRead = nil
168
169
    /*
170
     *   Static property: the highest footnote number currently in use.
171
     *   We start this at zero, because zero is never a valid footnote
172
     *   number.  
173
     */
174
    lastFootnote = 0
175
176
    /*
177
     *   Static property: a vector of all footnotes which have had numbers
178
     *   assigned.  We use this to find a footnote object given its note
179
     *   number.  
180
     */
181
    numberedFootnotes = static new Vector(20)
182
183
    /* static property: we've never shown a footnote reference before */
184
    everShownFootnote = nil
185
186
    /* static property: per-command-prompt daemon entrypoint */
187
    checkNotification()
188
    {
189
        /*
190
         *   If we've ever shown a footnote, show the footnote
191
         *   notification now.  Note that we know we've never shown a
192
         *   notification before simply because we're still running - we
193
         *   remove this daemon as soon as it shows its notification.  
194
         */
195
        if (everShownFootnote)
196
        {
197
            /* show the first footnote notification */
198
            gLibMessages.firstFootnote();
199
200
            /* 
201
             *   We only want to show this notification once in the whole
202
             *   game, so we can cancel this daemon now.  Since we're the
203
             *   event that's running, we can just tell the event manager
204
             *   to remove the current event from receiving further
205
             *   notifications.  
206
             */
207
            eventManager.removeCurrentEvent();
208
        }
209
    }
210
;
211
212
/* our FOOTNOTES settings item */
213
footnoteSettingsItem: SettingsItem
214
    /* our current status - the factory default is "medium" */
215
    showFootnotes = FootnotesMedium
216
217
    /* our config file variable ID */
218
    settingID = 'adv3.footnotes'
219
    
220
    /* show our short description */
221
    settingDesc = (gLibMessages.shortFootnoteStatus(showFootnotes))
222
223
    /* get the setting's external file string representation */
224
    settingToText()
225
    {
226
        switch(showFootnotes)
227
        {
228
        case FootnotesMedium:
229
            return 'medium';
230
            
231
        case FootnotesFull:
232
            return 'full';
233
            
234
        default:
235
            return 'off';
236
        }
237
    }
238
239
    settingFromText(str)
240
    {
241
        /* convert to lower-case and strip off spaces */
242
        if (rexMatch('<space>*(<alpha>+)', str.toLower()) != nil)
243
            str = rexGroup(1)[3];
244
        
245
        /* check the keyword */
246
        switch (str)
247
        {
248
        case 'off':
249
            showFootnotes = FootnotesOff;
250
            break;
251
            
252
        case 'medium':
253
            showFootnotes = FootnotesMedium;
254
            break;
255
            
256
        case 'full':
257
            showFootnotes = FootnotesFull;
258
            break;
259
        }
260
    }
261
;
262
263
/* pre-initialization - set up the footnote explanation daemon */
264
PreinitObject
265
    execute()
266
    {
267
        /* since we're available, register as the global footnote handler */
268
        libGlobal.footnoteClass = Footnote;
269
270
        /* initialize the footnote notification daemon */
271
        new PromptDaemon(Footnote, &checkNotification);
272
    }
273
;
274