home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / util / gnu / groff_src.lha / groff-1.10src / troff / column.cc < prev    next >
C/C++ Source or Header  |  1995-06-22  |  14KB  |  733 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 2, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  20.  
  21. #ifdef COLUMN
  22.  
  23. #include "troff.h"
  24. #include "symbol.h"
  25. #include "dictionary.h"
  26. #include "hvunits.h"
  27. #include "env.h"
  28. #include "request.h"
  29. #include "node.h"
  30. #include "token.h"
  31. #include "div.h"
  32. #include "reg.h"
  33. #include "stringclass.h"
  34.  
  35. void output_file::vjustify(vunits, symbol)
  36. {
  37.   // do nothing
  38. }
  39.  
  40. struct justification_spec;
  41. struct output_line;
  42.  
  43. class column : public output_file {
  44. private:
  45.   output_file *out;
  46.   vunits bottom;
  47.   output_line *col;
  48.   output_line **tail;
  49.   void add_output_line(output_line *);
  50.   void begin_page(int pageno, vunits page_length);
  51.   void flush();
  52.   void print_line(hunits, vunits, node *, vunits, vunits);
  53.   void vjustify(vunits, symbol);
  54.   void transparent_char(unsigned char c);
  55.   void copy_file(hunits, vunits, const char *);
  56.   int is_printing();
  57.   void check_bottom();
  58. public:
  59.   column();
  60.   ~column();
  61.   void start();
  62.   void output();
  63.   void justify(const justification_spec &);
  64.   void trim();
  65.   void reset();
  66.   vunits get_bottom();
  67.   vunits get_last_extra_space();
  68.   int is_active() { return out != 0; }
  69. };
  70.  
  71. column *the_column = 0;
  72.  
  73. struct transparent_output_line;
  74. struct vjustify_output_line;
  75.  
  76. class output_line {
  77.   output_line *next;
  78. public:
  79.   output_line();
  80.   virtual ~output_line();
  81.   virtual void output(output_file *, vunits);
  82.   virtual transparent_output_line *as_transparent_output_line();
  83.   virtual vjustify_output_line *as_vjustify_output_line();
  84.   virtual vunits distance();
  85.   virtual vunits height();
  86.   virtual void reset();
  87.   virtual vunits extra_space();    // post line
  88.   friend class column;
  89.   friend class justification_spec;
  90. };
  91.  
  92. class position_output_line : public output_line {
  93.   vunits dist;
  94. public:
  95.   position_output_line(vunits);
  96.   vunits distance();
  97. };
  98.   
  99. class node_output_line : public position_output_line {
  100.   node *nd;
  101.   hunits page_offset;
  102.   vunits before;
  103.   vunits after;
  104. public:
  105.   node_output_line(vunits, node *, hunits, vunits, vunits);
  106.   ~node_output_line();
  107.   void output(output_file *, vunits);
  108.   vunits height();
  109.   vunits extra_space();
  110. };
  111.  
  112. class vjustify_output_line : public position_output_line {
  113.   vunits current;
  114.   symbol typ;
  115. public:
  116.   vjustify_output_line(vunits dist, symbol);
  117.   vunits height();
  118.   vjustify_output_line *as_vjustify_output_line();
  119.   void vary(vunits amount);
  120.   void reset();
  121.   symbol type();
  122. };
  123.  
  124. inline symbol vjustify_output_line::type()
  125. {
  126.   return typ;
  127. }
  128.  
  129. class copy_file_output_line : public position_output_line {
  130.   symbol filename;
  131.   hunits hpos;
  132. public:
  133.   copy_file_output_line(vunits, const char *, hunits);
  134.   void output(output_file *, vunits);
  135. };
  136.  
  137. class transparent_output_line : public output_line {
  138.   string buf;
  139. public:
  140.   transparent_output_line();
  141.   void output(output_file *, vunits);
  142.   void append_char(unsigned char c);
  143.   transparent_output_line *as_transparent_output_line();
  144. };
  145.  
  146. output_line::output_line() : next(0)
  147. {
  148. }
  149.  
  150. output_line::~output_line()
  151. {
  152. }
  153.  
  154. void output_line::reset()
  155. {
  156. }
  157.  
  158. transparent_output_line *output_line::as_transparent_output_line()
  159. {
  160.   return 0;
  161. }
  162.  
  163. vjustify_output_line *output_line::as_vjustify_output_line()
  164. {
  165.   return 0;
  166. }
  167.  
  168. void output_line::output(output_file *, vunits)
  169. {
  170. }
  171.  
  172. vunits output_line::distance()
  173. {
  174.   return V0;
  175. }
  176.  
  177. vunits output_line::height()
  178. {
  179.   return V0;
  180. }
  181.  
  182. vunits output_line::extra_space()
  183. {
  184.   return V0;
  185. }
  186.  
  187. position_output_line::position_output_line(vunits d)
  188. : dist(d)
  189. {
  190. }
  191.  
  192. vunits position_output_line::distance()
  193. {
  194.   return dist;
  195. }
  196.  
  197. node_output_line::node_output_line(vunits d, node *n, hunits po, vunits b, vunits a)
  198. : position_output_line(d), nd(n), page_offset(po), before(b), after(a)
  199. {
  200. }
  201.  
  202. node_output_line::~node_output_line()
  203. {
  204.   delete_node_list(nd);
  205. }
  206.  
  207. void node_output_line::output(output_file *out, vunits pos)
  208. {
  209.   out->print_line(page_offset, pos, nd, before, after);
  210.   nd = 0;
  211. }
  212.  
  213. vunits node_output_line::height()
  214. {
  215.   return after;
  216. }
  217.  
  218. vunits node_output_line::extra_space()
  219. {
  220.   return after;
  221. }
  222.  
  223. vjustify_output_line::vjustify_output_line(vunits d, symbol t)
  224. : position_output_line(d), typ(t)
  225. {
  226. }
  227.  
  228. void vjustify_output_line::reset()
  229. {
  230.   current = V0;
  231. }
  232.  
  233. vunits vjustify_output_line::height()
  234. {
  235.   return current;
  236. }
  237.  
  238. vjustify_output_line *vjustify_output_line::as_vjustify_output_line()
  239. {
  240.   return this;
  241. }
  242.  
  243. inline void vjustify_output_line::vary(vunits amount)
  244. {
  245.   current += amount;
  246. }
  247.  
  248. transparent_output_line::transparent_output_line()
  249. {
  250. }
  251.  
  252. transparent_output_line *transparent_output_line::as_transparent_output_line()
  253. {
  254.   return this;
  255. }
  256.  
  257. void transparent_output_line::append_char(unsigned char c)
  258. {
  259.   assert(c != 0);
  260.   buf += c;
  261. }
  262.  
  263. void transparent_output_line::output(output_file *out, vunits)
  264. {
  265.   int len = buf.length();
  266.   for (int i = 0; i < len; i++)
  267.     out->transparent_char(buf[i]);
  268. }
  269.  
  270. copy_file_output_line::copy_file_output_line(vunits d, const char *f, hunits h)
  271. : position_output_line(d), hpos(h), filename(f)
  272. {
  273. }
  274.  
  275. void copy_file_output_line::output(output_file *out, vunits pos)
  276. {
  277.   out->copy_file(hpos, pos, filename.contents());
  278. }
  279.  
  280. column::column()
  281. : bottom(V0), col(0), tail(&col), out(0)
  282. {
  283. }
  284.  
  285. column::~column()
  286. {
  287.   assert(out != 0);
  288.   error("automatically outputting column before exiting");
  289.   output();
  290.   delete the_output;
  291. }
  292.  
  293. void column::start()
  294. {
  295.   assert(out == 0);
  296.   if (!the_output)
  297.     init_output();
  298.   assert(the_output != 0);
  299.   out = the_output;
  300.   the_output = this;
  301. }
  302.  
  303. void column::begin_page(int pageno, vunits page_length)
  304. {
  305.   assert(out != 0);
  306.   if (col) {
  307.     error("automatically outputting column before beginning next page");
  308.     output();
  309.     the_output->begin_page(pageno, page_length);
  310.   }
  311.   else
  312.     out->begin_page(pageno, page_length);
  313.     
  314. }
  315.  
  316. void column::flush()
  317. {
  318.   assert(out != 0);
  319.   out->flush();
  320. }
  321.  
  322. int column::is_printing()
  323. {
  324.   assert(out != 0);
  325.   return out->is_printing();
  326. }
  327.  
  328. vunits column::get_bottom()
  329. {
  330.   return bottom;
  331. }
  332.  
  333. void column::add_output_line(output_line *ln)
  334. {
  335.   *tail = ln;
  336.   bottom += ln->distance();
  337.   bottom += ln->height();
  338.   ln->next = 0;
  339.   tail = &(*tail)->next;
  340. }
  341.  
  342. void column::print_line(hunits page_offset, vunits pos, node *nd,
  343.             vunits before, vunits after)
  344. {
  345.   assert(out != 0);
  346.   add_output_line(new node_output_line(pos - bottom, nd, page_offset, before, after));
  347. }
  348.  
  349. void column::vjustify(vunits pos, symbol typ)
  350. {
  351.   assert(out != 0);
  352.   add_output_line(new vjustify_output_line(pos - bottom, typ));
  353. }
  354.  
  355. void column::transparent_char(unsigned char c)
  356. {
  357.   assert(out != 0);
  358.   transparent_output_line *tl = 0;
  359.   if (*tail)
  360.     tl = (*tail)->as_transparent_output_line();
  361.   if (!tl) {
  362.     tl = new transparent_output_line;
  363.     add_output_line(tl);
  364.   }
  365.   tl->append_char(c);
  366. }
  367.  
  368. void column::copy_file(hunits page_offset, vunits pos, const char *filename)
  369. {
  370.   assert(out != 0);
  371.   add_output_line(new copy_file_output_line(pos - bottom, filename, page_offset));
  372. }
  373.  
  374. void column::trim()
  375. {
  376.   output_line **spp = 0;
  377.   for (output_line **pp = &col; *pp; pp = &(*pp)->next)
  378.     if ((*pp)->as_vjustify_output_line() == 0)
  379.       spp = 0;
  380.     else if (!spp)
  381.       spp = pp;
  382.   if (spp) {
  383.     output_line *ln = *spp;
  384.     *spp = 0;
  385.     tail = spp;
  386.     while (ln) {
  387.       output_line *tem = ln->next;
  388.       bottom -= ln->distance();
  389.       bottom -= ln->height();
  390.       delete ln;
  391.       ln = tem;
  392.     }
  393.   }
  394. }
  395.  
  396. void column::reset()
  397. {
  398.   bottom = V0;
  399.   for (output_line *ln = col; ln; ln = ln->next) {
  400.     bottom += ln->distance();
  401.     ln->reset();
  402.     bottom += ln->height();
  403.   }
  404. }
  405.  
  406. void column::check_bottom()
  407. {
  408.   vunits b;
  409.   for (output_line *ln = col; ln; ln = ln->next) {
  410.     b += ln->distance();
  411.     b += ln->height();
  412.   }
  413.   assert(b == bottom);
  414. }
  415.  
  416. void column::output()
  417. {
  418.   assert(out != 0);
  419.   vunits vpos(V0);
  420.   output_line *ln = col;
  421.   while (ln) {
  422.     vpos += ln->distance();
  423.     ln->output(out, vpos);
  424.     vpos += ln->height();
  425.     output_line *tem = ln->next;
  426.     delete ln;
  427.     ln = tem;
  428.   }
  429.   tail = &col;
  430.   bottom = V0;
  431.   col = 0;
  432.   the_output = out;
  433.   out = 0;
  434. }
  435.  
  436. vunits column::get_last_extra_space()
  437. {
  438.   if (!col)
  439.     return V0;
  440.   for (output_line *p = col; p->next; p = p->next)
  441.     ;
  442.   return p->extra_space();
  443. }
  444.  
  445. class justification_spec {
  446.   vunits height;
  447.   symbol *type;
  448.   vunits *amount;
  449.   int n;
  450.   int maxn;
  451. public:
  452.   justification_spec(vunits);
  453.   ~justification_spec();
  454.   void append(symbol t, vunits v);
  455.   void justify(output_line *, vunits *bottomp) const;
  456. };
  457.  
  458. justification_spec::justification_spec(vunits h)
  459. : height(h), n(0), maxn(10)
  460. {
  461.   type = new symbol[maxn];
  462.   amount = new vunits[maxn];
  463. }
  464.  
  465. justification_spec::~justification_spec()
  466. {
  467.   a_delete type;
  468.   a_delete amount;
  469. }
  470.  
  471. void justification_spec::append(symbol t, vunits v)
  472. {
  473.   if (v <= V0) {
  474.     if (v < V0)
  475.       warning(WARN_RANGE,
  476.           "maximum space for vertical justification must not be negative");
  477.     else
  478.       warning(WARN_RANGE,
  479.           "maximum space for vertical justification must not be zero");
  480.     return;
  481.   }
  482.   if (n >= maxn) {
  483.     maxn *= 2;
  484.     symbol *old_type = type;
  485.     type = new symbol[maxn];
  486.     int i;
  487.     for (i = 0; i < n; i++)
  488.       type[i] = old_type[i];
  489.     a_delete old_type;
  490.     vunits *old_amount = amount;
  491.     amount = new vunits[maxn];
  492.     for (i = 0; i < n; i++)
  493.       amount[i] = old_amount[i];
  494.     a_delete old_amount;
  495.   }
  496.   assert(n < maxn);
  497.   type[n] = t;
  498.   amount[n] = v;
  499.   n++;
  500. }
  501.  
  502. void justification_spec::justify(output_line *col, vunits *bottomp) const
  503. {
  504.   if (*bottomp >= height)
  505.     return;
  506.   vunits total;
  507.   output_line *p;
  508.   for (p = col; p; p = p->next) {
  509.     vjustify_output_line *sp = p->as_vjustify_output_line();
  510.     if (sp) {
  511.       symbol t = sp->type();
  512.       for (int i = 0; i < n; i++) {
  513.     if (t == type[i])
  514.       total += amount[i];
  515.       }
  516.     }
  517.   }
  518.   vunits gap = height - *bottomp;
  519.   for (p = col; p; p = p->next) {
  520.     vjustify_output_line *sp = p->as_vjustify_output_line();
  521.     if (sp) {
  522.       symbol t = sp->type();
  523.       for (int i = 0; i < n; i++) {
  524.     if (t == type[i]) {
  525.       if (total <= gap) {
  526.         sp->vary(amount[i]);
  527.         gap -= amount[i];
  528.       }
  529.       else {
  530.         // gap < total
  531.         vunits v = scale(amount[i], gap, total);
  532.         sp->vary(v);
  533.         gap -= v;
  534.       }
  535.       total -= amount[i];
  536.     }
  537.       }
  538.     }
  539.   }
  540.   assert(total == V0);
  541.   *bottomp = height - gap;
  542. }
  543.   
  544. void column::justify(const justification_spec &js)
  545. {
  546.   check_bottom();
  547.   js.justify(col, &bottom);
  548.   check_bottom();
  549. }
  550.  
  551. void column_justify()
  552. {
  553.   vunits height;
  554.   if (!the_column->is_active())
  555.     error("can't justify column - column not active");
  556.   else if (get_vunits(&height, 'v')) {
  557.     justification_spec js(height);
  558.     symbol nm = get_long_name(1);
  559.     if (!nm.is_null()) {
  560.       vunits v;
  561.       if (get_vunits(&v, 'v')) {
  562.     js.append(nm, v);
  563.     int err = 0;
  564.     while (has_arg()) {
  565.       nm = get_long_name(1);
  566.       if (nm.is_null()) {
  567.         err = 1;
  568.         break;
  569.       }
  570.       if (!get_vunits(&v, 'v')) {
  571.         err = 1;
  572.         break;
  573.       }
  574.       js.append(nm, v);
  575.     }
  576.     if (!err)
  577.       the_column->justify(js);
  578.       }
  579.     }
  580.   }
  581.   skip_line();
  582. }
  583.  
  584. void column_start()
  585. {
  586.   if (the_column->is_active())
  587.     error("can't start column - column already active");
  588.   else
  589.     the_column->start();
  590.   skip_line();
  591. }
  592.  
  593. void column_output()
  594. {
  595.   if (!the_column->is_active())
  596.     error("can't output column - column not active");
  597.   else
  598.     the_column->output();
  599.   skip_line();
  600. }
  601.  
  602. void column_trim()
  603. {
  604.   if (!the_column->is_active())
  605.     error("can't trim column - column not active");
  606.   else
  607.     the_column->trim();
  608.   skip_line();
  609. }
  610.  
  611. void column_reset()
  612. {
  613.   if (!the_column->is_active())
  614.     error("can't reset column - column not active");
  615.   else
  616.     the_column->reset();
  617.   skip_line();
  618. }
  619.  
  620. class column_bottom_reg : public reg {
  621. public:
  622.   const char *get_string();
  623. };
  624.  
  625. const char *column_bottom_reg::get_string()
  626. {
  627.   return itoa(the_column->get_bottom().to_units());
  628. }
  629.  
  630. class column_extra_space_reg : public reg {
  631. public:
  632.   const char *get_string();
  633. };
  634.  
  635. const char *column_extra_space_reg::get_string()
  636. {
  637.   return itoa(the_column->get_last_extra_space().to_units());
  638. }
  639.  
  640. class column_active_reg : public reg {
  641. public:
  642.   const char *get_string();
  643. };
  644.  
  645. const char *column_active_reg::get_string()
  646. {
  647.   return the_column->is_active() ? "1" : "0";
  648. }
  649.  
  650. static int no_vjustify_mode = 0;
  651.  
  652. class vjustify_node : public node {
  653.   symbol typ;
  654. public:
  655.   vjustify_node(symbol);
  656.   int reread(int *);
  657.   const char *type();
  658.   int same(node *);
  659.   node *copy();
  660. };
  661.  
  662. vjustify_node::vjustify_node(symbol t)
  663. : typ(t)
  664. {
  665. }
  666.  
  667. node *vjustify_node::copy()
  668. {
  669.   return new vjustify_node(typ);
  670. }
  671.  
  672. const char *vjustify_node::type()
  673. {
  674.   return "vjustify_node";
  675. }
  676.  
  677. int vjustify_node::same(node *nd)
  678. {
  679.   return typ == ((vjustify_node *)nd)->typ;
  680. }
  681.  
  682. int vjustify_node::reread(int *bolp)
  683. {
  684.   curdiv->vjustify(typ);
  685.   *bolp = 1;
  686.   return 1;
  687. }
  688.  
  689. void macro_diversion::vjustify(symbol type)
  690. {
  691.   if (!no_vjustify_mode)
  692.     mac->append(new vjustify_node(type));
  693. }
  694.  
  695. void top_level_diversion::vjustify(symbol type)
  696. {
  697.   if (no_space_mode || no_vjustify_mode)
  698.     return;
  699.   assert(first_page_begun);    // I'm not sure about this.
  700.   the_output->vjustify(vertical_position, type);
  701. }
  702.  
  703. void no_vjustify()
  704. {
  705.   skip_line();
  706.   no_vjustify_mode = 1;
  707. }
  708.  
  709. void restore_vjustify()
  710. {
  711.   skip_line();
  712.   no_vjustify_mode = 0;
  713. }
  714.  
  715. void init_column_requests()
  716. {
  717.   the_column = new column;
  718.   init_request("cols", column_start);
  719.   init_request("colo", column_output);
  720.   init_request("colj", column_justify);
  721.   init_request("colr", column_reset);
  722.   init_request("colt", column_trim);
  723.   init_request("nvj", no_vjustify);
  724.   init_request("rvj", restore_vjustify);
  725.   number_reg_dictionary.define(".colb", new column_bottom_reg);
  726.   number_reg_dictionary.define(".colx", new column_extra_space_reg);
  727.   number_reg_dictionary.define(".cola", new column_active_reg);
  728.   number_reg_dictionary.define(".nvj",
  729.                    new constant_int_reg(&no_vjustify_mode));
  730. }
  731.  
  732. #endif /* COLUMN */
  733.