* All affected files: Update postal address of FSF.
[s-roff.git] / src / roff / troff / column.cpp
blob060b088d235544ae58247f8b882ae369694c64a1
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
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.
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.
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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21 #ifdef COLUMN
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"
35 void output_file::vjustify(vunits, symbol)
37 // do nothing
40 struct justification_spec;
41 struct output_line;
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; }
71 column *the_column = 0;
73 struct transparent_output_line;
74 struct vjustify_output_line;
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;
92 class position_output_line : public output_line {
93 vunits dist;
94 public:
95 position_output_line(vunits);
96 vunits distance();
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();
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();
124 inline symbol vjustify_output_line::type()
126 return typ;
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);
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();
146 output_line::output_line() : next(0)
150 output_line::~output_line()
154 void output_line::reset()
158 transparent_output_line *output_line::as_transparent_output_line()
160 return 0;
163 vjustify_output_line *output_line::as_vjustify_output_line()
165 return 0;
168 void output_line::output(output_file *, vunits)
172 vunits output_line::distance()
174 return V0;
177 vunits output_line::height()
179 return V0;
182 vunits output_line::extra_space()
184 return V0;
187 position_output_line::position_output_line(vunits d)
188 : dist(d)
192 vunits position_output_line::distance()
194 return dist;
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)
202 node_output_line::~node_output_line()
204 delete_node_list(nd);
207 void node_output_line::output(output_file *out, vunits pos)
209 out->print_line(page_offset, pos, nd, before, after);
210 nd = 0;
213 vunits node_output_line::height()
215 return after;
218 vunits node_output_line::extra_space()
220 return after;
223 vjustify_output_line::vjustify_output_line(vunits d, symbol t)
224 : position_output_line(d), typ(t)
228 void vjustify_output_line::reset()
230 current = V0;
233 vunits vjustify_output_line::height()
235 return current;
238 vjustify_output_line *vjustify_output_line::as_vjustify_output_line()
240 return this;
243 inline void vjustify_output_line::vary(vunits amount)
245 current += amount;
248 transparent_output_line::transparent_output_line()
252 transparent_output_line *transparent_output_line::as_transparent_output_line()
254 return this;
257 void transparent_output_line::append_char(unsigned char c)
259 assert(c != 0);
260 buf += c;
263 void transparent_output_line::output(output_file *out, vunits)
265 int len = buf.length();
266 for (int i = 0; i < len; i++)
267 out->transparent_char(buf[i]);
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)
275 void copy_file_output_line::output(output_file *out, vunits pos)
277 out->copy_file(hpos, pos, filename.contents());
280 column::column()
281 : bottom(V0), col(0), tail(&col), out(0)
285 column::~column()
287 assert(out != 0);
288 error("automatically outputting column before exiting");
289 output();
290 delete the_output;
293 void column::start()
295 assert(out == 0);
296 if (!the_output)
297 init_output();
298 assert(the_output != 0);
299 out = the_output;
300 the_output = this;
303 void column::begin_page(int pageno, vunits page_length)
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);
311 else
312 out->begin_page(pageno, page_length);
316 void column::flush()
318 assert(out != 0);
319 out->flush();
322 int column::is_printing()
324 assert(out != 0);
325 return out->is_printing();
328 vunits column::get_bottom()
330 return bottom;
333 void column::add_output_line(output_line *ln)
335 *tail = ln;
336 bottom += ln->distance();
337 bottom += ln->height();
338 ln->next = 0;
339 tail = &(*tail)->next;
342 void column::print_line(hunits page_offset, vunits pos, node *nd,
343 vunits before, vunits after)
345 assert(out != 0);
346 add_output_line(new node_output_line(pos - bottom, nd, page_offset, before, after));
349 void column::vjustify(vunits pos, symbol typ)
351 assert(out != 0);
352 add_output_line(new vjustify_output_line(pos - bottom, typ));
355 void column::transparent_char(unsigned char c)
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);
365 tl->append_char(c);
368 void column::copy_file(hunits page_offset, vunits pos, const char *filename)
370 assert(out != 0);
371 add_output_line(new copy_file_output_line(pos - bottom, filename, page_offset));
374 void column::trim()
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;
396 void column::reset()
398 bottom = V0;
399 for (output_line *ln = col; ln; ln = ln->next) {
400 bottom += ln->distance();
401 ln->reset();
402 bottom += ln->height();
406 void column::check_bottom()
408 vunits b;
409 for (output_line *ln = col; ln; ln = ln->next) {
410 b += ln->distance();
411 b += ln->height();
413 assert(b == bottom);
416 void column::output()
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;
429 tail = &col;
430 bottom = V0;
431 col = 0;
432 the_output = out;
433 out = 0;
436 vunits column::get_last_extra_space()
438 if (!col)
439 return V0;
440 for (output_line *p = col; p->next; p = p->next)
442 return p->extra_space();
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;
458 justification_spec::justification_spec(vunits h)
459 : height(h), n(0), maxn(10)
461 type = new symbol[maxn];
462 amount = new vunits[maxn];
465 justification_spec::~justification_spec()
467 a_delete type;
468 a_delete amount;
471 void justification_spec::append(symbol t, vunits v)
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;
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;
496 assert(n < maxn);
497 type[n] = t;
498 amount[n] = v;
499 n++;
502 void justification_spec::justify(output_line *col, vunits *bottomp) const
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];
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];
529 else {
530 // gap < total
531 vunits v = scale(amount[i], gap, total);
532 sp->vary(v);
533 gap -= v;
535 total -= amount[i];
540 assert(total == V0);
541 *bottomp = height - gap;
544 void column::justify(const justification_spec &js)
546 check_bottom();
547 js.justify(col, &bottom);
548 check_bottom();
551 void column_justify()
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;
570 if (!get_vunits(&v, 'v')) {
571 err = 1;
572 break;
574 js.append(nm, v);
576 if (!err)
577 the_column->justify(js);
581 skip_line();
584 void column_start()
586 if (the_column->is_active())
587 error("can't start column - column already active");
588 else
589 the_column->start();
590 skip_line();
593 void column_output()
595 if (!the_column->is_active())
596 error("can't output column - column not active");
597 else
598 the_column->output();
599 skip_line();
602 void column_trim()
604 if (!the_column->is_active())
605 error("can't trim column - column not active");
606 else
607 the_column->trim();
608 skip_line();
611 void column_reset()
613 if (!the_column->is_active())
614 error("can't reset column - column not active");
615 else
616 the_column->reset();
617 skip_line();
620 class column_bottom_reg : public reg {
621 public:
622 const char *get_string();
625 const char *column_bottom_reg::get_string()
627 return i_to_a(the_column->get_bottom().to_units());
630 class column_extra_space_reg : public reg {
631 public:
632 const char *get_string();
635 const char *column_extra_space_reg::get_string()
637 return i_to_a(the_column->get_last_extra_space().to_units());
640 class column_active_reg : public reg {
641 public:
642 const char *get_string();
645 const char *column_active_reg::get_string()
647 return the_column->is_active() ? "1" : "0";
650 static int no_vjustify_mode = 0;
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();
662 vjustify_node::vjustify_node(symbol t)
663 : typ(t)
667 node *vjustify_node::copy()
669 return new vjustify_node(typ, div_nest_level);
672 const char *vjustify_node::type()
674 return "vjustify_node";
677 int vjustify_node::same(node *nd)
679 return typ == ((vjustify_node *)nd)->typ;
682 int vjustify_node::reread(int *bolp)
684 curdiv->vjustify(typ);
685 *bolp = 1;
686 return 1;
689 void macro_diversion::vjustify(symbol type)
691 if (!no_vjustify_mode)
692 mac->append(new vjustify_node(type));
695 void top_level_diversion::vjustify(symbol type)
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);
703 void no_vjustify()
705 skip_line();
706 no_vjustify_mode = 1;
709 void restore_vjustify()
711 skip_line();
712 no_vjustify_mode = 0;
715 void init_column_requests()
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));
732 #endif /* COLUMN */