html-table.cpp revision 114402
1// -*- C++ -*-
2/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
3 *
4 *  Gaius Mulley (gaius@glam.ac.uk) wrote html-table.cpp
5 *
6 *  html-table.h
7 *
8 *  provides the methods necessary to handle indentation and tab
9 *  positions using html tables.
10 */
11
12/*
13This file is part of groff.
14
15groff is free software; you can redistribute it and/or modify it under
16the terms of the GNU General Public License as published by the Free
17Software Foundation; either version 2, or (at your option) any later
18version.
19
20groff is distributed in the hope that it will be useful, but WITHOUT ANY
21WARRANTY; without even the implied warranty of MERCHANTABILITY or
22FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
23for more details.
24
25You should have received a copy of the GNU General Public License along
26with groff; see the file COPYING.  If not, write to the Free Software
27Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
28
29#include "driver.h"
30#include "stringclass.h"
31#include "cset.h"
32#include "html-table.h"
33#include "ctype.h"
34#include "html.h"
35
36#if !defined(TRUE)
37#   define TRUE  (1==1)
38#endif
39#if !defined(FALSE)
40#   define FALSE (1==0)
41#endif
42
43tabs::tabs ()
44  : tab(NULL)
45{
46}
47
48tabs::~tabs ()
49{
50  delete_list();
51}
52
53/*
54 *  delete_list - frees the tab list and sets tab to NULL.
55 */
56
57void tabs::delete_list (void)
58{
59  tab_position *p = tab;
60  tab_position *q;
61
62  while (p != NULL) {
63    q = p;
64    p = p->next;
65    free(q);
66  }
67  tab = NULL;
68}
69
70void tabs::clear (void)
71{
72  delete_list();
73}
74
75/*
76 *  compatible - returns TRUE if the tab stops in, s, do
77 *               not conflict with the current tab stops.
78 *               The new tab stops are _not_ placed into
79 *               this class.
80 */
81
82int tabs::compatible (const char *s)
83{
84  char align;
85  int  total=0;
86  tab_position *last = tab;
87
88  if (last == NULL)
89    return FALSE;  // no tab stops defined
90
91  // move over tag name
92  while ((*s != (char)0) && !isspace(*s))
93    s++;
94
95  while (*s != (char)0 && last != NULL) {
96    // move over white space
97    while ((*s != (char)0) && isspace(*s))
98      s++;
99    // collect alignment
100    align = *s;
101    // move over alignment
102    s++;
103    // move over white space
104    while ((*s != (char)0) && isspace(*s))
105      s++;
106    // collect tab position
107    total += atoi(s);
108    // move over tab position
109    while ((*s != (char)0) && !isspace(*s))
110      s++;
111    if (last->alignment != align || last->position != total)
112      return FALSE;
113
114    last = last->next;
115  }
116  return TRUE;
117}
118
119/*
120 *  init - scans the string, s, and initializes the tab stops.
121 */
122
123void tabs::init (const char *s)
124{
125  char align;
126  int  total=0;
127  tab_position *last = NULL;
128
129  clear(); // remove any tab stops
130
131  // move over tag name
132  while ((*s != (char)0) && !isspace(*s))
133    s++;
134
135  while (*s != (char)0) {
136    // move over white space
137    while ((*s != (char)0) && isspace(*s))
138      s++;
139    // collect alignment
140    align = *s;
141    // move over alignment
142    s++;
143    // move over white space
144    while ((*s != (char)0) && isspace(*s))
145      s++;
146    // collect tab position
147    total = atoi(s);
148    // move over tab position
149    while ((*s != (char)0) && !isspace(*s))
150      s++;
151    if (last == NULL) {
152      tab = (tab_position *)malloc(sizeof(tab_position));
153      last = tab;
154    } else {
155      last->next = (tab_position *)malloc(sizeof(tab_position));
156      last = last->next;
157    }
158    last->alignment = align;
159    last->position = total;
160    last->next = NULL;
161  }
162}
163
164/*
165 *  find_tab - returns the tab number corresponding to the position, pos.
166 */
167
168int tabs::find_tab (int pos)
169{
170  tab_position *p;
171  int i=0;
172
173  for (p = tab; p != NULL; p = p->next) {
174    i++;
175    if (p->position == pos)
176      return i;
177  }
178  return 0;
179}
180
181/*
182 *  get_tab_pos - returns the, nth, tab position
183 */
184
185int tabs::get_tab_pos (int n)
186{
187  tab_position *p;
188
189  n--;
190  for (p = tab; (p != NULL) && (n>0); p = p->next) {
191    n--;
192    if (n == 0)
193      return p->position;
194  }
195  return 0;
196}
197
198char tabs::get_tab_align (int n)
199{
200  tab_position *p;
201
202  n--;
203  for (p = tab; (p != NULL) && (n>0); p = p->next) {
204    n--;
205    if (n == 0)
206      return p->alignment;
207  }
208  return 'L';
209}
210
211/*
212 *  dump_tab - display tab positions
213 */
214
215void tabs::dump_tabs (void)
216{
217  int i=1;
218  tab_position *p;
219
220  for (p = tab; p != NULL; p = p->next) {
221    printf("tab %d is %d\n", i, p->position);
222    i++;
223  }
224}
225
226/*
227 *  html_table - methods
228 */
229
230html_table::html_table (simple_output *op, int linelen)
231  : columns(NULL), out(op), linelength(linelen), last_col(NULL), start_space(FALSE)
232{
233  tab_stops = new tabs();
234}
235
236html_table::~html_table ()
237{
238  cols *c;
239  if (tab_stops != NULL)
240    delete tab_stops;
241
242  c = columns;
243  while (columns != NULL) {
244    columns = columns->next;
245    free(c);
246    c = columns;
247  }
248}
249
250/*
251 *  remove_cols - remove a list of columns as defined by, c.
252 */
253
254void html_table::remove_cols (cols *c)
255{
256  cols *p;
257
258  while (c != NULL) {
259    p = c;
260    c = c->next;
261    free(p);
262  }
263}
264
265/*
266 *  set_linelength - sets the line length value in this table.
267 *                   It also adds an extra blank column to the
268 *                   table should linelen exceed the last column.
269 */
270
271void html_table::set_linelength (int linelen)
272{
273  cols *p = NULL;
274  cols *c;
275  linelength = linelen;
276
277  for (c = columns; c != NULL; c = c->next) {
278    if (c->right > linelength) {
279      c->right = linelength;
280      remove_cols(c->next);
281      c->next = NULL;
282      return;
283    }
284    p = c;
285  }
286  if (p != NULL && p->right > 0)
287    add_column(p->no+1, p->right, linelength, 'L');
288}
289
290/*
291 *  get_effective_linelength -
292 */
293
294int html_table::get_effective_linelength (void)
295{
296  if (columns != NULL)
297    return linelength - columns->left;
298  else
299    return linelength;
300}
301
302/*
303 *  add_indent - adds the indent to a table.
304 */
305
306void html_table::add_indent (int indent)
307{
308  if (columns != NULL && columns->left > indent)
309    add_column(0, indent, columns->left, 'L');
310}
311
312/*
313 *  emit_table_header - emits the html header for this table.
314 */
315
316void html_table::emit_table_header (int space)
317{
318  if (columns == NULL)
319    return;
320
321  // dump_table();
322
323  last_col = NULL;
324  if (linelength > 0) {
325    int n = no_columns() + no_gaps();
326
327    out->nl();
328    out->nl();
329    if (space)
330      out->put_string("<p>");
331    start_space = space;
332    out->put_string("<table width=\"100%\" border=0 rules=\"none\" frame=\"void\"\n       cols=\"").put_number(n).put_string("\" cellspacing=\"0\" cellpadding=\"0\">").nl();
333    out->put_string("<tr valign=\"top\" align=\"left\">").nl();
334  }
335}
336
337/*
338 *  get_right - returns the right most position of this column.
339 */
340
341int html_table::get_right (cols *c)
342{
343  if (c != NULL && c->right > 0)
344    return c->right;
345  if (c->next != NULL)
346    return c->left;
347  return linelength;
348}
349
350/*
351 *  emit_col - moves onto column, n.
352 */
353
354void html_table::emit_col (int n)
355{
356  cols *c = columns;
357  cols *b = columns;
358  int   width = 0;
359
360  // must be a different row
361  if (last_col != NULL && n <= last_col->no)
362    emit_new_row();
363
364  while (c != NULL && c->no < n)
365    c = c->next;
366
367  // can we find column, n?
368  if (c != NULL && c->no == n) {
369    // shutdown previous column
370    if (last_col != NULL)
371      out->put_string("</td>").nl();
372
373    // find previous column
374    if (last_col == NULL)
375      b = columns;
376    else
377      b = last_col;
378
379    // have we a gap?
380    if (last_col != NULL) {
381      if (is_gap(b))
382	out->put_string("<td width=\"")
383	    .put_number(is_gap(b))
384	    .put_string("%\"></td>")
385	    .nl();
386      b = b->next;
387    }
388
389    // move across to column n
390    while (b != c) {
391      // we compute the difference after converting positions
392      // to avoid rounding errors
393      width = (get_right(b)*100 + get_effective_linelength()/2)
394		/ get_effective_linelength()
395	      - (b->left*100 + get_effective_linelength()/2)
396		  /get_effective_linelength();
397      if (width)
398	out->put_string("<td width=\"")
399	    .put_number(width)
400	    .put_string("%\"></td>")
401	    .nl();
402      // have we a gap?
403      if (is_gap(b))
404	out->put_string("<td width=\"")
405	    .put_number(is_gap(b))
406	    .put_string("%\"></td>")
407	    .nl();
408      b = b->next;
409    }
410    width = (get_right(b)*100 + get_effective_linelength()/2)
411	      / get_effective_linelength()
412	    - (b->left*100 + get_effective_linelength()/2)
413		/get_effective_linelength();
414    switch (b->alignment) {
415    case 'C':
416      out->put_string("<td width=\"")
417	  .put_number(width)
418	  .put_string("%\" align=center>")
419	  .nl();
420      break;
421    case 'R':
422      out->put_string("<td width=\"")
423	  .put_number(width)
424	  .put_string("%\" align=right>")
425	  .nl();
426      break;
427    default:
428      out->put_string("<td width=\"")
429	  .put_number(width)
430	  .put_string("%\">")
431	  .nl();
432    }
433    // remember column, b
434    last_col = b;
435  }
436}
437
438/*
439 *  finish_row -
440 */
441
442void html_table::finish_row (void)
443{
444  int n = 0;
445  cols *c;
446
447  if (last_col != NULL) {
448    for (c = last_col->next; c != NULL; c = c->next)
449      n = c->no;
450
451    if (n > 0)
452      emit_col(n);
453    out->put_string("</td>").nl();
454  }
455}
456
457/*
458 *  emit_new_row - move to the next row.
459 */
460
461void html_table::emit_new_row (void)
462{
463  finish_row();
464  out->put_string("<tr valign=\"top\" align=\"left\">").nl();
465  last_col = NULL;
466}
467
468void html_table::emit_finish_table (void)
469{
470  finish_row();
471  // out->put_string("linelength = ").put_number(linelength).nl();
472  out->put_string("</table>");
473  if (start_space)
474    out->put_string("</p>");
475  out->nl();
476}
477
478/*
479 *  add_column - adds a column. It returns FALSE if hstart..hend
480 *               crosses into a different columns.
481 */
482
483int html_table::add_column (int coln, int hstart, int hend, char align)
484{
485  cols *c = get_column(coln);
486
487  if (c == NULL)
488    return insert_column(coln, hstart, hend, align);
489  else
490    return modify_column(c, hstart, hend, align);
491}
492
493/*
494 *  get_column - returns the column, coln.
495 */
496
497cols *html_table::get_column (int coln)
498{
499  cols *c = columns;
500
501  while (c != NULL && coln != c->no)
502    c = c->next;
503
504  if (c != NULL && coln == c->no)
505    return c;
506  else
507    return NULL;
508}
509
510/*
511 *  insert_column - inserts a column, coln.
512 *                  It returns TRUE if it does not bump into
513 *                  another column.
514 */
515
516int html_table::insert_column (int coln, int hstart, int hend, char align)
517{
518  cols *c = columns;
519  cols *l = NULL;
520  cols *n = NULL;
521
522  while (c != NULL && c->no < coln) {
523    l = c;
524    c = c->next;
525  }
526  if ((l != NULL) && (hstart < l->right))
527    return FALSE;	// new column bumps into previous one
528
529  if ((l != NULL) && (l->next != NULL) &&
530      (l->next->left < hend))
531    return FALSE;  // new column bumps into next one
532
533  n = (cols *)malloc(sizeof(cols));
534  if (l == NULL) {
535    n->next = columns;
536    columns = n;
537  } else {
538    n->next = l->next;
539    l->next = n;
540  }
541  n->left = hstart;
542  n->right = hend;
543  n->no = coln;
544  n->alignment = align;
545  return TRUE;
546}
547
548/*
549 *  modify_column - given a column, c, modify the width to
550 *                  contain hstart..hend.
551 *                  It returns TRUE if it does not clash with
552 *                  the next or previous column.
553 */
554
555int html_table::modify_column (cols *c, int hstart, int hend, char align)
556{
557  cols *l = columns;
558
559  while (l != NULL && l->next != c)
560    l = l->next;
561
562  if ((l != NULL) && (hstart < l->right))
563    return FALSE;	// new column bumps into previous one
564
565  if ((c->next != NULL) && (c->next->left < hend))
566    return FALSE;  // new column bumps into next one
567
568  if (c->left > hstart)
569    c->left = hstart;
570
571  if (c->right < hend)
572    c->right = hend;
573
574  c->alignment = align;
575
576  return TRUE;
577}
578
579/*
580 *  find_tab_column - finds the column number for position, pos.
581 *                    It searches through the list tab stops.
582 */
583
584int html_table::find_tab_column (int pos)
585{
586  // remember the first column is reserved for untabbed glyphs
587  return tab_stops->find_tab(pos)+1;
588}
589
590/*
591 *  find_column - find the column number for position, pos.
592 *                It searches through the list of columns.
593 */
594
595int html_table::find_column (int pos)
596{
597  int   p=0;
598  cols *c;
599
600  for (c = columns; c != NULL; c = c->next) {
601    if (c->left > pos)
602      return p;
603    p = c->no;
604  }
605  return p;
606}
607
608/*
609 *  no_columns - returns the number of table columns (rather than tabs)
610 */
611
612int html_table::no_columns (void)
613{
614  int n=0;
615  cols *c;
616
617  for (c = columns; c != NULL; c = c->next)
618    n++;
619  return n;
620}
621
622/*
623 *  is_gap - returns the gap between column, c, and the next column.
624 */
625
626int html_table::is_gap (cols *c)
627{
628  if (c == NULL || c->right <= 0 || c->next == NULL)
629    return 0;
630  else
631    // we compute the difference after converting positions
632    // to avoid rounding errors
633    return (c->next->left*100 + get_effective_linelength()/2)
634	     / get_effective_linelength()
635	   - (c->right*100 + get_effective_linelength()/2)
636	       / get_effective_linelength();
637}
638
639/*
640 *  no_gaps - returns the number of table gaps between the columns
641 */
642
643int html_table::no_gaps (void)
644{
645  int n=0;
646  cols *c;
647
648  for (c = columns; c != NULL; c = c->next)
649    if (is_gap(c))
650      n++;
651  return n;
652}
653
654/*
655 *  get_tab_pos - returns the, nth, tab position
656 */
657
658int html_table::get_tab_pos (int n)
659{
660  return tab_stops->get_tab_pos(n);
661}
662
663char html_table::get_tab_align (int n)
664{
665  return tab_stops->get_tab_align(n);
666}
667
668
669void html_table::dump_table (void)
670{
671  if (columns != NULL) {
672    cols *c;
673    for (c = columns; c != NULL; c = c->next) {
674      printf("column %d  %d..%d  %c\n", c->no, c->left, c->right, c->alignment);
675    }
676  } else
677    tab_stops->dump_tabs();
678}
679
680/*
681 *  html_indent - creates an indent with indentation, ind, given
682 *                a line length of linelength.
683 */
684
685html_indent::html_indent (simple_output *op, int ind, int pageoffset, int linelength)
686{
687  table = new html_table(op, linelength);
688
689  table->add_column(1, ind+pageoffset, linelength, 'L');
690  table->add_indent(pageoffset);
691  in = ind;
692  pg = pageoffset;
693  ll = linelength;
694  is_used = FALSE;
695}
696
697html_indent::~html_indent (void)
698{
699  end();
700  delete table;
701}
702
703void html_indent::begin (int space)
704{
705  if (! is_used) {
706    table->emit_table_header(space);
707    table->emit_col(1);
708    is_used = TRUE;
709  }
710}
711
712void html_indent::end (void)
713{
714  if (is_used)
715    table->emit_finish_table();
716  is_used = FALSE;
717}
718
719/*
720 *  get_reg - collects the registers as supplied during initialization.
721 */
722
723void html_indent::get_reg (int *ind, int *pageoffset, int *linelength)
724{
725  *ind = in;
726  *pageoffset = pg;
727  *linelength = ll;
728}
729