status.t

documentation
 #charset "us-ascii"
 
 /* 
  *   Copyright (c) 2000, 2006 Michael J. Roberts.  All Rights Reserved. 
  *   
  *   TADS 3 Library - Status Line
  *   
  *   This module defines the framework for displaying the status line,
  *   which is the area conventionally displayed at the top of the screen
  *   showing information such as the current location, score (if scoring is
  *   used at all), and number of turns.  
  */
 
 /* include the library header */
 #include "adv3.h"
 
 
 /* ------------------------------------------------------------------------ */
 /* 
  *   in case the 'score' module isn't included, make sure we can refer to
  *   totalScore as a property 
  */
 property totalScore;
 
 
 /* ------------------------------------------------------------------------ */
 /*
  *   The banner window for the status line.  
  */
 statuslineBanner: BannerWindow
     removeBanner()
     {
         /* inherit the base handling */
         inherited();
 
         /* 
          *   notify the statusLine object that it needs to refigure the
          *   display mode 
          */
         statusLine.statusDispMode = nil;
     }
 
     /* initialize */
     initBannerWindow()
     {
         /* if we're already initialized, do nothing */
         if (inited_)
             return;
 
         /* inherit the default handling (to set our 'inited_' flag) */
         inherited();
         
         /* tell the status line to initialize its banner window */
         statusLine.initBannerWindow(self);
     }
 ;
 
 
 /* ------------------------------------------------------------------------ */
 /*
  *   A special OutputStream for the <BANNER> tag contents.  This is really
  *   just part of the main output stream, but we use a separate output
  *   stream object so that we have our own separate stream state variables
  *   (for paragraph breaking and so forth).  
  */
 transient statusTagOutputStream: OutputStream
     /*
      *   We're really part of the main window's output stream as far as the
      *   underlying interpreter I/O system is concerned, so we have to
      *   coordinate with the main game window's input manager. 
      */
     myInputManager = inputManager
 
     /* we sit atop the system-level main console output stream */
     writeFromStream(txt)
     {
         /* write the text directly to the main output stream */
         tadsSay(txt);
     }
 ;
 
 /*
  *   A special OutputStream for the left half of the status line (the
  *   short description area) in text mode.  We use a separate stream for
  *   this because we must write the text using the output mode switching
  *   for the status line.
  *   
  *   We only use this stream when we use the old-style text-mode status
  *   line interface, which explicitly separates the status line into a
  *   left part and a right part.  When we have the banner API available in
  *   the interpreter, we'll use banners instead, since banners give us
  *   much more flexibility.  
  */
 transient statusLeftOutputStream: OutputStream
     /* we sit atop the system-level main console output stream */
     writeFromStream(txt)
     {
         /* write the text directly to the main output stream */
         tadsSay(txt);
     }
 ;
 
 /*
  *   A special OutputStream for the right half of the status line (the
  *   score/turn count area) in text mode.  We use a separate stream for
  *   this because we have to write this text with the special
  *   statusRight() intrinsic in text mode.
  *   
  *   We only use this stream when we use the old-style text-mode status
  *   line interface, which explicitly separates the status line into a
  *   left part and a right part.  When we have the banner API available in
  *   the interpreter, we'll use banners instead, since banners give us
  *   much more flexibility.  
  */
 transient statusRightOutputStream: OutputStream
     /*
      *   Write from the stream.  We simply buffer up text until we're
      *   asked to display the final data. 
      */
     writeFromStream(txt)
     {
         /* buffer the text */
         buf_ += txt;
     }
 
     /*
      *   Flush the buffer.  This writes whatever we've buffered up to the
      *   right half of the text-mode status line. 
      */
     flushStream()
     {
         /* write the text to the system console */
         statusRight(buf_);
 
         /* we no longer have anything buffered */
         buf_ = '';
     }
 
     /* our buffered text */
     buf_ = ''
 ;
 
 
 /* ------------------------------------------------------------------------ */
 /*
  *   Statusline modes.  We have three different ways to display the
  *   statusline, depending on the level of support in the interpreter.
  *   
  *   StatusModeApi - use the banner API.  This is preferred method, because
  *   it gives us uniform capabilities on text and graphical interpreters,
  *   and provides an output stream for the statusline that is fully
  *   independent on the main game window's output stream.
  *   
  *   StatusModeTag - use the <BANNER> tag.  This is the method we must use
  *   for HTML-enabled interpreters that don't support the banner API.  This
  *   gives us the full formatting capabilities of HTML, but isn't as good
  *   as StatusModeApi because we have to share our output stream with the
  *   main game window.
  *   
  *   StatusModeText - use the old-style dedicated statusline in a text-only
  *   interpreter.  This is the least desirable method, because it gives us
  *   a rigid format for the statusline (exactly one line high, no control
  *   over colors, and with the strict left/right bifurcation).  We'll only
  *   use this method if we're on a text-only interpreter that doesn't
  *   support the banner API.  
  */
 enum StatusModeApi, StatusModeTag, StatusModeText;
 
 /* ------------------------------------------------------------------------ */
 /*
  *   Status line - this is an abstract object that controls the status line
  *   display.  
  *   
  *   We provide two main methods: showStatusHtml, which shows the status
  *   line in HTML format, and showStatusText, which shows the status line
  *   in plain text mode.  To display the status line, we invoke one or the
  *   other of these methods, according to the current mode, to display the
  *   statusline.  The default implementations of these methods generate the
  *   appropriate formatting codes for a statusline with a left part and a
  *   right part, calling showStatusLeft and showStatusRight, respectively,
  *   to display the text for the parts.
  *   
  *   Games can customize the statusline at two levels.  At the simpler
  *   level, a game can modify showStatusLeft and/or showStatusRight to
  *   change the text displayed on the left and/or right of the statusline.
  *   Since these two methods are used regardless of the statusline style of
  *   the underlying interpreter, games don't have to worry about the
  *   different modes when overriding these.
  *   
  *   At the more complex level, a game can modify showStatusHtml and/or
  *   showStatusText.  Modifying these routines provides complete control
  *   over the formatting of the entir status line.  If a game wants to use
  *   something other than the traditional left/right display, it must
  *   modify these methods.
  *   
  *   This object is transient, because the statusline style is a function
  *   of the interpreter we're currently running on, and thus isn't suitable
  *   for saving persistently.
  */
 transient statusLine: object
     /* 
      *   Show the status line, in HTML or text mode, as appropriate.  By
      *   default, the library sets this up as a "prompt daemon," which
      *   means that this will be called automatically just before each
      *   command line is read.  
      */
     showStatusLine()
     {
         local oldStr;
 
         /* if the status line isn't active, or there's no PC, skip this */
         if (statusDispMode == nil || gPlayerChar == nil)
             return;
 
         /* 
          *   showing the status line doesn't normally change any game
          *   state, so we can turn on the sense cache while generating the
          *   display 
          */
         libGlobal.enableSenseCache();
 
         /* 
          *   Enter status-line mode.  This will do whatever is required for
          *   our current status-line display style to prepare the output
          *   manager so that any text we display to the default output
          *   stream is displayed on the status line. 
          */
         oldStr = beginStatusLine();
 
         /* make sure we restore statusline mode before we're done */
         try
         {
             /*
              *   Generate a text or HTML status line, as appropriate.  If
              *   we're in <BANNER> tag mode or banner API mode, use HTML to
              *   format the contents of the status line; if we're using the
              *   old-style text mode, use plain text, since the formatting
              *   is rigidly defined in this mode.  
              */
             if (statusDispMode != StatusModeText)
             {
                 /* show the HTML status line */
                 showStatusHtml();
             }
             else
             {
                 /* show the status line in plain text mode */
                 showStatusText();
             }
         }
         finally
         {
             /* end status-line mode */
             endStatusLine(oldStr);
 
             /* turn off sense caching */
             libGlobal.disableSenseCache();
         }
     }
 
     /* prompt-daemon showing the status line */
     showStatusLineDaemon()
     {
         /* show the status line as normal */
         showStatusLine();
 
         /* 
          *   Explicitly flush the status line if it's in a banner window.
          *   This will ensure that we'll redraw the status line on each
          *   turn if we're reading an input script, which is nice because
          *   it provides a visual indication that something's happening. 
          */
         if (statusDispMode == StatusModeApi)
             statuslineBanner.flushBanner();
     }
 
     /*
      *   Show the status line in HTML format.  Our default implementation
      *   shows the traditional two-part (left/right) status line, using
      *   showStatusLeft() and showStatusRight() to display the parts.  
      */
     showStatusHtml()
     {
         /* hyperlink the location name to a "look around" command */
         "<a plain href='<<gLibMessages.commandLookAround>>'>";
             
         /* show the left part of the status line */
         showStatusLeft();
             
         /* set up for the score part on the right half */
         "</a><tab align=right><a plain
             href='<<gLibMessages.commandFullScore>>'>";
         
         /* show the right part of the status line */
         showStatusRight();
         
         /* end the score link */
         "</a>";
         
         /* add the status-line exit list, if desired */
         if (gPlayerChar.location != nil)
             gPlayerChar.location.showStatuslineExits();
     }
 
     /*
      *   Get the estimated HTML-style banner height, in lines of text.
      *   This is used to set the status line banner size for platforms
      *   where sizing to the exact height of the rendered contents isn't
      *   supported.
      *   
      *   If showStatusHtml() is overridden to display more or fewer lines
      *   of text than the basic implementation here, then this routine must
      *   be overridden as well to reflect the new height.  
      */
     getEstimatedHeightHtml()
     {
         local ht;
         
         /* 
          *   we need one line for the basic display (the location name and
          *   score/turn count) 
          */
         ht = 1;
 
         /* add in the estimated height of the exits display, if appropriate */
         if (gPlayerChar.location != nil)
             ht += gPlayerChar.location.getStatuslineExitsHeight();
 
         /* return the result */
         return ht;
     }
 
     /*
      *   Show the statusline in text mode.  Our default implementation
      *   shows the traditional two-part (left/right) status line, using
      *   showStatusLeft() and showStatusRight() to display the parts.  
      */
     showStatusText()
     {
         /* show the left part of the display */
         showStatusLeft();
 
         /* switch to the right-side status stream */
         outputManager.setOutputStream(statusRightOutputStream);
 
         /* show the right-half text */
         showStatusRight();
         
         /* flush the right-side stream */
         statusRightOutputStream.flushStream();
     }
 
     /*
      *   Show the left part of a standard left/right statusline.  By
      *   default, we'll show the player character's 
      */
     showStatusLeft()
     {
         local actor;
 
         /* get the player character actor */
         actor = gPlayerChar;
 
         "<.statusroom>";
 
         /* show the actor's location's status name */
         if (actor != nil && actor.location != nil)
             actor.location.statusName(actor);
 
         "<./statusroom>";
     }
 
     /*
      *   Show the right part of a standard left/right statusline.  By
      *   default, we'll show the current score, a slash, and the number of
      *   turns. 
      */
     showStatusRight()
     {
         local s;
 
         /* if there's a score object, show the score */
         if ((s = libGlobal.scoreObj) != nil)
         {
             /* show the score and the number of turns so far */
             "<.statusscore><<s.totalScore>>/<<
             libGlobal.totalTurns>><./statusscore>";
         }
     }
 
     /*
      *   Set up the status line's color scheme.  This is called each time
      *   we redraw the status line to set the background and text colors.
      *   We simply show a <BODY> tag that selects the parameterized colors
      *   STATUSBG and STATUSTEXT.  (These are called "parameterized" colors
      *   because they don't select specific colors, but rather select
      *   whatever colors the interpreter wishes to use for the status line.
      *   In many cases, the interpreter lets the user select these colors
      *   via a Preferences dialog.)  
      */
     setColorScheme()
     {
         /* set up the interpreter's standard status line colors */
         "<body bgcolor=statusbg text=statustext>";
     }
 
     /* 
      *   Begin status-line mode.  This sets up the output manager so that
      *   text written to the default output stream is displayed on the
      *   status line.  Returns the original output stream.
      */
     beginStatusLine()
     {
         local oldStr;
 
         /* check what kind of statusline display we're using */
         switch(statusDispMode)
         {
         case StatusModeApi:
             /* 
              *   We have a banner API window.  Start by clearing the
              *   window, so we can completely replace everything in it.  
              */
             statuslineBanner.clearWindow();
 
             /*
              *   If the platform doesn't support size-to-contents, then set
              *   the height to our best estimate for the size.
              *   
              *   If we do support size-to-contents, we'll set the height to
              *   the exact rendered size when we're done, so we don't need
              *   to worry about setting an estimate; indicate this to the
              *   interpreter by setting the is-advisory flag to true.  
              */
             statuslineBanner.setSize(getEstimatedHeightHtml(),
                                      BannerSizeAbsolute, true);
 
             /* switch to the banner's output stream */
             oldStr = statuslineBanner.setOutputStream();
 
             /* set up the statusline color in the window */
             setColorScheme();
 
             /* done */
             break;
 
         case StatusModeTag:
             /* 
              *   We're using <BANNER> tags.  Switch to our statusline
              *   output stream.  
              */
             oldStr = outputManager.setOutputStream(statusTagOutputStream);
 
             /* set up the <BANNER> tag */
             "<banner id=StatusLine height=previous border>";
 
             /* set up the color scheme */
             setColorScheme();
 
             /* done */
             break;
 
         case StatusModeText:
             /* flush the main window */
             flushOutput();
 
             /* plain text mode - enter text status mode */
             statusMode(StatModeStatus);
 
             /* switch to the status-left output stream */
             oldStr = outputManager.setOutputStream(statusLeftOutputStream);
 
             /* done */
             break;
         }
 
         /* return the original output stream */
         return oldStr;
     }
 
     /* end statusline display */
     endStatusLine(oldStr)
     {
         /* restore the old default output stream */
         outputManager.setOutputStream(oldStr);
 
         /* check the type of statusline we're generating */
         switch (statusDispMode)
         {
         case StatusModeApi:
             /* banner API mode - end the last line */
             statuslineBanner.writeToBanner('\n');
 
             /* 
              *   Size the window to its current contents.  This doesn't
              *   work everywhere - on a few platforms, it does nothing -
              *   but this will give us the optimal size where it's
              *   supported.  On platforms that don't support this, it'll do
              *   nothing, which means we'll simply be left with the
              *   "advisory" size we established earlier.  
              */
             statuslineBanner.sizeToContents();
             break;
 
         case StatusModeTag:
             /* HTML <BANNER> mode - end the <BANNER> tag */
             statusTagOutputStream.writeToStream('</banner>');
             break;
 
         case StatusModeText:
             /* plain text statusline - end status mode */
             statusMode(StatModeNormal);
         }
     }
 
     /*
      *   Initialize the banner window, given the BannerWindow object
      *   representing the status line banner API window.  
      */
     initBannerWindow(win)
     {
         /* 
          *   Try showing the banner API window.  If that succeeds, use the
          *   banner API window, since it's the most portable and flexible
          *   way to show the status line.  If we can't create the banner
          *   API window, it means we're on an interpreter that doesn't
          *   support the banner API, so fall back on one of the older, less
          *   flexible mechanisms; which older mechanism we choose depends
          *   on what kind of interpreter we're on.
          *   
          *   Since we create the status line banner during initialization
          *   and normally leave it as the first item in the display list at
          *   all times, we can attach to an existing status line banner
          *   window if there is one.  This will avoid unnecessary redrawing
          *   on RESTART.  
          */
         if (win.showBanner(nil, BannerFirst, nil, BannerTypeText,
                            BannerAlignTop, nil, nil,
                            BannerStyleBorder | BannerStyleTabAlign))
         {
             /* 
              *   we successfully created the banner window - use the banner
              *   API to show the status line 
              */
             statusDispMode = StatusModeApi;
         }
         else if (systemInfo(SysInfoInterpClass) == SysInfoIClassHTML)
         {
             /*
              *   We failed to create a banner API window, and we're running
              *   on a full HTML interpreter, so use <BANNER> tags to
              *   produce the status line.  
              */
             statusDispMode = StatusModeTag;
         }
         else
         {
             /*
              *   We failed to create a banner API window, and we're running
              *   on a text-only interpreter - use the old-style
              *   fixed-format status line mechanism.  
              */
             statusDispMode = StatusModeText;
         }
     }
 
     /* 
      *   The status mode we're using.  If this is nil, it means we haven't
      *   chosen a mode yet. 
      */
     statusDispMode = nil
 ;
 
TADS 3 Library Manual
Generated on 9/8/2006 from TADS version 3.0.11