column.cpp revision 259065
119304Speter// -*- C++ -*-
219304Speter/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc.
319304Speter     Written by James Clark (jjc@jclark.com)
419304Speter
519304SpeterThis file is part of groff.
619304Speter
719304Spetergroff is free software; you can redistribute it and/or modify it under
819304Speterthe terms of the GNU General Public License as published by the Free
919304SpeterSoftware Foundation; either version 2, or (at your option) any later
1019304Speterversion.
1119304Speter
1219304Spetergroff is distributed in the hope that it will be useful, but WITHOUT ANY
13254225SpeterWARRANTY; without even the implied warranty of MERCHANTABILITY or
1419304SpeterFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1519304Speterfor more details.
1619304Speter
1719304SpeterYou should have received a copy of the GNU General Public License along
1819304Speterwith groff; see the file COPYING.  If not, write to the Free Software
1919304SpeterFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
2019304Speter
2119304Speter#ifdef COLUMN
2219304Speter
2319304Speter#include "troff.h"
2419304Speter#include "symbol.h"
2519304Speter#include "dictionary.h"
2619304Speter#include "hvunits.h"
2719304Speter#include "env.h"
2819304Speter#include "request.h"
2919304Speter#include "node.h"
3019304Speter#include "token.h"
3119304Speter#include "div.h"
3219304Speter#include "reg.h"
3319304Speter#include "stringclass.h"
3419304Speter
3519304Spetervoid output_file::vjustify(vunits, symbol)
3619304Speter{
3719304Speter  // do nothing
3819304Speter}
3919304Speter
4019304Speterstruct justification_spec;
4119304Speterstruct output_line;
4219304Speter
4319304Speterclass column : public output_file {
4419304Speterprivate:
4519304Speter  output_file *out;
4619304Speter  vunits bottom;
4719304Speter  output_line *col;
4819304Speter  output_line **tail;
4919304Speter  void add_output_line(output_line *);
5019304Speter  void begin_page(int pageno, vunits page_length);
5119304Speter  void flush();
5219304Speter  void print_line(hunits, vunits, node *, vunits, vunits);
5319304Speter  void vjustify(vunits, symbol);
5419304Speter  void transparent_char(unsigned char c);
5519304Speter  void copy_file(hunits, vunits, const char *);
5619304Speter  int is_printing();
5719304Speter  void check_bottom();
5819304Speterpublic:
5919304Speter  column();
6019304Speter  ~column();
6119304Speter  void start();
6219304Speter  void output();
6319304Speter  void justify(const justification_spec &);
6419304Speter  void trim();
65254225Speter  void reset();
6619304Speter  vunits get_bottom();
6719304Speter  vunits get_last_extra_space();
6819304Speter  int is_active() { return out != 0; }
69254225Speter};
70254225Speter
7119304Spetercolumn *the_column = 0;
7219304Speter
7319304Speterstruct transparent_output_line;
7419304Speterstruct vjustify_output_line;
7519304Speter
7619304Speterclass output_line {
7719304Speter  output_line *next;
7819304Speterpublic:
7919304Speter  output_line();
8019304Speter  virtual ~output_line();
8119304Speter  virtual void output(output_file *, vunits);
8219304Speter  virtual transparent_output_line *as_transparent_output_line();
8319304Speter  virtual vjustify_output_line *as_vjustify_output_line();
8419304Speter  virtual vunits distance();
8519304Speter  virtual vunits height();
8619304Speter  virtual void reset();
8719304Speter  virtual vunits extra_space();	// post line
8819304Speter  friend class column;
8919304Speter  friend class justification_spec;
9019304Speter};
9119304Speter
9219304Speterclass position_output_line : public output_line {
9319304Speter  vunits dist;
9419304Speterpublic:
9519304Speter  position_output_line(vunits);
9619304Speter  vunits distance();
9719304Speter};
9819304Speter
9919304Speterclass node_output_line : public position_output_line {
100254225Speter  node *nd;
10119304Speter  hunits page_offset;
10219304Speter  vunits before;
10319304Speter  vunits after;
10419304Speterpublic:
10519304Speter  node_output_line(vunits, node *, hunits, vunits, vunits);
10619304Speter  ~node_output_line();
10719304Speter  void output(output_file *, vunits);
10819304Speter  vunits height();
10919304Speter  vunits extra_space();
11019304Speter};
11119304Speter
11219304Speterclass vjustify_output_line : public position_output_line {
11319304Speter  vunits current;
11419304Speter  symbol typ;
11519304Speterpublic:
11619304Speter  vjustify_output_line(vunits dist, symbol);
11719304Speter  vunits height();
11819304Speter  vjustify_output_line *as_vjustify_output_line();
11919304Speter  void vary(vunits amount);
12019304Speter  void reset();
12119304Speter  symbol type();
12219304Speter};
12319304Speter
124254225Speterinline symbol vjustify_output_line::type()
12519304Speter{
12619304Speter  return typ;
12719304Speter}
12819304Speter
12919304Speterclass copy_file_output_line : public position_output_line {
13019304Speter  symbol filename;
13119304Speter  hunits hpos;
13219304Speterpublic:
13319304Speter  copy_file_output_line(vunits, const char *, hunits);
13419304Speter  void output(output_file *, vunits);
13519304Speter};
13619304Speter
13719304Speterclass transparent_output_line : public output_line {
13819304Speter  string buf;
13919304Speterpublic:
14019304Speter  transparent_output_line();
14119304Speter  void output(output_file *, vunits);
14219304Speter  void append_char(unsigned char c);
14319304Speter  transparent_output_line *as_transparent_output_line();
14419304Speter};
14519304Speter
14619304Speteroutput_line::output_line() : next(0)
14719304Speter{
14819304Speter}
14919304Speter
15019304Speteroutput_line::~output_line()
15119304Speter{
15219304Speter}
15319304Speter
15419304Spetervoid output_line::reset()
15519304Speter{
15619304Speter}
15719304Speter
15819304Spetertransparent_output_line *output_line::as_transparent_output_line()
15919304Speter{
16019304Speter  return 0;
16119304Speter}
16219304Speter
16319304Spetervjustify_output_line *output_line::as_vjustify_output_line()
16419304Speter{
16519304Speter  return 0;
16619304Speter}
16719304Speter
16819304Spetervoid output_line::output(output_file *, vunits)
16919304Speter{
17019304Speter}
17119304Speter
172254225Spetervunits output_line::distance()
17319304Speter{
17419304Speter  return V0;
17519304Speter}
176254225Speter
177254225Spetervunits output_line::height()
17819304Speter{
17919304Speter  return V0;
18019304Speter}
18119304Speter
18219304Spetervunits output_line::extra_space()
18319304Speter{
18419304Speter  return V0;
18519304Speter}
18619304Speter
18719304Speterposition_output_line::position_output_line(vunits d)
18819304Speter: dist(d)
18919304Speter{
19019304Speter}
19119304Speter
19219304Spetervunits position_output_line::distance()
19319304Speter{
19419304Speter  return dist;
19519304Speter}
19619304Speter
19719304Speternode_output_line::node_output_line(vunits d, node *n, hunits po, vunits b, vunits a)
19819304Speter: position_output_line(d), nd(n), page_offset(po), before(b), after(a)
19919304Speter{
20019304Speter}
20119304Speter
20219304Speternode_output_line::~node_output_line()
20319304Speter{
20419304Speter  delete_node_list(nd);
20519304Speter}
20619304Speter
20719304Spetervoid node_output_line::output(output_file *out, vunits pos)
20819304Speter{
20919304Speter  out->print_line(page_offset, pos, nd, before, after);
21019304Speter  nd = 0;
21119304Speter}
21219304Speter
21319304Spetervunits node_output_line::height()
214254225Speter{
21519304Speter  return after;
21619304Speter}
21719304Speter
21819304Spetervunits node_output_line::extra_space()
21919304Speter{
22019304Speter  return after;
22119304Speter}
22219304Speter
22319304Spetervjustify_output_line::vjustify_output_line(vunits d, symbol t)
22419304Speter: position_output_line(d), typ(t)
22519304Speter{
22619304Speter}
22719304Speter
22819304Spetervoid vjustify_output_line::reset()
22919304Speter{
23019304Speter  current = V0;
23119304Speter}
23219304Speter
23319304Spetervunits vjustify_output_line::height()
23419304Speter{
23519304Speter  return current;
23619304Speter}
23719304Speter
23819304Spetervjustify_output_line *vjustify_output_line::as_vjustify_output_line()
23919304Speter{
24019304Speter  return this;
24119304Speter}
24219304Speter
24319304Speterinline void vjustify_output_line::vary(vunits amount)
24419304Speter{
24519304Speter  current += amount;
24619304Speter}
24719304Speter
24819304Spetertransparent_output_line::transparent_output_line()
24919304Speter{
25019304Speter}
251
252transparent_output_line *transparent_output_line::as_transparent_output_line()
253{
254  return this;
255}
256
257void transparent_output_line::append_char(unsigned char c)
258{
259  assert(c != 0);
260  buf += c;
261}
262
263void 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
270copy_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
275void copy_file_output_line::output(output_file *out, vunits pos)
276{
277  out->copy_file(hpos, pos, filename.contents());
278}
279
280column::column()
281: bottom(V0), col(0), tail(&col), out(0)
282{
283}
284
285column::~column()
286{
287  assert(out != 0);
288  error("automatically outputting column before exiting");
289  output();
290  delete the_output;
291}
292
293void 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
303void 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
316void column::flush()
317{
318  assert(out != 0);
319  out->flush();
320}
321
322int column::is_printing()
323{
324  assert(out != 0);
325  return out->is_printing();
326}
327
328vunits column::get_bottom()
329{
330  return bottom;
331}
332
333void 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
342void 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
349void column::vjustify(vunits pos, symbol typ)
350{
351  assert(out != 0);
352  add_output_line(new vjustify_output_line(pos - bottom, typ));
353}
354
355void 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
368void 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
374void 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
396void 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
406void 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
416void 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
436vunits 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
445class justification_spec {
446  vunits height;
447  symbol *type;
448  vunits *amount;
449  int n;
450  int maxn;
451public:
452  justification_spec(vunits);
453  ~justification_spec();
454  void append(symbol t, vunits v);
455  void justify(output_line *, vunits *bottomp) const;
456};
457
458justification_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
465justification_spec::~justification_spec()
466{
467  a_delete type;
468  a_delete amount;
469}
470
471void 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
502void 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
544void column::justify(const justification_spec &js)
545{
546  check_bottom();
547  js.justify(col, &bottom);
548  check_bottom();
549}
550
551void 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
584void 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
593void 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
602void 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
611void 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
620class column_bottom_reg : public reg {
621public:
622  const char *get_string();
623};
624
625const char *column_bottom_reg::get_string()
626{
627  return i_to_a(the_column->get_bottom().to_units());
628}
629
630class column_extra_space_reg : public reg {
631public:
632  const char *get_string();
633};
634
635const char *column_extra_space_reg::get_string()
636{
637  return i_to_a(the_column->get_last_extra_space().to_units());
638}
639
640class column_active_reg : public reg {
641public:
642  const char *get_string();
643};
644
645const char *column_active_reg::get_string()
646{
647  return the_column->is_active() ? "1" : "0";
648}
649
650static int no_vjustify_mode = 0;
651
652class vjustify_node : public node {
653  symbol typ;
654public:
655  vjustify_node(symbol);
656  int reread(int *);
657  const char *type();
658  int same(node *);
659  node *copy();
660};
661
662vjustify_node::vjustify_node(symbol t)
663: typ(t)
664{
665}
666
667node *vjustify_node::copy()
668{
669  return new vjustify_node(typ, div_nest_level);
670}
671
672const char *vjustify_node::type()
673{
674  return "vjustify_node";
675}
676
677int vjustify_node::same(node *nd)
678{
679  return typ == ((vjustify_node *)nd)->typ;
680}
681
682int vjustify_node::reread(int *bolp)
683{
684  curdiv->vjustify(typ);
685  *bolp = 1;
686  return 1;
687}
688
689void macro_diversion::vjustify(symbol type)
690{
691  if (!no_vjustify_mode)
692    mac->append(new vjustify_node(type));
693}
694
695void 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
703void no_vjustify()
704{
705  skip_line();
706  no_vjustify_mode = 1;
707}
708
709void restore_vjustify()
710{
711  skip_line();
712  no_vjustify_mode = 0;
713}
714
715void 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