~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads3/lib/adv3/status.t

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#charset "us-ascii"
 
2
 
 
3
/* 
 
4
 *   Copyright (c) 2000, 2006 Michael J. Roberts.  All Rights Reserved. 
 
5
 *   
 
6
 *   TADS 3 Library - Status Line
 
7
 *   
 
8
 *   This module defines the framework for displaying the status line,
 
9
 *   which is the area conventionally displayed at the top of the screen
 
10
 *   showing information such as the current location, score (if scoring is
 
11
 *   used at all), and number of turns.  
 
12
 */
 
13
 
 
14
/* include the library header */
 
15
#include "adv3.h"
 
16
 
 
17
 
 
18
/* ------------------------------------------------------------------------ */
 
19
/* 
 
20
 *   in case the 'score' module isn't included, make sure we can refer to
 
21
 *   totalScore as a property 
 
22
 */
 
23
property totalScore;
 
24
 
 
25
 
 
26
/* ------------------------------------------------------------------------ */
 
27
/*
 
28
 *   The banner window for the status line.  
 
29
 */
 
30
statuslineBanner: BannerWindow
 
31
    removeBanner()
 
32
    {
 
33
        /* inherit the base handling */
 
34
        inherited();
 
35
 
 
36
        /* 
 
37
         *   notify the statusLine object that it needs to refigure the
 
38
         *   display mode 
 
39
         */
 
40
        statusLine.statusDispMode = nil;
 
41
    }
 
42
 
 
43
    /* initialize */
 
44
    initBannerWindow()
 
45
    {
 
46
        /* if we're already initialized, do nothing */
 
47
        if (inited_)
 
48
            return;
 
49
 
 
50
        /* inherit the default handling (to set our 'inited_' flag) */
 
51
        inherited();
 
52
        
 
53
        /* tell the status line to initialize its banner window */
 
54
        statusLine.initBannerWindow(self);
 
55
    }
 
56
;
 
57
 
 
58
 
 
59
/* ------------------------------------------------------------------------ */
 
60
/*
 
61
 *   A special OutputStream for the <BANNER> tag contents.  This is really
 
62
 *   just part of the main output stream, but we use a separate output
 
63
 *   stream object so that we have our own separate stream state variables
 
64
 *   (for paragraph breaking and so forth).  
 
65
 */
 
66
transient statusTagOutputStream: OutputStream
 
67
    /*
 
68
     *   We're really part of the main window's output stream as far as the
 
69
     *   underlying interpreter I/O system is concerned, so we have to
 
70
     *   coordinate with the main game window's input manager. 
 
71
     */
 
72
    myInputManager = inputManager
 
73
 
 
74
    /* we sit atop the system-level main console output stream */
 
75
    writeFromStream(txt)
 
76
    {
 
77
        /* write the text directly to the main output stream */
 
78
        tadsSay(txt);
 
79
    }
 
80
;
 
81
 
 
82
/*
 
83
 *   A special OutputStream for the left half of the status line (the
 
84
 *   short description area) in text mode.  We use a separate stream for
 
85
 *   this because we must write the text using the output mode switching
 
86
 *   for the status line.
 
87
 *   
 
88
 *   We only use this stream when we use the old-style text-mode status
 
89
 *   line interface, which explicitly separates the status line into a
 
90
 *   left part and a right part.  When we have the banner API available in
 
91
 *   the interpreter, we'll use banners instead, since banners give us
 
92
 *   much more flexibility.  
 
93
 */
 
94
transient statusLeftOutputStream: OutputStream
 
95
    /* we sit atop the system-level main console output stream */
 
96
    writeFromStream(txt)
 
97
    {
 
98
        /* write the text directly to the main output stream */
 
99
        tadsSay(txt);
 
100
    }
 
101
;
 
102
 
 
103
/*
 
104
 *   A special OutputStream for the right half of the status line (the
 
105
 *   score/turn count area) in text mode.  We use a separate stream for
 
106
 *   this because we have to write this text with the special
 
107
 *   statusRight() intrinsic in text mode.
 
108
 *   
 
109
 *   We only use this stream when we use the old-style text-mode status
 
110
 *   line interface, which explicitly separates the status line into a
 
111
 *   left part and a right part.  When we have the banner API available in
 
112
 *   the interpreter, we'll use banners instead, since banners give us
 
113
 *   much more flexibility.  
 
114
 */
 
115
transient statusRightOutputStream: OutputStream
 
116
    /*
 
117
     *   Write from the stream.  We simply buffer up text until we're
 
118
     *   asked to display the final data. 
 
119
     */
 
120
    writeFromStream(txt)
 
121
    {
 
122
        /* buffer the text */
 
123
        buf_ += txt;
 
124
    }
 
125
 
 
126
    /*
 
127
     *   Flush the buffer.  This writes whatever we've buffered up to the
 
128
     *   right half of the text-mode status line. 
 
129
     */
 
130
    flushStream()
 
131
    {
 
132
        /* write the text to the system console */
 
133
        statusRight(buf_);
 
134
 
 
135
        /* we no longer have anything buffered */
 
136
        buf_ = '';
 
137
    }
 
138
 
 
139
    /* our buffered text */
 
140
    buf_ = ''
 
141
;
 
142
 
 
143
 
 
144
/* ------------------------------------------------------------------------ */
 
145
/*
 
146
 *   Statusline modes.  We have three different ways to display the
 
147
 *   statusline, depending on the level of support in the interpreter.
 
148
 *   
 
149
 *   StatusModeApi - use the banner API.  This is preferred method, because
 
150
 *   it gives us uniform capabilities on text and graphical interpreters,
 
151
 *   and provides an output stream for the statusline that is fully
 
152
 *   independent on the main game window's output stream.
 
153
 *   
 
154
 *   StatusModeTag - use the <BANNER> tag.  This is the method we must use
 
155
 *   for HTML-enabled interpreters that don't support the banner API.  This
 
156
 *   gives us the full formatting capabilities of HTML, but isn't as good
 
157
 *   as StatusModeApi because we have to share our output stream with the
 
158
 *   main game window.
 
159
 *   
 
160
 *   StatusModeText - use the old-style dedicated statusline in a text-only
 
161
 *   interpreter.  This is the least desirable method, because it gives us
 
162
 *   a rigid format for the statusline (exactly one line high, no control
 
163
 *   over colors, and with the strict left/right bifurcation).  We'll only
 
164
 *   use this method if we're on a text-only interpreter that doesn't
 
165
 *   support the banner API.  
 
166
 */
 
167
enum StatusModeApi, StatusModeTag, StatusModeText;
 
168
 
 
169
/* ------------------------------------------------------------------------ */
 
170
/*
 
171
 *   Status line - this is an abstract object that controls the status line
 
172
 *   display.  
 
173
 *   
 
174
 *   We provide two main methods: showStatusHtml, which shows the status
 
175
 *   line in HTML format, and showStatusText, which shows the status line
 
176
 *   in plain text mode.  To display the status line, we invoke one or the
 
177
 *   other of these methods, according to the current mode, to display the
 
178
 *   statusline.  The default implementations of these methods generate the
 
179
 *   appropriate formatting codes for a statusline with a left part and a
 
180
 *   right part, calling showStatusLeft and showStatusRight, respectively,
 
181
 *   to display the text for the parts.
 
182
 *   
 
183
 *   Games can customize the statusline at two levels.  At the simpler
 
184
 *   level, a game can modify showStatusLeft and/or showStatusRight to
 
185
 *   change the text displayed on the left and/or right of the statusline.
 
186
 *   Since these two methods are used regardless of the statusline style of
 
187
 *   the underlying interpreter, games don't have to worry about the
 
188
 *   different modes when overriding these.
 
189
 *   
 
190
 *   At the more complex level, a game can modify showStatusHtml and/or
 
191
 *   showStatusText.  Modifying these routines provides complete control
 
192
 *   over the formatting of the entir status line.  If a game wants to use
 
193
 *   something other than the traditional left/right display, it must
 
194
 *   modify these methods.
 
195
 *   
 
196
 *   This object is transient, because the statusline style is a function
 
197
 *   of the interpreter we're currently running on, and thus isn't suitable
 
198
 *   for saving persistently.
 
199
 */
 
200
transient statusLine: object
 
201
    /* 
 
202
     *   Show the status line, in HTML or text mode, as appropriate.  By
 
203
     *   default, the library sets this up as a "prompt daemon," which
 
204
     *   means that this will be called automatically just before each
 
205
     *   command line is read.  
 
206
     */
 
207
    showStatusLine()
 
208
    {
 
209
        local oldStr;
 
210
 
 
211
        /* if the status line isn't active, or there's no PC, skip this */
 
212
        if (statusDispMode == nil || gPlayerChar == nil)
 
213
            return;
 
214
 
 
215
        /* 
 
216
         *   showing the status line doesn't normally change any game
 
217
         *   state, so we can turn on the sense cache while generating the
 
218
         *   display 
 
219
         */
 
220
        libGlobal.enableSenseCache();
 
221
 
 
222
        /* 
 
223
         *   Enter status-line mode.  This will do whatever is required for
 
224
         *   our current status-line display style to prepare the output
 
225
         *   manager so that any text we display to the default output
 
226
         *   stream is displayed on the status line. 
 
227
         */
 
228
        oldStr = beginStatusLine();
 
229
 
 
230
        /* make sure we restore statusline mode before we're done */
 
231
        try
 
232
        {
 
233
            /*
 
234
             *   Generate a text or HTML status line, as appropriate.  If
 
235
             *   we're in <BANNER> tag mode or banner API mode, use HTML to
 
236
             *   format the contents of the status line; if we're using the
 
237
             *   old-style text mode, use plain text, since the formatting
 
238
             *   is rigidly defined in this mode.  
 
239
             */
 
240
            if (statusDispMode != StatusModeText)
 
241
            {
 
242
                /* show the HTML status line */
 
243
                showStatusHtml();
 
244
            }
 
245
            else
 
246
            {
 
247
                /* show the status line in plain text mode */
 
248
                showStatusText();
 
249
            }
 
250
        }
 
251
        finally
 
252
        {
 
253
            /* end status-line mode */
 
254
            endStatusLine(oldStr);
 
255
 
 
256
            /* turn off sense caching */
 
257
            libGlobal.disableSenseCache();
 
258
        }
 
259
    }
 
260
 
 
261
    /* prompt-daemon showing the status line */
 
262
    showStatusLineDaemon()
 
263
    {
 
264
        /* show the status line as normal */
 
265
        showStatusLine();
 
266
 
 
267
        /* 
 
268
         *   Explicitly flush the status line if it's in a banner window.
 
269
         *   This will ensure that we'll redraw the status line on each
 
270
         *   turn if we're reading an input script, which is nice because
 
271
         *   it provides a visual indication that something's happening. 
 
272
         */
 
273
        if (statusDispMode == StatusModeApi)
 
274
            statuslineBanner.flushBanner();
 
275
    }
 
276
 
 
277
    /*
 
278
     *   Show the status line in HTML format.  Our default implementation
 
279
     *   shows the traditional two-part (left/right) status line, using
 
280
     *   showStatusLeft() and showStatusRight() to display the parts.  
 
281
     */
 
282
    showStatusHtml()
 
283
    {
 
284
        /* hyperlink the location name to a "look around" command */
 
285
        "<a plain href='<<gLibMessages.commandLookAround>>'>";
 
286
            
 
287
        /* show the left part of the status line */
 
288
        showStatusLeft();
 
289
            
 
290
        /* set up for the score part on the right half */
 
291
        "</a><tab align=right><a plain
 
292
            href='<<gLibMessages.commandFullScore>>'>";
 
293
        
 
294
        /* show the right part of the status line */
 
295
        showStatusRight();
 
296
        
 
297
        /* end the score link */
 
298
        "</a>";
 
299
        
 
300
        /* add the status-line exit list, if desired */
 
301
        if (gPlayerChar.location != nil)
 
302
            gPlayerChar.location.showStatuslineExits();
 
303
    }
 
304
 
 
305
    /*
 
306
     *   Get the estimated HTML-style banner height, in lines of text.
 
307
     *   This is used to set the status line banner size for platforms
 
308
     *   where sizing to the exact height of the rendered contents isn't
 
309
     *   supported.
 
310
     *   
 
311
     *   If showStatusHtml() is overridden to display more or fewer lines
 
312
     *   of text than the basic implementation here, then this routine must
 
313
     *   be overridden as well to reflect the new height.  
 
314
     */
 
315
    getEstimatedHeightHtml()
 
316
    {
 
317
        local ht;
 
318
        
 
319
        /* 
 
320
         *   we need one line for the basic display (the location name and
 
321
         *   score/turn count) 
 
322
         */
 
323
        ht = 1;
 
324
 
 
325
        /* add in the estimated height of the exits display, if appropriate */
 
326
        if (gPlayerChar.location != nil)
 
327
            ht += gPlayerChar.location.getStatuslineExitsHeight();
 
328
 
 
329
        /* return the result */
 
330
        return ht;
 
331
    }
 
332
 
 
333
    /*
 
334
     *   Show the statusline in text mode.  Our default implementation
 
335
     *   shows the traditional two-part (left/right) status line, using
 
336
     *   showStatusLeft() and showStatusRight() to display the parts.  
 
337
     */
 
338
    showStatusText()
 
339
    {
 
340
        /* show the left part of the display */
 
341
        showStatusLeft();
 
342
 
 
343
        /* switch to the right-side status stream */
 
344
        outputManager.setOutputStream(statusRightOutputStream);
 
345
 
 
346
        /* show the right-half text */
 
347
        showStatusRight();
 
348
        
 
349
        /* flush the right-side stream */
 
350
        statusRightOutputStream.flushStream();
 
351
    }
 
352
 
 
353
    /*
 
354
     *   Show the left part of a standard left/right statusline.  By
 
355
     *   default, we'll show the player character's location, by calling
 
356
     *   statusName() on the PC's immediate container.  
 
357
     */
 
358
    showStatusLeft()
 
359
    {
 
360
        local actor;
 
361
 
 
362
        /* get the player character actor */
 
363
        actor = gPlayerChar;
 
364
 
 
365
        "<.statusroom>";
 
366
 
 
367
        /* show the actor's location's status name */
 
368
        if (actor != nil && actor.location != nil)
 
369
            actor.location.statusName(actor);
 
370
 
 
371
        "<./statusroom>";
 
372
    }
 
373
 
 
374
    /*
 
375
     *   Show the right part of a standard left/right statusline.  By
 
376
     *   default, we'll show the current score, a slash, and the number of
 
377
     *   turns. 
 
378
     */
 
379
    showStatusRight()
 
380
    {
 
381
        local s;
 
382
 
 
383
        /* if there's a score object, show the score */
 
384
        if ((s = libGlobal.scoreObj) != nil)
 
385
        {
 
386
            /* show the score and the number of turns so far */
 
387
            "<.statusscore><<s.totalScore>>/<<
 
388
            libGlobal.totalTurns>><./statusscore>";
 
389
        }
 
390
    }
 
391
 
 
392
    /*
 
393
     *   Set up the status line's color scheme.  This is called each time
 
394
     *   we redraw the status line to set the background and text colors.
 
395
     *   We simply show a <BODY> tag that selects the parameterized colors
 
396
     *   STATUSBG and STATUSTEXT.  (These are called "parameterized" colors
 
397
     *   because they don't select specific colors, but rather select
 
398
     *   whatever colors the interpreter wishes to use for the status line.
 
399
     *   In many cases, the interpreter lets the user select these colors
 
400
     *   via a Preferences dialog.)  
 
401
     */
 
402
    setColorScheme()
 
403
    {
 
404
        /* set up the interpreter's standard status line colors */
 
405
        "<body bgcolor=statusbg text=statustext>";
 
406
    }
 
407
 
 
408
    /* 
 
409
     *   Begin status-line mode.  This sets up the output manager so that
 
410
     *   text written to the default output stream is displayed on the
 
411
     *   status line.  Returns the original output stream.
 
412
     */
 
413
    beginStatusLine()
 
414
    {
 
415
        local oldStr;
 
416
 
 
417
        /* check what kind of statusline display we're using */
 
418
        switch(statusDispMode)
 
419
        {
 
420
        case StatusModeApi:
 
421
            /* 
 
422
             *   We have a banner API window.  Start by clearing the
 
423
             *   window, so we can completely replace everything in it.  
 
424
             */
 
425
            statuslineBanner.clearWindow();
 
426
 
 
427
            /*
 
428
             *   If the platform doesn't support size-to-contents, then set
 
429
             *   the height to our best estimate for the size.
 
430
             *   
 
431
             *   If we do support size-to-contents, we'll set the height to
 
432
             *   the exact rendered size when we're done, so we don't need
 
433
             *   to worry about setting an estimate; indicate this to the
 
434
             *   interpreter by setting the is-advisory flag to true.  
 
435
             */
 
436
            statuslineBanner.setSize(getEstimatedHeightHtml(),
 
437
                                     BannerSizeAbsolute, true);
 
438
 
 
439
            /* switch to the banner's output stream */
 
440
            oldStr = statuslineBanner.setOutputStream();
 
441
 
 
442
            /* set up the statusline color in the window */
 
443
            setColorScheme();
 
444
 
 
445
            /* done */
 
446
            break;
 
447
 
 
448
        case StatusModeTag:
 
449
            /* 
 
450
             *   We're using <BANNER> tags.  Switch to our statusline
 
451
             *   output stream.  
 
452
             */
 
453
            oldStr = outputManager.setOutputStream(statusTagOutputStream);
 
454
 
 
455
            /* set up the <BANNER> tag */
 
456
            "<banner id=StatusLine height=previous border>";
 
457
 
 
458
            /* set up the color scheme */
 
459
            setColorScheme();
 
460
 
 
461
            /* done */
 
462
            break;
 
463
 
 
464
        case StatusModeText:
 
465
            /* flush the main window */
 
466
            flushOutput();
 
467
 
 
468
            /* plain text mode - enter text status mode */
 
469
            statusMode(StatModeStatus);
 
470
 
 
471
            /* switch to the status-left output stream */
 
472
            oldStr = outputManager.setOutputStream(statusLeftOutputStream);
 
473
 
 
474
            /* done */
 
475
            break;
 
476
        }
 
477
 
 
478
        /* return the original output stream */
 
479
        return oldStr;
 
480
    }
 
481
 
 
482
    /* end statusline display */
 
483
    endStatusLine(oldStr)
 
484
    {
 
485
        /* restore the old default output stream */
 
486
        outputManager.setOutputStream(oldStr);
 
487
 
 
488
        /* check the type of statusline we're generating */
 
489
        switch (statusDispMode)
 
490
        {
 
491
        case StatusModeApi:
 
492
            /* banner API mode - end the last line */
 
493
            statuslineBanner.writeToBanner('\n');
 
494
 
 
495
            /* 
 
496
             *   Size the window to its current contents.  This doesn't
 
497
             *   work everywhere - on a few platforms, it does nothing -
 
498
             *   but this will give us the optimal size where it's
 
499
             *   supported.  On platforms that don't support this, it'll do
 
500
             *   nothing, which means we'll simply be left with the
 
501
             *   "advisory" size we established earlier.  
 
502
             */
 
503
            statuslineBanner.sizeToContents();
 
504
            break;
 
505
 
 
506
        case StatusModeTag:
 
507
            /* HTML <BANNER> mode - end the <BANNER> tag */
 
508
            statusTagOutputStream.writeToStream('</banner>');
 
509
            break;
 
510
 
 
511
        case StatusModeText:
 
512
            /* plain text statusline - end status mode */
 
513
            statusMode(StatModeNormal);
 
514
        }
 
515
    }
 
516
 
 
517
    /*
 
518
     *   Initialize the banner window, given the BannerWindow object
 
519
     *   representing the status line banner API window.  
 
520
     */
 
521
    initBannerWindow(win)
 
522
    {
 
523
        /* 
 
524
         *   Try showing the banner API window.  If that succeeds, use the
 
525
         *   banner API window, since it's the most portable and flexible
 
526
         *   way to show the status line.  If we can't create the banner
 
527
         *   API window, it means we're on an interpreter that doesn't
 
528
         *   support the banner API, so fall back on one of the older, less
 
529
         *   flexible mechanisms; which older mechanism we choose depends
 
530
         *   on what kind of interpreter we're on.
 
531
         *   
 
532
         *   Since we create the status line banner during initialization
 
533
         *   and normally leave it as the first item in the display list at
 
534
         *   all times, we can attach to an existing status line banner
 
535
         *   window if there is one.  This will avoid unnecessary redrawing
 
536
         *   on RESTART.  
 
537
         */
 
538
        if (win.showBanner(nil, BannerFirst, nil, BannerTypeText,
 
539
                           BannerAlignTop, nil, nil,
 
540
                           BannerStyleBorder | BannerStyleTabAlign))
 
541
        {
 
542
            /* 
 
543
             *   we successfully created the banner window - use the banner
 
544
             *   API to show the status line 
 
545
             */
 
546
            statusDispMode = StatusModeApi;
 
547
        }
 
548
        else if (systemInfo(SysInfoInterpClass) == SysInfoIClassHTML)
 
549
        {
 
550
            /*
 
551
             *   We failed to create a banner API window, and we're running
 
552
             *   on a full HTML interpreter, so use <BANNER> tags to
 
553
             *   produce the status line.  
 
554
             */
 
555
            statusDispMode = StatusModeTag;
 
556
        }
 
557
        else
 
558
        {
 
559
            /*
 
560
             *   We failed to create a banner API window, and we're running
 
561
             *   on a text-only interpreter - use the old-style
 
562
             *   fixed-format status line mechanism.  
 
563
             */
 
564
            statusDispMode = StatusModeText;
 
565
        }
 
566
    }
 
567
 
 
568
    /* 
 
569
     *   The status mode we're using.  If this is nil, it means we haven't
 
570
     *   chosen a mode yet. 
 
571
     */
 
572
    statusDispMode = nil
 
573
;
 
574