!
!   bquote.h
!
!   Synopsis
!
!       Extension for writing block quotes, i.e. indented
!       paragraphs, in Version 5 games. The effect is similar to
!       the <blockquote> tag in HTML, but it always uses a fixed
!       width font.
!
!   Inclusion
!
!       Include the file "bquote.h" at the beginning of your
!       game file. For z-code, this extension is stand-alone.
!
!       For Glulx, it relies on using the standard Inform
!       library. Glulx compability is provided for the case that
!       you decide to flip the -G switch during development. If
!       you're going for Glulx from the start, you might be
!       better off using the block quote style as produced by
!       glk($0086, 7);
!
!   How to print block quotes
!
!       First, the block quote must be opened with
!
!           bq_open(margin, padding, maxwidth);
!
!       This will start a block quote with the specified margin
!       left and right. If you specify a padding, the block
!       quote is printed in a special style, usually reverse,
!       with the padding from the left and right borders.
!
!       Usually, the width of the quote is determined by the
!       width of the screen and the margins and paddings. If you
!       specify maxwidth, the width will still be adjusted to
!       the screen width, but if the inner width of the box is
!       greater than maxwidth, maxwidth is used.
!
!       If you specify a negative maxwidth, the margin will be
!       calculated so that the quote will be centered and
!       -maxwidth characters wide. If the calculated margin is
!       less than the specified margin, the width will be
!       adjusted.
!
!       For printing the text, use the routines
!
!           bq_text(str, a1, a2, a3);
!           bq_newline();
!
!       where str may be the reference to a static string or a
!       routine which can take up to three arguments a#.
!
!       The output text must not contain the newline character ^
!       - that's what the bq_newline() function is for - and it
!       must not be longer than BQ_MAX_LENGTH characters. The
!       complete text may be of any length, but it must be
!       defined in chunks of less than BQ_MAX_LENGTH characters.
!
!       If the text is longer, a warning is issued, which
!       should tell you to either break down your text into
!       smaller chunks or to define a larger value for the
!       constant BQ_MAX_LENGTH (default is 512) before including
!       "bqoute.h".
!
!       Between two chunks of text, a space is inserted
!       automatically.
!
!       Finally, close your quote to return to normal printing
!       by calling
!
!           bq_close();
!
!       Before opening and after closing a quote, a newline is
!       printed. You might want to end the text before the quote
!       and begin the text after it with an extra newline:
!
!           description [;
!               text "One section in particular catches
!                   your attention:^";
!               bq_open(4);
!               bq_text("The security system ...");
!               bq_newline();
!               bq_newline();
!               bq_text("The password is ~Halifax~.");
!               bq_close();
!               "^Now that's interesting.";
!           ];
!
!   Examples of quote styles:
!
!       Regular blockquote, four spaces left and right:
!
!           bq_open(4);
!
!       Reverse box across the whole screen with a padding of
!       four:
!
!           bq_open(0, 4);
!
!       Zero margin boxes work on many interpreters, but on
!       some terps they might cause extra blank lines, so better
!       use a safty margin:
!
!           bq_open(1, 3);
!
!       Narrow box 30 spaces wide, aligned to left margin (4):
!
!           bq_open(4, 1, 30);
!
!       Same, but centered. The text is always left flushed:
!
!           bq_open(4, 1, -30);
!
!   Customization
!
!       The style for quotes with a positive margin is reverse
!       per default, making such block quotes inline box quotes.
!
!       You can change the style of these quotes by specifying
!       your own routines bq_style_on and bq_style_off, e.g. to
!       use coloured blocks and a "mail quote" effect:
!
!           Replace bq_style_on;
!           Replace bq_style_off;
!
!           [bq_style_on;
!             if ((0->1) & 1 ~= 0) @set_colour 3 9;
!             else style reverse;
!             print">";
!           ];
!
!           [bq_style_off;
!             if ((0->1) & 1 ~= 0) @set_colour 1 1;
!             else style roman;
!           ];
!
!           Include "bquote";
!
!       Words that run longer than a line are printed over the
!       right margin. Buffering is off in bq mode, so that the
!       layout is usually not very much affected. You can change
!       this behaviour by defining the constant BQ_TRIM_LINES
!       which causes long words to be pruned. A tilde is then
!       printed and the word continues on the next line. This
!       might be tidier for reverse boxes.
!
!   Versions and History
!
!       18-May-2005     Version 1.0
!
!   Author
!
!       Martin Oehm     (martin.oehm at gmx.de)
!
!---------------------------------------------------------------

System_file;

Default BQ_MAX_LENGTH 512;

Global bq_width = -1;
Global bq_margin;
Global bq_padding;
Global bq_pos;

Array bq_buffer -> BQ_MAX_LENGTH + 2;

[bq_getwidth;
   #ifdef TARGET_ZCODE;
   ! read the z-code header byte
   return 0->$21;
   #ifnot;
   ! use the width of the status window if possible
   if (gg_statuswin)
     glk($0025, gg_statuswin, gg_arguments, gg_arguments + WORDSIZE);
   else
     glk($0025, gg_mainwin, gg_arguments, gg_arguments + WORDSIZE);
   return gg_arguments-->0;
   #endif;
];

[bq_open marg padd max;
   if (bq_width > 0) print "[*** Waring: bq already open. ***]";
   bq_width = bq_getwidth();
   bq_padding = padd;
   if (max < 0) {
       ! margins are determined by width
       bq_margin = (bq_width + max)/2 - bq_padding;
       if (bq_margin < marg) bq_margin = marg;
       max = 0;
   } else {
       ! width is determined by margins
       bq_margin = marg;
   }
   bq_width = bq_width - 2*bq_margin - 2*bq_padding;
   if (max && bq_width > max) bq_width = max;
   font off;
   bq_newline(1);

   #ifdef TARGET_ZCODE;
   #ifndef BQ_TRIM_LINES;
   ! no buffering for words that run over the right margin
   @buffer_mode 0;
   #endif;
   #endif;
];

[bq_style_on; style reverse; ];

[bq_style_off; style roman; ];

[bq_newline suppress;
   if (suppress <= 0 && bq_padding) {
       spaces bq_width - bq_pos + bq_padding;
       bq_style_off();
   }
   new_line;
   bq_pos = 0;
   spaces bq_margin;
   if (suppress < 0) return;
   if (bq_padding) {
       bq_style_on();
       spaces bq_padding;
   }
];

[bq_print a l;
   if (l < 0 || l > BQ_MAX_LENGTH) "***",  l, " ***";
   while (l--) {
       if (a->0 == 13 or 10) print "@@94";
       else print (char) a->0;
       a++; bq_pos++;
   }
];

[bq_text s a1 a2 a3   a l ll sep;
   if (bq_width < 0) "[*** Error: bq not opened! ***]";
   #ifdef TARGET_ZCODE;
   @output_stream 3 bq_buffer;
   if (s ofclass String) s.print();
   if (s ofclass Routine) s.call(a1, a2, a3);
   @output_stream -3;
   l = bq_buffer-->0;
   a = bq_buffer + 2;
   #ifnot;
   l = PrintAnyToArray(bq_buffer, BQ_MAX_LENGTH, s, a1, a2, a3);
   a = bq_buffer;
   #endif;

   if (l > BQ_MAX_LENGTH) "[*** Error: String chunk too long! ***]";

   while (bq_pos + l > bq_width) {
       ll = bq_width - bq_pos - 1;
       while (ll > 0 && a->ll ~= ' ') ll--;
       #ifdef BQ_TRIM_LINES;
       if (ll <= 0 && bq_pos == 0) {
           ! trim overlong words
           ll = bq_width - bq_pos -1;
           sep = true;
       }
       #ifnot;
       if (ll <= 0 && bq_pos == 0) {
           ! find next space
           ll = bq_width - bq_pos;
           while (ll < l && a->ll ~= ' ') ll++;
       }
       #endif;
       if (ll > 0) {
           bq_print(a, ll);
           a = a + ll + 1;
           l = l -ll - 1;
           if (sep) {
               print "@@126"; bq_pos++;
               a--; l++; sep = false;
           }
           if (l < 0) { a = a + l; l = 0; }
       }
       bq_newline();
   }
   bq_print(a, l);
   if (bq_pos < bq_width) {
       print " "; bq_pos++;
   }
];

[bq_close;
   #ifdef TARGET_ZCODE;
   #ifndef BQ_TRIM_LINES;
   @buffer_mode 1;
   #endif;
   #endif;
   bq_newline(-1);
   font on;
   bq_width = -1;
];