dtinfo subtree dtinfo
[oweals/cde.git] / cde / programs / dtinfo / dtinfo / src / OnlineRender / CanvasRenderer.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 // $XConsortium: CanvasRenderer.C /main/61 1996/12/03 18:21:46 rcs $
24 /*      Copyright (c) 1994,1995,1996 FUJITSU LIMITED    */
25 /*      All Rights Reserved                             */
26
27 #define C_TOC_Element
28 #define C_NodeViewInfo
29 #define L_Basic
30
31 #define C_GraphicsMgr
32 #define C_PrefMgr
33 #define L_Managers
34
35 #define C_WindowSystem
36 #define L_Other
37
38 #define C_PixmapGraphic
39 #define L_Graphics
40
41 #define C_NamedData
42 #define C_OString
43 #define C_NodeViewInfo
44 #define L_Basic 
45 #include <Prelude.h>
46
47 #include "Managers/WString.hh"
48
49 #include <utility/funcs.h>
50
51 #include "Element.h"
52 #include "Attribute.h"
53 #include "CanvasRenderer.hh"
54 #include "Feature.h"
55 #include "FeatureValue.h"
56
57 // stylesheet spec states default leading to be 2
58 #define DEFAULT_CONTAINER_LEADING 2
59
60 #if defined(UseWideChars) && defined(sun) && !defined(SVR4)
61 #define mbstowcs(a,b,c) Xmbstowcs(a,b,c)
62 #define wcstombs(a,b,c) Xwcstombs(a,b,c)
63 #endif
64
65 #include <DtI/LinkMgrP.h>
66
67 #undef self
68 #include <DtI/DisplayAreaP.h>
69 #include <DtI/FontI.h>
70 #include <DtI/FontAttrI.h>
71 #include <DtI/RegionI.h>
72
73 // information to be passed back 
74 NodeViewInfo *gNodeViewInfo ;
75
76 // avoid HardCopy hack
77 Renderer *gRenderer = 0 ;
78
79 DtHelpDispAreaStruct *gHelpDisplayArea = 0;
80
81 struct s_entry
82 {
83   unsigned int  index;
84   const char   *name ;
85 };
86
87 static
88 struct s_entry symbols[] = 
89 {
90   GRAPHIC_attr, "GRAPHIC",
91   INLGRAPHIC,   "INLGRAPHIC",
92   ISMAP,        "ISMAP",
93   OLID,         "OL-ID",
94   OLIDREF,      "OL-IDREF",
95   REMOTE,       "REMOTE",
96   TABLE,        "TABLE",
97   TERMS,        "TERMS",
98   ALIGN,        "ALIGN",
99   BOTTOM,       "BOTTOM",
100   LINEBREAK,    "LINEBREAK",
101   CHARSETS,     "CHARSET",
102   SET,          "SET",
103   BGCOLOR,      "BGCOLOR",
104   FGCOLOR,      "FGCOLOR",
105   FONT,         "FONT",
106   FONTCATALOG,  "FONTCATALOG",
107   FONTFAMILY,   "FONTFAMILY",
108   HIGHLIGHT,    "HIGHLIGHT",
109   IGNORE,       "IGNORE",
110   LEFT,         "LEFT",
111   MARGIN,       "MARGIN",
112   SUFFIX,       "SUFFIX",
113   PREFIX,       "PREFIX",
114   PREVIEW,      "PREVIEW",
115   RIGHT,        "RIGHT",
116   TABSTOPS,     "TABSTOP",
117   CONTENT,      "CONTENT",
118   TOP,          "TOP",
119   OVERLINE,     "OVERLINE",
120   STRIKETHROUGH,"STRIKETHROUGH",
121   UNDERLINE,    "UNDERLINE",
122   WRAP,         "WRAP",
123   XLFD,         "XLFD",
124   GRAPHIC_feature,"GRAPHIC",
125   FAMILY,       "FAMILY",
126   WEIGHT,       "WEIGHT",
127   SLANT,        "SLANT",
128   SIZE,         "SIZE",
129   CHARSET,      "CHARSET",
130   REVERSEVIDEO, "REVERSE-VIDEO",
131   AT,           "AT",
132   EVERY,        "EVERY",
133   POSITION,     "POSITION",
134   HORIZ,        "HORIZ",
135   VERT,         "VERT",
136   LAYOUT,       "LAYOUT",
137   ASPACE,       "ASPACE",
138   BSPACE,       "BSPACE",
139   LEADING,      "LEADING",
140   FINDENT,      "FINDENT",
141   LINDENT,      "LINDENT",
142   RINDENT,      "RINDENT",
143   FLOW,         "FLOW",
144   JUSTIFY,      "JUSTIFY",
145   VJUSTIFY,     "VJUSTIFY",
146   BORDER,       "BORDER",
147   THICKNESS,    "THICKNESS",
148   ROW,          "ROW",
149   COLS,         "COLS",
150   COLFORMAT,    "COLFORMAT",
151   CHARALIGN,    "CHARALIGN",
152   SPANCOLS,     "SPANCOLS",
153   MOREROWS,     "MOREROWS",
154   CELL,         "CELL",
155   WIDTH,        "WIDTH",
156   FALLBACK,     "FALLBACK",
157   FOUNDRY,      "FOUNDRY",
158   NAME,         "NAME",
159   DISPLAY,      "DISPLAY",
160   COLREF,       "COLREF",
161   SUBSUPER,     "POSITION",
162   COLSEP,       "COLSEP",
163   ROWSEP,       "ROWSEP",
164   TGROUP,       "TGROUP",
165   FRAME,        "FRAME",
166   MEDIA,        "MEDIUM",
167   PAGEBREAK,    "PAGEBREAK",
168   FOOTERS,      "FOOTERS",
169   HEADERS,      "HEADERS",
170   ORIENTATION,  "ORIENTATION"
171 };
172 // supported features
173
174 // these are last resorts, hard-coded
175 const char* const CanvasRenderer::f_hcf_sans = "sans";
176 const char* const CanvasRenderer::f_hcf_serif = "*";
177 const char* const CanvasRenderer::f_hcf_mono = "*";
178 const char* const CanvasRenderer::f_hcf_symbol = "*";
179
180 const char* const CanvasRenderer::f_hcf_weight = "*";
181 const char* const CanvasRenderer::f_hcf_slant = "*";
182 const char* const CanvasRenderer::f_hcf_fallback = "*";
183 const int CanvasRenderer::f_hcf_size = -1;
184
185 static _DtCvSegment *insert_break (_DtCvSegment *container,
186                                    unsigned long type);
187 static _DtCvSegment *new_segment (unsigned long type);
188 static void insert_segment (_DtCvSegment *container, _DtCvSegment *segment);
189
190 #ifdef CONTAINER_DEBUG
191 static
192 const char *
193 print_justify (unsigned value)
194 {
195   const char *rvalue ;
196   switch (value)
197     {
198     case _DtCvJUSTIFY_LEFT:
199       rvalue =  "_DtCvJUSTIFY_LEFT" ;
200       break ;
201     case _DtCvJUSTIFY_RIGHT:
202       rvalue =  "_DtCvJUSTIFY_RIGHT" ;
203       break ;
204     case _DtCvJUSTIFY_CENTER:
205       rvalue =  "_DtCvJUSTIFY_CENTER" ;
206       break ;
207     case _DtCvJUSTIFY_TOP:
208       rvalue =  "_DtCvJUSTIFY_TOP" ;
209       break ;
210     case _DtCvJUSTIFY_BOTTOM:
211       rvalue =  "_DtCvJUSTIFY_BOTTOM" ;
212       break ;
213     case _DtCvJUSTIFY_LEFT_CORNER:
214       rvalue =  "_DtCvJUSTIFY_LEFT_CORNER" ;
215       break ;
216     case _DtCvJUSTIFY_LEFT_MARGIN:
217       rvalue =  "_DtCvJUSTIFY_MARGIN" ;
218       break ;
219     case _DtCvJUSTIFY_RIGHT_CORNER:
220       rvalue =  "_DtCvJUSTIFY_RIGHT_CORNER" ;
221       break ;
222     case _DtCvJUSTIFY_RIGHT_MARGIN:
223       rvalue =  "_DtCvJUSTIFY_RIGHT_MARGIN" ;
224       break ;
225     default: 
226       rvalue = "Unknown" ;
227     }
228   return rvalue ;
229 }
230 #endif
231
232 CanvasRenderer::CanvasRenderer(int font_scale)
233 : Renderer(),
234   fBogusSymbol(gElemSymTab->intern("%BOGUS")),
235   f_font_scale(font_scale),
236   f_font (0),
237   f_link_idx (-1),
238   f_current_tgroup (0),
239   f_level(0)
240 {
241   // make symbols 
242   for ( int i=0; i < REND_SYMBOLS; i++)
243     f_symbols[symbols[i].index] = new
244       Symbol(gSymTab->intern(symbols[i].name)); 
245
246   f_sans    = window_system().get_string_default("FontSans");
247   f_serif   = window_system().get_string_default("FontSerif");
248   f_mono    = window_system().get_string_default("FontMono");
249   f_symbol  = window_system().get_string_default("FontSymbol");
250   if (! (f_sans   && *f_sans))   f_sans   = f_hcf_sans;
251   if (! (f_serif  && *f_serif))  f_serif  = f_hcf_serif;
252   if (! (f_mono   && *f_mono))   f_mono   = f_hcf_mono;
253   if (! (f_symbol && *f_symbol)) f_symbol = f_hcf_symbol;
254
255   // determine default border width
256   f_border_width = window_system().get_int_default("BorderWidth");
257   if (! f_border_width)
258     f_border_width = 2;
259 }
260
261 CanvasRenderer::~CanvasRenderer()
262 {
263   for (int i = 0 ; i < REND_SYMBOLS; i++)
264     delete f_symbols[i] ;
265 }
266
267 // virtuals
268
269 FeatureSet *
270 CanvasRenderer::initialize()
271 {
272   f_default_features = new FeatureSet ;
273
274   FeatureSet *font_set = new FeatureSet ;
275
276   font_set->add(new Feature(*f_symbols[WEIGHT], new FeatureValueString(f_hcf_weight)));
277   font_set->add(new Feature(*f_symbols[SLANT], new FeatureValueString(f_hcf_slant)));
278   font_set->add(new Feature(*f_symbols[SIZE], new FeatureValueInt(f_hcf_size)));
279   font_set->add (new Feature (*f_symbols[FALLBACK], new FeatureValueString (f_hcf_fallback)));
280
281   f_default_features->add(new Feature(*f_symbols[FONT], new FeatureValueFeatureSet(font_set)));
282
283   // default leading
284   FeatureSet* layout = new FeatureSet;
285
286   layout->add(new Feature(*f_symbols[LEADING],
287               new FeatureValueInt(DEFAULT_CONTAINER_LEADING)));
288
289   f_default_features->add(new Feature(*f_symbols[LAYOUT],
290                           new FeatureValueFeatureSet(layout)));
291
292 #ifdef JBM
293   // other default features
294   FeatureSet *margin = new FeatureSet;
295   margin->add (new Feature(*f_symbols[LEFT], new FeatureValueInt (20)));
296   f_default_features->add(new Feature(*f_symbols[MARGIN],
297                                       new FeatureValueFeatureSet(margin)));
298 #endif
299 #ifdef JBM
300   f_default_features->add(new Feature(*f_symbols[WRAP],
301                                       new FeatureValueString("word")));
302   f_default_features->add(new Feature(*f_symbols[BREAK],
303                                       new FeatureValueString("line")));
304 #endif
305   return f_default_features;
306 }
307
308 void
309 CanvasRenderer::Begin()
310 {
311   // pre document initialization...called before document is processed 
312   _DtCvTopicInfo        *topic = new _DtCvTopicInfo ;
313
314   topic->link_data = _DtLinkDbCreate() ;
315   topic->seg_list  = new_segment(_DtCvCONTAINER);
316   topic->id_str = NULL ;
317   topic->mark_list = NULL ;
318   
319
320   gNodeViewInfo->topic(topic); // NOTE: was after do_features...
321
322   f_current_container = topic->seg_list ;
323   f_current_displayable = 0 ;
324   f_link_idx = -1 ;
325
326   // set up initial parameters 
327   ElementFeatures features(this) ;
328
329   do_features(features, *f_default_features, *f_default_features,
330               f_symbols); 
331
332   f_leading_stack.push(features.layout().leading());
333   f_leading_level_stack.push(f_level);
334
335   setup_container(f_current_container, features);
336
337   /*
338      have to apply the features here
339    */
340
341   f_vcc = 0 ;
342
343   f_stack.push (f_current_container);
344 }
345
346 void
347 CanvasRenderer::End()
348 {
349   // post processing...called after document ends 
350 #ifdef HIERARCHY_DEBUG
351   void print_hierarchy(_DtCvTopicInfo*);
352   print_hierarchy(gNodeViewInfo->topic());
353 #endif
354
355   while (f_leading_level_stack.entries())
356     f_leading_level_stack.pop();
357   while (f_leading_stack.entries())
358     f_leading_stack.pop();
359 }
360
361 static unsigned is_literal = 0 ;
362
363 // returns non-zero value if element is to be ignored 
364 unsigned int
365 CanvasRenderer::BeginElement(const Element              &element,
366                              const FeatureSet   &local,
367                              const FeatureSet   &complete,
368                              const FeatureSet   &parentComplete)
369 {
370   // at this point, f_current_container contains the 
371   // parent of the element we are about to process
372
373 #ifdef BEGIN_ELEMENT_DEBUG
374   cerr << '<' << element.gi() << '>' << endl;
375 #endif
376
377   assert(f_current_container->type == _DtCvCONTAINER);
378
379   // have to do some VCC counting here
380
381
382 #ifdef FEATURE_DEBUG
383   cerr << element.gi() << ": \t" << local << endl;
384   cerr << element.gi() << "=>\t" << complete << endl;
385 #endif
386
387   // now process styles 
388
389   f_fstack.push (f_font);
390   f_stack.push(f_current_container);
391   f_link_stack.push (f_link_idx);
392   f_tgroup_stack.push (f_current_tgroup);
393
394 #ifdef OL_LINK_DEBUG
395   cerr << "pushed link_index: " << f_link_idx << endl;
396 #endif
397
398 #ifdef DEBUG_USE_NAMES
399   _DtCvSegment *hold_segment = f_current_container ;
400 #endif
401
402   ElementFeatures features(this) ;
403
404   do_features(features, local, complete, f_symbols); 
405
406   if (features.ignore()) {
407     if (f_tgroup_stack.entries())   f_tgroup_stack.pop();
408     if (f_link_stack.entries())     f_link_stack.pop();
409     if (f_stack.entries())          f_stack.pop();
410     if (f_subsuper_stack.entries()) f_subsuper_stack.pop();
411
412     return features.ignore();
413   }
414
415   ++f_level;
416
417   if (features.ignore_linebreak(False)) {
418     // disable linebreak features beforehand here if linebreak
419     // should be ignored for this element
420
421     if (features.prefix().linebreak() & LINEBREAK_BEFORE)
422       features.prefix().linebreak(
423                 features.prefix().linebreak() & ~LINEBREAK_BEFORE);
424
425     if (features.suffix().linebreak() & LINEBREAK_AFTER)
426       features.suffix().linebreak(
427                 features.suffix().linebreak() & ~LINEBREAK_AFTER);
428
429     features.linebreak(LINEBREAK_NONE);
430   }
431   
432   {
433     SegClientData scd(_DtCvSTRING);
434
435     scd.hilite_type(features.highlight().overline() |
436                     features.highlight().strikethrough() |
437                     features.highlight().underline());
438     UAS_String& bg_color(features.highlight().bg_color());
439     UAS_String& fg_color(features.highlight().fg_color());
440     if ((char*)bg_color && *(char*)bg_color)
441       scd.bg_color(strdup((char*)bg_color));
442     if ((char*)fg_color && *(char*)fg_color)
443       scd.fg_color(strdup((char*)fg_color));
444
445     f_scd_stack.push(new SegClientData(scd));
446   }
447
448   f_subsuper_stack.push(features.subsuper());
449
450   if (features.table() == NULL && f_table_stack.entries()) {
451     // semantics: f_table_stack.top should always be the current.
452     //            need special care on the other end as well.
453     f_table_stack.push(f_table_stack.top());
454   }
455   else
456     f_table_stack.push(features.table());
457
458   if (features.layout().has_layout(False) &&
459       current_leading() != features.layout().leading())
460   {
461     f_leading_stack.push(features.layout().leading());
462     f_leading_level_stack.push(f_level);
463   }
464
465   // get info on special olias stuff like links etc
466   handle_olias_attributes(features, element,
467                           local, complete, parentComplete); 
468
469   // if we are not already in a link, see if we start one
470   if (f_link_idx == -1)
471     {
472       f_link_idx = features.link_idx();
473     }
474
475   if (features.tgroup())
476     {
477       _DtCvSegment *tableseg = new_segment(_DtCvTABLE);
478
479       // NOTE: have to delete this guy when he pops out the other end !
480       f_current_tgroup = features.tgroup();
481       f_current_tgroup->set_segment(tableseg);
482
483       // associate tgroup with the surrounding table 
484       f_current_tgroup->table(f_table_stack.top());
485
486       if (f_current_tgroup->table() &&
487           f_current_tgroup->table()->frame() != TableDefn::table_frame_none)
488         {
489           _DtCvSegment* former_current_container = f_current_container;
490
491           // NOTE: the purpose of the new container is just for
492           //       drawing a border around a tgroup
493           f_current_container = create_container(f_current_container);
494
495           setup_cascaded_tgroup_container(former_current_container,
496                                           f_current_container);
497         }
498
499       insert_segment(f_current_container, tableseg);
500
501     }
502
503   if (features.col_format()) {
504 #ifdef COLSPEC_DEBUG
505     {
506         ColFormat *colf = features.col_format();
507         fprintf(stderr, "(DEBUG) ColFormat justify=%d, cols=%d, name=\"%s\", width=%d\n",
508                                 colf->justify(), colf->cols(), colf->name(), colf->width());
509     }
510 #endif
511     f_current_tgroup->add (features.col_format());
512   }
513
514   if (features.row()) {
515     f_current_tgroup->add_row(features.row());
516   }
517
518   if (features.requires_container(False) ||
519       (features.prefix().text() && features.prefix().requires_container(True)) ||
520       (features.suffix().text() && features.suffix().requires_container(True)))
521     {
522       
523       if (features.cell().has_cell())
524         {
525           f_current_container = create_container (NULL);
526
527           ColDefn* coldef = new ColDefn(f_current_container, NULL,
528                                         features.cell().spanrows(),
529                                         features.cell().colref(),
530                                         features.cell().colstart(),
531                                         features.cell().colend(),
532                                         features.cell().justify(),
533                                         features.cell().vjustify(),
534                                         features.cell().colsep(),
535                                         features.cell().rowsep(),
536                                         features.cell().char_align()); 
537
538           f_current_tgroup->add(coldef);
539         }
540       else
541         {
542           f_current_container = create_container(f_current_container);
543           setup_container (f_current_container, features);
544         }
545       
546
547 #ifdef CONTAINER_DEBUG
548       cerr << "Container: " << 
549         "\tjustify:  " << print_justify(f_current_container->handle.container.justify) << 
550           "\tvjustify: " << print_justify(f_current_container->handle.container.vjustify) << 
551           "\torient:   " << print_justify(f_current_container->handle.container.orient) << 
552           "\tvorient:   " << print_justify(f_current_container->handle.container.vorient) << 
553           "\tflow:     " << f_current_container->handle.container.flow << 
554             endl;
555 #endif
556
557       f_current_container->handle.container.id = features.locator();
558
559       if (f_link_idx != -1)
560         {
561           f_current_container->link_idx = f_link_idx ;
562           f_current_container->type |= _DtCvHYPER_TEXT ;
563         }
564
565     }
566   else
567     {
568       // may have to build a marker segment
569       if (features.locator())
570         {
571           _DtCvSegment *marker = new_segment (_DtCvMARKER);
572           marker->handle.marker = features.locator();
573           insert_segment (f_current_container, marker);
574         }
575
576         const char *font = features.font();
577         if (font && *font)
578           f_font = features.font();
579     }
580
581   // NOTE: pagebreak implicitly adds a linebreak before the pagebreak.
582   if (features.pagebreak() & PAGEBREAK_BEFORE) {
583     insert_break (f_current_container, _DtCvNEW_LINE);
584     insert_break (f_current_container, _DtCvPAGE_BREAK);
585   }
586   else if (features.linebreak() & LINEBREAK_BEFORE) {
587     insert_break (f_current_container, _DtCvNEW_LINE);
588   }
589
590   // check for prefix text, because if there is no text, then we are
591   // not really doing a prefix, and we just ignore it...possible
592   // exception, if an <HR> is required as a prefix item
593
594   if (features.prefix().text())
595     {
596       _DtCvSegment *container = f_current_container ;
597
598       if (features.prefix().requires_container(True))
599         {
600           const char *font = f_font ;
601           container = create_container (f_current_container);
602           setup_container (container, features.prefix(), True);
603           f_font = font ;
604 #ifdef CONTAINER_DEBUG
605       cerr << "Prefix Container: " << 
606         "\tjustify:  " << f_current_container->handle.container.justify << 
607           "\tvjustify: " << f_current_container->handle.container.vjustify << 
608           "\torient:   " << f_current_container->handle.container.orient << 
609           "\tvorient:   " << f_current_container->handle.container.vorient << 
610           "\tflow:     " << f_current_container->handle.container.flow << 
611             endl;
612 #endif
613           // create a container for the element content
614           f_current_container = create_container(f_current_container);
615         }
616
617       if (features.prefix().linebreak() & LINEBREAK_BEFORE)
618         insert_break (container, _DtCvNEW_LINE);
619
620       const char *font = features.prefix().font() ;
621       if (font == NULL || *font == '\0')
622         font = f_font ;
623
624
625       _DtCvSegment *segment = insert_string (container,
626                                              font,
627                                              features.prefix().text(),
628                                              strlen(features.prefix().text()),
629                                              False) ;
630
631       // indicate that this is added information 
632       segment->type |= _DtCvAPP_FLAG1 ;
633
634       if (features.prefix().requires_container(True))
635         f_current_displayable = 0;
636
637       if (features.prefix().linebreak() & LINEBREAK_AFTER) {
638         if (! features.prefix().ignore_linebreak(True))
639           segment->type |= _DtCvNEW_LINE ;
640       }
641     }
642     else if (features.prefix().linebreak() & LINEBREAK_AFTER) {
643       if (! features.prefix().ignore_linebreak(True))
644         insert_break(f_current_container, _DtCvNEW_LINE);
645     }
646
647   // do graphics handling
648   if (features.graphic())
649     {
650       _DtCvSegment *grseg = features.graphic();
651       insert_segment(f_current_container, grseg);
652       grseg->link_idx = features.link_idx();
653
654       if (f_current_displayable)
655         f_current_displayable->next_disp = grseg;
656       f_current_displayable = grseg;
657
658 #if 0
659       // I have no idea what the next 2 lines are intended for. 
660       // Why should prefix's linebreak take effect on the element's
661       // flow-break? --- kamiya@chistech.com
662       if (features.prefix().linebreak() & LINEBREAK_AFTER)
663         grseg->type |= _DtCvNEW_LINE ;
664 #endif
665     }
666
667   PartialElementFeatures *suffixFeatures = 0;
668   if (features.suffix().text())
669     {
670       // have to stack this guy and add it to the data during
671       // EndElement 
672       suffixFeatures = new PartialElementFeatures(features.suffix());
673     }
674   if ((features.linebreak() & LINEBREAK_AFTER) ||
675       (features.pagebreak() & PAGEBREAK_AFTER))
676     {
677       // Add linebreak/pagebreak info to suffix; create empty suffix
678       // if necessary.
679       if (suffixFeatures == 0)
680         suffixFeatures = new PartialElementFeatures(this);
681
682       // if our element had a linebreak after, then include it in the suffix
683       // while preserving potential suffix linebreak settings.
684       if (features.linebreak() & LINEBREAK_AFTER)
685         suffixFeatures->linebreak(suffixFeatures->linebreak() |
686                                   LINEBREAK_AFTER);
687
688       // if our element had a pagebreak after, then include it in the suffix;
689       // suffixes cannot have pagebreaks, so don't worry about preserving.
690       if (features.pagebreak() & PAGEBREAK_AFTER)
691         suffixFeatures->pagebreak(PAGEBREAK_AFTER);
692     }
693   f_suffixes.push (suffixFeatures);
694
695   is_literal = f_current_container->handle.container.type == _DtCvLITERAL;
696
697   if (features.xref()) {
698
699 #if 0
700     if (g_xref_subsection && window_system().dtinfo_font()) {
701
702       extern UAS_String f_dtinfo_font_name;
703
704       UAS_String swapped_out = f_font;
705       f_font = f_dtinfo_font_name;
706
707       char buf[2];
708       buf[0] = (char)0x4c;
709       buf[1] = (char)0;
710
711       _data(buf, 2, False);
712
713       f_font = swapped_out;
714     }
715 #endif
716
717     _data(features.xref(), strlen(features.xref()), False);
718   }
719
720   return 0 ;                    // not do not ignore content
721 }
722
723 void
724 CanvasRenderer::EndElement(const Symbol& /*name*/)
725 {
726 #ifdef END_ELEMENT_DEBUG
727   cerr << "</" << name << '>' << endl;
728 #endif
729
730   // take care of suffix stuff here
731   PartialElementFeatures *suffix = f_suffixes.pop();
732
733   if (suffix)
734     {
735       if (suffix->text())
736         {
737           _DtCvSegment *container = f_current_container ;
738
739           if (suffix->requires_container(True))
740             {
741               container = create_container (f_current_container);
742               setup_container (container, *suffix, True);
743             }
744           else
745             {
746               if (! suffix->ignore_linebreak(True)) {
747                 if (suffix->linebreak() & LINEBREAK_BEFORE) {
748                   insert_break (container, _DtCvNEW_LINE);
749                 }
750               }
751             }
752           const char *font = suffix->font() ;
753           if (font == NULL || *font == '\0')
754             font = f_font ;
755
756           _DtCvSegment *segment = insert_string (container,
757                                                  font,
758                                                  suffix->text(),
759                                                  strlen(suffix->text()),
760                                                  False) ;
761
762           // indicate that this is added information
763           segment->type |= _DtCvAPP_FLAG1 ;
764
765           if (suffix->linebreak() & LINEBREAK_AFTER) {
766             segment->type |= _DtCvNEW_LINE ;
767           }
768
769           if (suffix->requires_container(True))
770             f_current_displayable = 0;
771         }
772       else if (suffix->linebreak() & LINEBREAK_AFTER)
773         {
774           // Element had a LINEBREAK_AFTER
775
776           if (f_current_displayable != 0)
777             {
778               f_current_displayable->type |= _DtCvNEW_LINE ;
779             }
780           else
781             {
782               insert_break (f_current_container, _DtCvNEW_LINE);
783             }
784         }
785
786       if (suffix->pagebreak() & PAGEBREAK_AFTER) {
787         // Put page break after suffix segment.
788         // NOTE: insert linebreak first if not already added.
789         if (!(suffix->linebreak() & LINEBREAK_AFTER))
790           insert_break (f_current_container, _DtCvNEW_LINE);
791         insert_break (f_current_container, _DtCvPAGE_BREAK);
792       }
793       delete suffix ;
794     }
795
796   // pop the stacks, returning state to that of current element 
797   f_font = f_fstack.pop();      
798   f_current_container = f_stack.pop ();
799   f_link_idx = f_link_stack.pop();
800   if (f_scd_stack.top())
801     delete f_scd_stack.pop();
802   if (f_subsuper_stack.entries())
803     f_subsuper_stack.pop();
804
805   // pop the table stack 
806   // if the table that we pop is not the one we are working on, then
807   // we have finished with the current table, and can build it then
808   // dispose of the memory used
809
810   TGDefn *next_tgroup = f_tgroup_stack.pop();
811
812   if (f_current_tgroup != next_tgroup)
813     {
814       // convert the data we have accumulated into the _DtCvTable
815       // segment that represents this table
816       f_current_tgroup->build();        
817       delete f_current_tgroup ;
818     }
819   f_current_tgroup = next_tgroup ;
820
821   if (f_table_stack.entries()) {
822     TableDefn* table = f_table_stack.pop();
823     if (table) {
824       if (f_table_stack.entries() == 0 || table != f_table_stack.top())
825         delete table;
826     }
827   }
828
829   if (f_leading_stack.entries() && f_leading_level_stack.entries())
830   {
831     if (f_leading_level_stack.top() == f_level)
832     {
833         f_leading_stack.pop();
834         f_leading_level_stack.pop();
835     }
836   }
837
838   --f_level;
839 }
840
841 void
842 CanvasRenderer::data(const char *data, unsigned int size)
843 {
844   _data(data, size, True);
845 }
846
847 void
848 CanvasRenderer::_data(const char *data, unsigned int size, Boolean count_vcc)
849 {
850   _DtCvSegment *segment ;
851
852   if (is_literal)
853     {
854 #ifdef LITERAL_DEBUG
855       cerr << "literal: " << data << endl;
856 #endif
857       segment = insert_literal(f_current_container, f_font, data, size);
858     }
859   else
860     {
861       segment = insert_string (f_current_container,
862                                f_font, data, size, count_vcc);
863     }
864
865   if (f_link_idx != -1)
866     {
867       segment->link_idx = f_link_idx ;
868       segment->type |= _DtCvHYPER_TEXT ;
869 #ifdef OL_LINK_DEBUG
870       cerr << "data:( " << f_link_idx << ") " << data << endl;
871 #endif
872     }
873 }
874
875 void
876 CanvasRenderer::handle_olias_attributes(ElementFeatures  &features,
877                                         const Element   &element,
878                                         const FeatureSet        & /* local */,
879                                         const FeatureSet        &complete,
880                                         const FeatureSet        & /*parentComplete*/
881                                         )
882 {
883   // check for specific olias attributes
884   
885   // NOTE: more efficient to iterate over attributes than to check each
886   // attribute on every element (when I have time) - jbm  
887
888   const Attribute *olxref =
889                 element.get_olias_attribute(gSymTab->intern("OL-XREF"));
890   if (olxref) {
891     UAS_String locator = olxref->value();
892
893     UAS_Pointer<UAS_Common> target =
894                 gNodeViewInfo->node_ptr()->create_relative(locator);
895
896     if (target) {
897       extern char* g_mmdb_section_label;
898
899       UAS_String title;
900       if (g_mmdb_section_label && *g_mmdb_section_label)
901         title = g_mmdb_section_label;
902       else
903         title = target->title();
904
905       if ((char*)title && *(char*)title)
906         features.xref(strdup((char*)title));
907
908 #ifdef XREF_DEBUG
909     cerr << '<' << element.gi() << " xref=" << (char*)locator << '>'
910          << " target=" << (char*) (char*)target->id() << endl;
911 #endif
912     }
913   }
914   
915   /* -------- Locators -------- */
916   const Attribute *locator = element.get_olias_attribute(*f_symbols[OLID]);
917   if (locator)
918     {
919 #ifdef LOCATOR_DEBUG
920       cerr << '<' << element.gi() << " locator=" << locator->value() << '>' << endl;
921 #endif
922       const char *locator_value = locator->value();
923       
924       // Need to place the locator value as an ID in the Container
925       features.locator (strdup (locator_value));
926
927     }
928   
929   
930   /* -------- Links -------- */
931   const Attribute *olidref = element.get_olias_attribute(*f_symbols[OLIDREF]);
932   if (olidref)
933     {
934       // enter this segment into the link db
935
936       UAS_String locator = (char*)olidref->value();
937
938       if ((char*)locator && *(char*)locator && gNodeViewInfo) {
939
940         // We should not really use gNodeViewInfo. CanvasRenderer should
941         // a member of type UAS_Pointer<UAS_Common>
942
943         UAS_Pointer<UAS_Common> target =
944                 gNodeViewInfo->node_ptr()->create_relative(locator);
945
946         if (target) // create link only when resolved
947         {
948           // hack - currently, a hint of 0 is a hypertext link and a hint
949           // of 1 is a graphic. this should really be co-ordinated with
950           // the constants used by the Canvas
951
952           features.link_idx (
953            _DtLinkDbAddLink(
954                 gNodeViewInfo->topic()->link_data,               // link db
955                                         0,                       // id
956                                         (char *)(olidref->value()), // spec
957                                         _DtCvLinkType_AppDefine, // type
958                                         0,                       // hint
959                                         0));                     // description
960         }
961       }
962       
963 #ifdef OL_LINK_DEBUG
964       cerr << "idref: " << olidref->value() << " --> " << features.link_idx() << endl;
965 #endif
966
967       /* -------- see if it is remote -------- */
968       
969       // Can we mark this anymore?
970 #ifdef NotDefined
971       
972       const Attribute *remote =
973         element.get_olias_attribute(*f_symbols[REMOTE]);
974       if (remote)
975         {
976           // keep track of it somehow
977           // remote is only used to check for valid links at initial display
978           // time and is not used at actual link following time 
979           ON_DEBUG(cerr << "remote link: " << b_node->target() << endl);
980           b_node->remote_target(true);
981         }
982 #ifdef DEBUG_LINK
983       cerr << "link (" << b_node->target() << ") placed on: " << (void*)b_node
984         << endl ;
985 #endif
986 #endif      
987     }
988   
989   /* -------- Graphics -------- */
990   
991   const Attribute *grattr =
992     element.get_olias_attribute(*f_symbols[GRAPHIC_attr]);
993   
994   if (grattr)
995     {
996       const Feature *graphic_display_type =
997         complete.deep_lookup("GRAPHIC", "ALIGN", 0);
998
999 #ifdef GRAPHIC_DEBUG
1000       cerr << "GRAPHIC" << endl;
1001
1002       cerr << "Graphic( " << element.gi() << "): (" ;
1003       if (graphic_display_type)
1004         cerr << *graphic_display_type ;
1005       else
1006         cerr << "nil" ;
1007       cerr << ")" << endl;
1008
1009       cerr << "graphic id: " << grattr->value() << endl;
1010 #endif
1011
1012
1013       const char *graphicid = grattr->value();
1014       UAS_Pointer<Graphic> gr(graphics_mgr().get(gNodeViewInfo->node_ptr(), graphicid));
1015       PixmapGraphic *graphic = gr->graphic();
1016
1017       gNodeViewInfo->add_graphic (gr); // preserve the Graphic and Pixmap objects
1018
1019       // inform the PixmapGraphic that we are going to keep its pixmap
1020       //graphic->set_type (PixmapGraphic::PRESERVE);
1021
1022       // Set up DtHelp graphic representation
1023       DtHelpGraphicStruct       *pGS ;
1024       _DtHelpDARegion           *pReg ;
1025
1026       pGS = new DtHelpGraphicStruct ;
1027       pReg = new _DtHelpDARegion ;
1028
1029       pGS->pix = graphic->pixmap();
1030       pGS->width = graphic->width() ;
1031       pGS->height = graphic->height();
1032       pGS->mask = 0 ;
1033       pGS->pixels = NULL ;
1034       pGS->num_pixels = 0 ;
1035
1036 #ifdef GRAPHIC_DEBUG
1037       cerr << "pixmap ( " << pGS->width << ", " << pGS->height << 
1038         ") is: " << (void*)pGS->pix << " ==>" << (unsigned)pGS->pix << endl;   
1039 #endif
1040
1041       pReg->inited = True ;
1042       pReg->type = _DtHelpDAGraphic ;
1043       pReg->handle = (_DtCvPointer)pGS ;
1044
1045       _DtCvSegment *segment = new_segment (_DtCvREGION);
1046       segment->handle.region.info = (_DtCvPointer)pReg ;
1047       segment->handle.region.width = pGS->width ;
1048       segment->handle.region.height = pGS->height ;
1049       segment->handle.region.ascent = -1 ;
1050       
1051       SegClientData scd(_DtCvREGION);
1052       scd.GraphicHandle((void*)(Graphic*)gr);
1053       // for detaching purposes
1054       segment->client_use = new SegClientData(scd);
1055
1056 #ifdef GRAPHIC_TRAVERSAL
1057       // if graphic is detachable, then it must be traversible
1058       // hack - currently, a hint of 0 is a hypertext link and a hint
1059       // of 1 is a graphic. this should really be co-ordinated with
1060       // the constants used by the Canvas
1061       segment->type |= _DtCvHYPER_TEXT ;
1062       segment->link_idx = 
1063         _DtLinkDbAddLink (gNodeViewInfo->topic()->link_data, // link_db
1064                           0,                                 // id
1065                           grattr->value(),                   // spec
1066                           _DtCvLinkType_AppDefine,           // type
1067                           1,                                 // hint
1068                           0);                                // description
1069
1070 #endif
1071       // place this segment into our structure for later insertion
1072       // into the Canvas segment tree
1073       features.graphic (segment);
1074     }
1075
1076
1077 #ifdef JBM
1078   
1079   if (grattr)
1080     {
1081       // now see if style sheet wants it inlined
1082       const Feature *graphic_display_type =
1083         complete.deep_lookup("GRAPHIC", "ALIGN", 0);
1084 #ifdef IGRAPHIC_DEBUG
1085       cout << "Graphic( " << element.gi() << "): (" ;
1086       if (graphic_display_type)
1087         cout << *graphic_display_type ;
1088       else
1089         cout << "nil" ;
1090       cout << ")" << endl;
1091 #endif
1092       
1093       if (graphic_display_type &&
1094           !strcasecmp("inline", *graphic_display_type->value()))
1095         {
1096           // inline graphic
1097           
1098           // NOTE: there may be tree problems, because graphic objects have no
1099           // content, and it looks as though the tag was empty, so the tree may be
1100           // built wrong after processing a graphic (especially an inline
1101           // graphic). We may have to drop a branch to put this on?
1102           
1103           // see if it is an ismap graphic 
1104           const Attribute *ismap = element.get_olias_attribute(*f_symbols[ISMAP]);
1105           
1106           const char *graphicid = grattr->value();
1107           Pointer<Graphic> gr(graphics_mgr().get(gNodeViewInfo->node_ptr(), graphicid));
1108           PixmapGraphic *graphic = gr->graphic();
1109           
1110           if (graphic->pixmap() != XtUnspecifiedPixmap)
1111             {
1112               gNodeViewInfo->add_graphic(gr);
1113 #ifdef TML_NO_THIS_ASSIGNMENT
1114               model* to_model = f_current_node->get_model();
1115               igfxnode *ignode = new(to_model) igfxnode(to_model);
1116 #else
1117               igfxnode *ignode = new igfxnode(to_model);
1118 #endif
1119               if (ismap)
1120                 ignode->set_ismap();
1121               ignode->set_graphic_handle((Graphic*)gr);
1122               ignode->set_graphic_dimensions(graphic->width(),
1123                                              graphic->height());
1124               f_current_node->connect_node(ignode, f_connect_dir);
1125               f_current_node = ignode ;
1126               f_connect_dir = n_right ;
1127             }
1128         }
1129       else
1130         {
1131           
1132           // This routine puts the graphic as a child of a child of the
1133           // current bnode .
1134           // when we exit, current node is the new bnode, but connect dir is
1135           // to the right in case there are siblings.
1136           
1137 #ifdef TML_NO_THIS_ASSIGNMENT
1138           model* to_model = f_current_node->get_model();
1139           node *current_node = new(to_model) bnode(to_model);
1140 #else
1141           node *current_node = new bnode(to_model);
1142 #endif
1143           b_node->connect_node(current_node, n_down);
1144           current_node->vcc(f_vcc) ;
1145           
1146           node_dir connect_dir = n_down ;
1147           
1148           // space above graphic
1149           
1150           const Feature *spacefp = complete.deep_lookup(f_symbols[MARGIN],
1151                                                         f_symbols[TOP], 0);
1152           
1153           int spacing = 10 ;
1154           
1155           if (spacefp)
1156             spacing = point2pixel(*spacefp->value());
1157           
1158           model *the_model = current_node->get_model();
1159 #ifdef TML_NO_THIS_ASSIGNMENT
1160           space_node *spnode = new (the_model) space_node(spacing, the_model);
1161 #else
1162           space_node *spnode = new space_node(spacing, the_model);
1163 #endif
1164           current_node->connect_node(spnode, connect_dir);
1165           
1166           spnode->vcc(f_vcc);
1167           
1168           const char *graphicid = grattr->value();
1169           
1170           // create a gnode with one blank, so we have somewhere to attach marks 
1171 #ifdef TML_NO_THIS_ASSIGNMENT
1172           gnode *new_gnode = new (the_model) gnode(the_model);
1173 #else
1174           gnode *new_gnode = new gnode(the_model);
1175 #endif
1176           new_gnode->vcc(f_vcc);
1177 #ifdef UseWideChars
1178           wchar_t tml_blankStr[2];
1179           *tml_blankStr         = (wchar_t)' ';
1180           *(tml_blankStr + 1)   = (wchar_t)'\0';
1181                                            new_gnode->attach_data(tml_blankStr,1); 
1182 #else
1183               new_gnode->attach_data(" ",1); 
1184 #endif
1185               spnode->connect_node(new_gnode, n_right);
1186               
1187               // now get graphic
1188               Pointer<Graphic> gr(graphics_mgr().get(gNodeViewInfo->node_ptr(), graphicid));
1189               PixmapGraphic *graphic = gr->graphic();
1190               
1191               if (graphic->pixmap() != XtUnspecifiedPixmap)
1192                 {
1193                   gNodeViewInfo->add_graphic(gr);
1194                   
1195 #ifdef TML_NO_THIS_ASSIGNMENT
1196                   gfxnode *new_gfxnode = new (the_model) gfxnode(the_model);
1197 #else
1198                   gfxnode *new_gfxnode = new gfxnode(the_model);
1199 #endif
1200                   
1201                   new_gnode->connect_node(new_gfxnode, n_right);
1202                   
1203                   
1204                   // NOTE: passing in REAL Graphic object
1205                   // this is ok because it is tracked via the node_view_info
1206                   new_gfxnode->set_graphic_handle((Graphic*)gr);
1207                   new_gfxnode->set_graphic_dimensions(graphic->width(),
1208                                                   graphic->height());
1209               new_gfxnode->vcc(f_vcc);
1210               
1211               // insert another blank to attach end of marks
1212 #ifdef TML_NO_THIS_ASSIGNMENT
1213               gnode *tgnode = new (the_model) gnode(the_model);
1214 #else
1215               gnode *tgnode = new gnode(the_model);
1216 #endif
1217               new_gfxnode->connect_node(tgnode, n_right);
1218 #ifdef UseWideChars
1219               tgnode->attach_data(tml_blankStr,1); 
1220 #else
1221               tgnode->attach_data(" ", 1);
1222 #endif
1223               current_node = tgnode ;
1224             }
1225           else
1226             {
1227               char buf[100] ;
1228               sprintf(buf, "Graphic \"%s\" unavailable", graphic);
1229 #ifdef TML_NO_THIS_ASSIGNMENT
1230               new_gnode = new (the_model) gnode(the_model);
1231 #else
1232               new_gnode = new gnode(the_model);
1233 #endif
1234               new_gnode->vcc(f_vcc);
1235 #ifdef UseWideChars
1236               wchar_t* buffer = new wchar_t[ strlen(buf) + 1 ];
1237               int nc = mbstowcs(buffer, buf, strlen(buf) + 1);
1238               assert( nc >= 0 );
1239               new_gnode->attach_data(buffer, nc);
1240               delete[] buffer;
1241 #else
1242               new_gnode->attach_data(buf, strlen(buf));
1243 #endif
1244               spnode->connect_node(new_gnode, n_right);
1245               current_node = new_gnode;
1246             }
1247           // more spacing at end of graphic
1248 #ifdef TML_NO_THIS_ASSIGNMENT
1249           spnode = new (the_model) space_node(spacing, the_model);
1250 #else
1251           spnode = new space_node(spacing, the_model);
1252 #endif
1253           spnode->vcc(f_vcc);
1254           current_node->connect_node(spnode, n_right);
1255           
1256           // set current node to child of bnode so that EndElement knows that node
1257           // had content, and sets the next node to be connected to the right 
1258           // the node 
1259           f_current_node = b_node->get_node(n_down) ;
1260           f_connect_dir = n_right ;
1261           
1262         }
1263     }
1264 #endif
1265   
1266   // Terms must come last, otherwise vcc will be screwed up for above items
1267   
1268   /* -------- Terms -------- */
1269   const Attribute *terms = element.get_olias_attribute(*f_symbols[TERMS]);
1270   if (terms)
1271     {
1272       int vcc = 0 ;
1273       const char *p = terms->value();
1274 #ifdef UseWideChars
1275       while (*p) {
1276         int mb_len = mblen(p, MB_CUR_MAX);
1277         assert( mb_len > 0 );
1278         if (mb_len == 1) {
1279           const unsigned char ch = (unsigned char)*p++;
1280           if (ch == ' '  || // space
1281               ch == '\t' || // tab
1282               ch == '\n' || // newline
1283               ch == 0xA0) // nbsp
1284             continue;
1285         }
1286         else
1287           p += mb_len;
1288         
1289         vcc++;
1290       }
1291 #else
1292       while (*p)
1293         {
1294           if (!((*p == ' ') || // space
1295                 (*p == '        ') || // tab
1296                 (*p == '\n') || // newline
1297                 (*p == (char)0xA0))) // nbsp
1298             {
1299               vcc++ ;
1300             }
1301           p++;
1302         }
1303 #endif
1304       f_vcc += vcc;
1305     }
1306 }
1307
1308 static
1309 _DtCvSegment *
1310 insert_break (_DtCvSegment *container, unsigned long type)
1311 {
1312   _DtCvSegment *segment = new_segment (_DtCvNOOP);
1313   segment->type |= type;
1314   insert_segment (container, segment) ;
1315   return segment ;
1316 }
1317
1318 void
1319 CanvasRenderer::setup_cascaded_tgroup_container(_DtCvSegment* parent,
1320                                                 _DtCvSegment* child)
1321 {
1322   if (! (parent && child))
1323     return;
1324   if (! (parent->type & _DtCvCONTAINER && child->type & _DtCvCONTAINER))
1325     return;
1326
1327   // orient,...,flow (controller specific) are irrelevant
1328   child->handle.container.type     = parent->handle.container.type;
1329   child->handle.container.leading  = parent->handle.container.leading;
1330   child->handle.container.fmargin  = 0;
1331   child->handle.container.rmargin  = 0;
1332   child->handle.container.tmargin  = 0;
1333   child->handle.container.bmargin  = 0;
1334   child->handle.container.bdr_info = parent->handle.container.bdr_info;
1335   child->handle.container.justify  = _DtCvINHERIT;
1336   child->handle.container.vjustify = _DtCvINHERIT;
1337
1338   TableDefn::table_frame_t frame = f_current_tgroup->table()->frame();
1339
1340   switch (frame) {
1341     case TableDefn::table_frame_top:
1342       child->handle.container.border   = _DtCvBORDER_TOP;
1343       break;
1344     case TableDefn::table_frame_bottom:
1345       child->handle.container.border   = _DtCvBORDER_BOTTOM;
1346       break;
1347     case TableDefn::table_frame_topbot:
1348       child->handle.container.border   = _DtCvBORDER_HORZ;
1349       break;
1350     case TableDefn::table_frame_sides:
1351       child->handle.container.border   = _DtCvBORDER_VERT;
1352       break;
1353     case TableDefn::table_frame_all:
1354     default:
1355       child->handle.container.border   = _DtCvBORDER_FULL;
1356       break;
1357   }
1358 }
1359
1360 void
1361 CanvasRenderer::setup_cascaded_container(_DtCvSegment*)
1362 {}
1363
1364 _DtCvSegment *
1365 CanvasRenderer::create_cascaded_container(_DtCvSegment*)
1366 {
1367   return NULL;
1368 }
1369
1370 static
1371 _DtCvSegment *
1372 new_segment(unsigned long type)
1373 {
1374   _DtCvSegment *segment = new _DtCvSegment ;
1375
1376   { // zero-out fields
1377     unsigned size = sizeof(segment->handle);
1378     //cerr << "Handle size = " << size << endl;
1379     char *p = (char *)&(segment->handle) ;
1380     for (int i = 0; i < size; i++, p++)
1381           *p = 0;
1382   }
1383   
1384   segment->type         = type ;
1385   segment->link_idx     = -1 ;
1386   segment->next_seg     = NULL ;
1387   segment->next_disp    = NULL ;
1388   segment->client_use   = NULL ;
1389   segment->internal_use = NULL ;
1390   
1391   switch (type)
1392     {
1393     case _DtCvCONTAINER:
1394       segment->handle.container.id      = 0 ;
1395       segment->handle.container.type    = _DtCvDYNAMIC ; 
1396       segment->handle.container.border  = _DtCvBORDER_NONE ;
1397       segment->handle.container.justify = _DtCvJUSTIFY_LEFT ;
1398       segment->handle.container.vjustify= _DtCvJUSTIFY_TOP;
1399       segment->handle.container.orient  = _DtCvJUSTIFY_LEFT_CORNER ;
1400       segment->handle.container.vorient = _DtCvJUSTIFY_CENTER ;
1401       segment->handle.container.flow    = _DtCvWRAP;
1402       segment->handle.container.percent = 0 ;
1403       segment->handle.container.leading = DEFAULT_CONTAINER_LEADING ;
1404       segment->handle.container.fmargin = 0 ;
1405       segment->handle.container.lmargin = 0 ;
1406       segment->handle.container.rmargin = 0 ;
1407       segment->handle.container.tmargin = 0 ;
1408       segment->handle.container.bmargin = 0 ;
1409       segment->handle.container.justify_char = NULL ;
1410 #if 0
1411       segment->handle.container.bdr_info.width = 1 ;
1412 #else
1413       segment->handle.container.bdr_info.width = 0 ;
1414 #endif
1415       segment->handle.container.bdr_info.data = NULL ;
1416       segment->handle.container.seg_list = 0;
1417       break;
1418
1419     case _DtCvSTRING:
1420       if (MB_CUR_MAX > 1)
1421         segment->type |= _DtCvWIDE_CHAR;
1422       segment->handle.string.string = NULL;
1423       segment->handle.string.font   = NULL;
1424       break;
1425
1426     case _DtCvREGION:
1427       segment->type |= _DtCvIN_LINE;
1428       break;
1429       
1430     default:
1431       break;
1432     }
1433   
1434   return segment ;
1435 }
1436
1437 _DtCvSegment *
1438 CanvasRenderer::create_container(_DtCvSegment *parent)
1439 {
1440   _DtCvSegment *container = new_segment(_DtCvCONTAINER);
1441   
1442   if (parent)                   
1443     {
1444       _DtCvSegment *tseg = parent->handle.container.seg_list ;
1445   
1446       if (tseg)
1447         {
1448           while (tseg->next_seg)
1449             tseg = tseg->next_seg ;
1450       
1451           tseg->next_seg = container ;
1452         }
1453       else
1454         parent->handle.container.seg_list = container ;
1455   
1456     }
1457   return container ;
1458   f_current_displayable = 0 ;
1459 }
1460
1461 _DtCvSegment *
1462 CanvasRenderer::insert_literal(_DtCvSegment *container,
1463                                const char *font, const char *data,
1464                                unsigned int size)
1465 {
1466   // We have a literal element. This has to be broken down into
1467   // individual string segments, separated by DtCvNEW_LINE segments
1468
1469   char *start = (char *) data;          // start of current substring
1470
1471   char *cr;
1472   // replace all <CR> with newlines
1473   while ((cr = strchr(start, '\015')))
1474   {
1475     *cr = '\n';         // replace with newline
1476     start = cr ;
1477     start++ ;           // advancd beyond the newline
1478   }
1479
1480   return insert_string(container, font, data, size);
1481 }
1482
1483 _DtCvSegment *
1484 CanvasRenderer::insert_string (_DtCvSegment *container,
1485                                const char *font, const char *data,
1486                                unsigned int /*size*/, Boolean count_vcc)
1487 {
1488   _DtCvSegment *seg = NULL ;
1489
1490   char *start = (char *)data; 
1491   char *newline ;
1492
1493   // find the newlines, and make a string segment up to that point,
1494   // then advance our start pointer beyond that point
1495
1496   for (; newline = strchr(start, '\n'); start = newline + 1) {
1497
1498       seg = really_insert_string(container, font, start, (newline - start), count_vcc);
1499       _DtCvSegment *lbseg = insert_break(container, _DtCvNEW_LINE);
1500
1501       f_current_displayable->next_disp = lbseg ;
1502       f_current_displayable = lbseg ;
1503
1504   }
1505
1506   // at end of data now
1507   if (strlen(start) > 0)
1508     seg = really_insert_string(container, font, start, strlen(start), count_vcc);
1509
1510   return seg ;
1511 }
1512
1513 _DtCvSegment *
1514 CanvasRenderer::really_insert_string (_DtCvSegment *container,
1515                                       const char *font, const char *data,
1516                                       unsigned int size, Boolean count_vcc)
1517 {
1518   // put data into a string segment
1519   
1520   _DtCvSegment *strseg = new_segment(_DtCvSTRING);
1521
1522   insert_segment (container, strseg);
1523
1524   // set sub/super script attribute to the segment
1525   if (f_subsuper_stack.entries() &&
1526       f_subsuper_stack.top() > PartialElementFeatures::baseline) {
1527
1528     if (f_subsuper_stack.top() == PartialElementFeatures::subscript)
1529       strseg->type |= _DtCvSUB_SCRIPT;
1530     else if (f_subsuper_stack.top() == PartialElementFeatures::superscript)
1531       strseg->type |= _DtCvSUPER_SCRIPT;
1532 #ifdef SUBSUPER_DEBUG
1533     else
1534       cerr << "(WARNING) unknown value specified as sub/super script."
1535            << endl;
1536 #endif
1537   }
1538   
1539   if (f_current_displayable)
1540     f_current_displayable->next_disp = strseg ;
1541   
1542   f_current_displayable = strseg ;
1543   
1544   int ret_indx ;
1545   // calculate dthelp font index
1546   {
1547     // WARNING: this routine keeps the ptr to the xlfd_spec
1548     //          should be ok unless they try to delete it
1549     //          as it is a const and therefore static to us
1550     
1551     /* resolve current language (order of precedence : LC_ALL,LC_TYPE,LANG) */
1552     const char* lang;
1553     if ((lang = getenv("LC_ALL")) == NULL)
1554       if ((lang = getenv("LC_CTYPE")) == NULL)
1555         if ((lang = getenv("LANG")) == NULL)
1556           lang = "C";
1557
1558     _DtHelpGetExactFontIndex(gHelpDisplayArea, // Display Area Structure
1559                              lang, // lang
1560                              0, // char_set
1561                              (char*) font,      // xlfd_spec
1562                              &ret_indx // returned index
1563                              );
1564 #ifdef FONT_INDEX_DEBUG  
1565     cerr << " indx: " << ret_indx << " " << font << endl;
1566 #endif
1567   }
1568   strseg->handle.string.font = (_DtCvPointer)(size_t) ret_indx ;
1569
1570    // copy data into new memory to hand over to the canvas
1571   char *string = new char[size + 1];
1572   memcpy(string, data, size);
1573   string[size] = '\0';
1574
1575   unsigned int offset;
1576   if (ret_indx < 0 &&
1577       strcmp((char*)gNodeViewInfo->node_ptr()->locale(), "ja_JP.EUC-JP") == 0)
1578   {
1579 #ifdef CR_JP_DEBUG
1580     cerr << "<CR> is being processed for Japanese." << endl;
1581     cerr << "string=" << string << endl;
1582 #endif
1583     unsigned char* strp;
1584
1585     while (strp = (unsigned char*)strchr(string, '\015')) {
1586 #ifdef CR_JP_DEBUG
1587       cerr << "<CR> found...";
1588 #endif
1589       unsigned char *last = NULL, *next = NULL;
1590
1591       for (last = strp;
1592            (char*)last >= string && *last <= (unsigned char)' ';
1593            last--);
1594       for (next = strp;
1595            (char*)next < string + size && *next <= (unsigned char)' ';
1596            next++)
1597
1598       // check for boundary violation
1599       if ((char*)last < string || (char*)next == string) {
1600 #ifdef CR_JP_DEBUG
1601         cerr << "replaced with a space" << endl;
1602 #endif
1603         *strp = ' ';
1604         continue;
1605       }
1606
1607       if (*last < 128 || *next < 128) { // between asciis 
1608 #ifdef CR_JP_DEBUG
1609         cerr << "replaced with a space" << endl;
1610 #endif
1611         *strp = ' ';
1612       }
1613       else {
1614 #ifdef CR_JP_DEBUG
1615         cerr << "marked as 0x90" << endl;
1616 #endif
1617         *strp = 0x90; // mark to be removed later (works only for EUC)
1618       }
1619     }
1620
1621     char *buf = new char[size + 1], *bufp;
1622
1623     for (bufp = buf, offset = 0; offset < size && string[offset]; offset++)
1624     {
1625       unsigned char& c = (unsigned char &)string[offset];
1626
1627       if (c != 0x90)
1628         *bufp++ = c;
1629     }
1630     *bufp = 0;
1631
1632     delete[] string;
1633     string = buf;
1634   }
1635   else
1636   {
1637     for (offset = 0; offset < size && string[offset]; offset++)
1638     {
1639       char& pos = string[offset];
1640       pos = (pos != '\015') ? pos : ' ';
1641     }
1642     // no need for string termination here
1643   }
1644
1645 #ifdef DEBUG_R_INPUT_STRING
1646   cout << string ;
1647 #endif
1648
1649   SegClientData scd(_DtCvSTRING);
1650   scd.vcc(f_vcc);
1651
1652   if (strseg->type & _DtCvWIDE_CHAR)
1653   {
1654     if (ret_indx < 0) {
1655       WString wstring(string, size);
1656 #ifdef CRE_I18N_DEBUG
1657       fprintf(stderr, "(DEBUG) wstring=\"%s\"\n", wstring.get_mbstr());
1658 #endif
1659       if ((wchar_t*)wstring == NULL || *(wchar_t*)wstring == 0) {
1660         strseg->type &= ~_DtCvWIDE_CHAR;
1661       }
1662       else {
1663         delete[] string;
1664         string = (char*)wstring.get_wstr();
1665       }
1666     }
1667     else { // single font being used, do not use wchar_t
1668       strseg->type &= ~_DtCvWIDE_CHAR;
1669     }
1670   }
1671
1672   // account for vcc for this string
1673   scd.vclen(0);
1674   if (strseg->type & _DtCvWIDE_CHAR) {
1675     wchar_t *p;
1676     for (p = (wchar_t*)string; *p; p++) {
1677       if (*p != ' '  && *p != '\t' && *p != '\n')
1678         scd.vclen()++;
1679     }
1680   }
1681   else { // also need to exclude nbsp
1682     unsigned char* p;
1683     for (p = (unsigned char*)string; *p; p++) {
1684       if (*p != ' ' && *p != '\t' && *p != '\n' && *p != 0xA0)
1685         scd.vclen()++;
1686     }
1687   }
1688
1689   // setup highlight info
1690   strseg->client_use = new SegClientData(*f_scd_stack.top());
1691   if (count_vcc) {
1692     f_vcc += scd.vclen();
1693     ((SegClientData*)strseg->client_use)->vcc  (scd.vcc());
1694     ((SegClientData*)strseg->client_use)->vclen(scd.vclen());
1695   }
1696   
1697   strseg->handle.string.string = string ;
1698   
1699   
1700 #ifdef HILITE_DEBUG
1701   _DtCvStringClientData* pSCD = (_DtCvStringClientData*)strseg->client_use;
1702   if (pSCD) {
1703     fprintf(stderr, "(DEBUG) vcc=%d vclen=%d, type=0x%x\n",
1704                         pSCD->vcc, pSCD->vclen, pSCD->hilite_type);
1705   }
1706 #endif
1707
1708   return strseg ;
1709 }
1710
1711 void
1712 CanvasRenderer::setup_container(_DtCvSegment *container,
1713                                 PartialElementFeatures &features,
1714                                 int affix)
1715 {
1716   const char* font = (char*)features.font();
1717   if (font && *font)
1718     f_font = features.font();
1719   
1720   // position
1721   if (features.position().horiz() != _DtCvOPTION_BAD)
1722     {
1723       container->handle.container.orient = features.position().horiz();
1724       container->type |= _DtCvCONTROLLER ;
1725     }
1726   
1727   if (features.position().vert() != _DtCvOPTION_BAD)
1728     {
1729       container->handle.container.vorient = features.position().vert();
1730       container->type |= _DtCvCONTROLLER ;
1731     }
1732   
1733   // layout
1734   if (features.layout().has_layout(affix))
1735     {
1736       Layout &layout = features.layout();
1737       
1738       container->handle.container.fmargin = layout.findent();
1739       container->handle.container.lmargin = layout.lindent();
1740       container->handle.container.rmargin = layout.rindent();
1741       container->handle.container.tmargin = layout.aspace();
1742       container->handle.container.bmargin = layout.bspace();
1743       
1744       if (layout.flow() != _DtCvOPTION_BAD)
1745         {
1746           // NOTE: container is not a controller just because this
1747           // flag gets set, otherwise you will see all the objects
1748           // grouped together off to one side (upper left by default)
1749           container->handle.container.type = layout.flow();
1750         }
1751       if (layout.wrap() != _DtCvOPTION_BAD)
1752         {
1753           container->handle.container.flow = layout.wrap();
1754           container->type |= _DtCvCONTROLLER ;    
1755         }
1756       if (layout.justify() != _DtCvOPTION_BAD)
1757         {
1758           container->handle.container.justify = layout.justify();
1759         }
1760     }
1761
1762     if (features.layout().has_layout(affix)) {
1763         container->handle.container.leading = features.layout().leading();
1764     }
1765     else if (f_leading_stack.entries()) {
1766         container->handle.container.leading = f_leading_stack.top();
1767     }
1768     else {
1769         container->handle.container.leading = DEFAULT_CONTAINER_LEADING;
1770     }
1771 }
1772
1773 void
1774 CanvasRenderer::setup_container(_DtCvSegment *container, ElementFeatures &features)
1775 {
1776     setup_container (container, *(PartialElementFeatures*)&features, False);
1777     
1778     // for online use, set the margins for the container
1779     // when printing, the margins will be set on the form
1780
1781     if (!window_system().printing()) {
1782         
1783         // margins
1784         container->handle.container.fmargin += features.margin().first() ;
1785         container->handle.container.lmargin += features.margin().left() ;
1786         container->handle.container.rmargin += features.margin().right();
1787         container->handle.container.tmargin += features.margin().top(); 
1788         container->handle.container.bmargin += features.margin().bottom();
1789     }
1790     
1791     // border
1792     container->handle.container.border = features.border();
1793     if (features.border_width() < 0)
1794         container->handle.container.bdr_info.width = f_border_width;
1795     else
1796         container->handle.container.bdr_info.width = features.border_width();
1797 }
1798
1799
1800 PartialElementFeatures::PartialElementFeatures(CanvasRenderer* renderer)
1801 : f_text (0),
1802   f_font(0),
1803   f_linebreak(0),
1804   f_pagebreak(PAGEBREAK_NONE),
1805   f_graphic (0),
1806   f_subsuper(baseline),
1807   f_ignore(0),
1808   f_layout(renderer),
1809   f_orientation("portrait")
1810
1811 {
1812 }
1813
1814 PartialElementFeatures::PartialElementFeatures (PartialElementFeatures &features)
1815 : f_text (features.f_text),
1816   f_font (features.f_font),
1817   f_highlight (features.f_highlight),
1818   f_linebreak (features.f_linebreak),
1819   f_pagebreak (features.f_pagebreak),
1820   f_position (features.f_position),
1821   f_layout (features.f_layout),
1822   f_graphic (features.f_graphic),
1823   f_subsuper (features.f_subsuper),
1824   f_ignore (features.f_ignore),
1825   f_orientation (features.f_orientation)
1826 {
1827 }
1828
1829
1830 PartialElementFeatures::~PartialElementFeatures()
1831 {
1832 }
1833
1834 ElementFeatures::ElementFeatures(CanvasRenderer* renderer)
1835 : PartialElementFeatures(renderer),
1836   f_row (NULL),
1837   f_border (_DtCvBORDER_NONE),
1838   f_graphic (0),
1839   f_locator (0),
1840   f_link_idx (-1),
1841   f_table (NULL),
1842   f_tgroup (NULL),
1843   f_colformat (NULL),
1844   f_border_width(-1), // invalid border width
1845   f_xref(NULL),
1846   f_prefix(renderer),
1847   f_suffix(renderer)
1848 {}
1849
1850 ElementFeatures::~ElementFeatures()
1851 {
1852   if (f_xref) free(f_xref);
1853 }
1854
1855 int
1856 PartialElementFeatures::ignore_linebreak(int affix)
1857 {
1858   return (layout().has_layout(affix) ||
1859           position().has_position());
1860 }
1861
1862 int
1863 ElementFeatures::ignore_linebreak(int)
1864 {
1865   if (prefix().text() && prefix().ignore_linebreak(True))
1866       return 1;
1867
1868   if (suffix().text() && suffix().ignore_linebreak(True))
1869       return 1;
1870
1871   return (PartialElementFeatures::ignore_linebreak(False) ||
1872           margin().has_margins());
1873 }
1874
1875 int
1876 PartialElementFeatures::requires_container(int affix)
1877 {
1878   return (layout().has_layout(affix) ||
1879           position().has_position());
1880 }
1881
1882 int
1883 ElementFeatures::requires_container(int)
1884 {
1885   return (PartialElementFeatures::requires_container(False) ||
1886           margin().has_margins() ||
1887           (border() != _DtCvBORDER_NONE) ||
1888           cell().has_cell());
1889 }
1890
1891
1892 Margins::Margins()
1893 : f_first (0),
1894   f_left  (0),
1895   f_right (0),
1896   f_top   (0),
1897   f_bottom(0)
1898 {
1899 }
1900
1901 int
1902 Margins::has_margins()
1903 {
1904   // returns a non zero (true) value if any margin setting
1905   // exists
1906   return f_first | f_left | f_right | f_top | f_bottom ;
1907 }
1908
1909 Layout::Layout(CanvasRenderer* renderer)
1910 : f_renderer(renderer),
1911   f_aspace (0), f_bspace (0), f_leading (-1),
1912   f_findent (0), f_rindent(0), f_lindent (0),
1913   f_flow (_DtCvOPTION_BAD),
1914   f_wrap (_DtCvOPTION_BAD),
1915   f_justify (_DtCvOPTION_BAD)
1916 {
1917 }
1918
1919 Layout::Layout (Layout &layout)
1920 : f_renderer(layout.f_renderer),
1921   f_aspace (layout.f_aspace),
1922   f_bspace (layout.f_bspace),
1923   f_leading (layout.f_leading),
1924   f_findent (layout.f_findent),
1925   f_lindent (layout.f_lindent),
1926   f_rindent (layout.f_rindent),
1927   f_flow (layout.f_flow),
1928   f_wrap (layout.f_wrap),
1929   f_justify (layout.f_justify)
1930 {
1931 }
1932
1933 unsigned
1934 Layout::has_layout(int affix)
1935 {
1936   if (f_leading >= 0)
1937   {
1938     if (affix) {
1939       if (f_renderer->current_leading() != f_leading)
1940         return True;
1941     }
1942     else {
1943       if (f_renderer->f_level == f_renderer->f_leading_level_stack.top() ||
1944         f_renderer->current_leading() != f_leading)
1945       return True;
1946     }
1947   }
1948
1949   return f_aspace || f_bspace || f_findent || f_rindent || f_lindent || 
1950         (f_flow != _DtCvOPTION_BAD) ||
1951         (f_wrap != _DtCvOPTION_BAD) ||
1952           (f_justify != _DtCvOPTION_BAD) ;
1953 }
1954
1955 int
1956 CanvasRenderer::current_leading()
1957 {
1958   if (f_leading_stack.entries() == 0)
1959     return -1;
1960
1961   return f_leading_stack.top();
1962 }
1963
1964 static
1965 void
1966 insert_segment (_DtCvSegment *container, _DtCvSegment *segment)
1967 {
1968 #ifdef C1_DEBUG
1969   if (container == segment)
1970     cerr << "ERROR: Containing ourself" << endl;
1971   cerr << (void*)container << " <-- " << (void*)segment << endl;
1972 #endif
1973   _DtCvSegment *child =
1974     container->handle.container.seg_list ;
1975   
1976   if (!child)
1977     {
1978       // first data in this segment
1979       container->handle.container.seg_list = segment ;
1980     }
1981   else
1982     {
1983       while (child->next_seg)
1984         child = child->next_seg ;
1985       
1986       child->next_seg = segment ;
1987       
1988     }
1989 }
1990
1991 RowDefn::RowDefn() : f_vjustify(_DtCvOPTION_BAD), f_rowsep(CRSEP_NOT_SPECIFIED)
1992 {}
1993     
1994 RowDefn::~RowDefn() 
1995
1996   f_columns.clearAndDestroy ();
1997 }
1998
1999 TableDefn::TableDefn(const char* frame_str, int colsep, int rowsep) :
2000         f_frame(table_frame_default), f_colsep(colsep), f_rowsep(rowsep)
2001 {
2002   if (frame_str)
2003     f_frame = string_to_token(frame_str);
2004 }
2005
2006 TableDefn::~TableDefn()
2007 {}
2008
2009 void
2010 TableDefn::frame(const char* frame_str)
2011 {
2012   if (frame_str)
2013     f_frame = string_to_token(frame_str);
2014   else
2015     f_frame = table_frame_default;
2016 }
2017
2018 TableDefn::table_frame_t
2019 TableDefn::string_to_token(const char* frame_str)
2020 {
2021   if (frame_str == NULL)
2022     return table_frame_default;
2023
2024   table_frame_t token;
2025
2026   if (! strcasecmp(frame_str, "none"))
2027     token = table_frame_none;
2028   else if (! strcasecmp(frame_str, "top"))
2029     token = table_frame_top;
2030   else if (! strcasecmp(frame_str, "bottom"))
2031     token = table_frame_bottom;
2032   else if (! strcasecmp(frame_str, "topbot"))
2033     token = table_frame_topbot;
2034   else if (! strcasecmp(frame_str, "sides"))
2035     token = table_frame_sides;
2036   else if (! strcasecmp(frame_str, "all"))
2037     token = table_frame_all;
2038   else
2039     token = table_frame_default;
2040
2041   return token;
2042 }
2043
2044 TGDefn::TGDefn(_DtCvFrmtOption justify, _DtCvFrmtOption vjustify) :
2045         f_numcols (1),f_segment (0), f_justify(justify), f_vjustify(vjustify),
2046         f_colsep(CRSEP_NOT_SPECIFIED), f_rowsep(CRSEP_NOT_SPECIFIED),
2047         f_char_align('\0'), f_table(NULL)
2048 {}
2049
2050 TGDefn::~TGDefn()
2051 {
2052   f_colformats.clearAndDestroy();
2053   f_rows.clearAndDestroy();
2054 }
2055
2056 void
2057 TGDefn::add_row(RowDefn *rd)
2058 {
2059   if (rd)
2060     f_rows.append (rd);
2061 }
2062
2063 void
2064 TGDefn::add (ColFormat *cf)
2065 {
2066   if (cf)
2067     f_colformats.append (cf);
2068 }
2069
2070
2071 void 
2072 TGDefn::add (ColDefn *cd)
2073 {
2074   if (f_rows.entries() > 0) {
2075     f_rows.last()->append (cd);
2076   }
2077 }
2078
2079 static void
2080 add_id(char **cell_ids, unsigned row, unsigned number, _DtCvSegment *segment)
2081 {
2082   char *id = segment->handle.container.id;
2083   char idstr[64];
2084   if (id == NULL)
2085     {
2086       sprintf(idstr,"id%d", number);
2087       
2088       id = idstr ;
2089       segment->handle.container.id = strdup (id);
2090     }
2091   if (cell_ids[row] == NULL)
2092     {
2093       cell_ids[row] = strdup (id) ;
2094     }
2095   else
2096     {
2097       char *orig = cell_ids[row] ;
2098       cell_ids[row] = new char [ strlen (orig) + 1 + strlen (id) + 1 ] ;
2099       sprintf(cell_ids[row], "%s %s", orig, id);
2100     }
2101 }
2102
2103
2104 ColFormat::ColFormat() 
2105 : f_char_align ('.'),
2106   f_justify (_DtCvOPTION_BAD),
2107   f_width (1),
2108   f_name(NULL),
2109   f_colsep(CRSEP_NOT_SPECIFIED),
2110   f_rowsep(CRSEP_NOT_SPECIFIED)
2111 {}
2112
2113
2114 ColFormat::~ColFormat()
2115 {
2116   if (f_name)
2117     delete f_name ;
2118 }
2119
2120 const char *
2121 ColFormat::name(const char *string)
2122 {
2123   f_name = strdup (string);
2124   return name() ;
2125 }
2126      
2127 static
2128 _DtCvSegment *ensure_id(_DtCvSegment *segment)
2129 {
2130   static unsigned id_count = 0;
2131   // in a table, an id cannot contain a space, so if it does
2132   // we wrap it in a new container
2133   if ((segment->handle.container.id != NULL) &&
2134       strchr(segment->handle.container.id, ' '))
2135     {
2136       _DtCvSegment *new_container = new_segment (_DtCvCONTAINER);
2137       new_container->handle.container.seg_list = segment ;
2138       segment = new_container ;
2139     }
2140
2141   // generate an id if the segment does not have one
2142   if (segment->handle.container.id == NULL)
2143     {
2144       char buffer[16] ;
2145       sprintf(buffer, "id%d", id_count++);
2146       segment->handle.container.id = strdup (buffer);
2147     }
2148   return segment ;
2149 }
2150
2151
2152 ColFormat *
2153 TGDefn::find_format(const char *name, int* index)
2154 {
2155   if (name == NULL || *name == '\0')
2156     return NULL;
2157
2158   ColFormat* format = NULL;
2159
2160   CC_TPtrSlistIterator<ColFormat> cf_cursor (f_colformats);
2161       
2162   int nth;
2163   for (nth = 0; ++cf_cursor; nth++)
2164     {
2165       if (cf_cursor.key()->name() == NULL)
2166         continue;
2167
2168 #ifdef TABLE_DEBUG_X
2169       cerr << "\tname: " << cf_cursor.key()->name() << endl;
2170       cerr << "\tchar: " << cf_cursor.key()->char_align() << endl;
2171       cerr << "\tjustify: " << cf_cursor.key()->justify() << endl;
2172       cerr << "\twidth: " << cf_cursor.key()->width() << endl;
2173 #endif
2174
2175       if (!strcmp(name, cf_cursor.key()->name())) {
2176         format = cf_cursor.key();
2177         break;
2178       }
2179     }
2180
2181   if (format && index) // if found, return the index to the format
2182     *index = nth;
2183
2184   return format;
2185 }
2186
2187
2188 void
2189 TGDefn::build()
2190 {
2191   unsigned i ;
2192   unsigned num_cells   = 0; // # of virtual cells
2193   unsigned num_rows    = 0; // # of physical rows
2194   unsigned num_columns = 0; // # of physical columns
2195
2196   // calculate the actual number of items we have to distribute
2197   // throughout the table, as well as the widest row (in terms of
2198   // number of columns)
2199
2200   // calculate the number of rows we really have
2201   // NOTE: Here we calculate the physically accurate number of
2202   //       rows, not # of <row>s. Some cells can have rowspan > 1
2203   //       which means more rows may be required than # of <row>s.
2204   
2205   CC_TPtrSlistIterator<RowDefn> row_cursor(f_rows) ;
2206
2207   int current_row;
2208   for (current_row = 0; ++row_cursor; current_row++)
2209   {
2210     CC_TPtrSlistIterator<ColDefn> col_cursor (row_cursor.key()->columns());
2211     while (++col_cursor)
2212     {
2213         // extent the number of rows if this columns spans below
2214         // the last row calculated so far
2215         unsigned rowspan = col_cursor.key()->spanrows();
2216
2217         unsigned potential_last_row = current_row + rowspan ;
2218         if (num_rows < potential_last_row)
2219           num_rows = potential_last_row ;
2220     }
2221   }
2222 #ifdef TABLE_DEBUG
2223   fprintf(stderr, "(DEBUG) # of physical rows = %d\n", num_rows);
2224 #endif
2225
2226   // find out how many virtual cells we actually have, and the number
2227   // of columns in the table
2228   // NOTE: Here we calculate the physically accurate number of
2229   //       columns, instead of using numcols of <tgroup>.
2230
2231   unsigned *col_count = new unsigned[num_rows] ;
2232   for (i = 0 ; i < num_rows; i++)
2233     col_count[i] = 0 ; 
2234
2235   row_cursor.reset();
2236
2237   for (current_row = 0; ++row_cursor; current_row++)
2238     {
2239       unsigned entries = row_cursor.key()->columns().entries() ;
2240       num_cells += entries ;
2241
2242       // now find out how many columns we span
2243       CC_TPtrSlistIterator<ColDefn> col_cursor (row_cursor.key()->columns());
2244       
2245
2246       // adding up the spans of each column in the row
2247       // if we rowspan, include our count in the spanned rows
2248
2249       while (++col_cursor)
2250         {
2251           ColDefn *col = col_cursor.key() ;
2252
2253           int mth;
2254           int spancols = 1;
2255           if (find_format(col->colstart(), &mth)) {
2256             int nth;
2257             if (find_format(col->colend(), &nth)) {
2258               if (mth > nth) {
2259                 int anonym = mth;
2260                 mth = nth;
2261                 nth = anonym;
2262               }
2263               spancols += nth - mth;
2264             }
2265           }
2266
2267           for (i = 0; i < col->spanrows(); i++)
2268               col_count[current_row + i] += spancols;
2269         }
2270     }
2271
2272   for ( i = 0 ; i < num_rows; i++)
2273     if (num_columns < col_count[i])
2274       num_columns = col_count[i] ;
2275
2276   delete[] col_count;
2277
2278 #ifdef TABLE_DEBUG
2279   fprintf(stderr, "(DEBUG) # of physical columns = %d\n", num_columns);
2280   fprintf(stderr, "(DEBUG) # of virtual cells = %d\n", num_cells);
2281 #endif
2282
2283   // create a grid of our table, then populate each box in the grid
2284   // we then walk the grid to build our DtCvTable structure
2285
2286   // allocate row memory
2287   _DtCvSegment* **grid = new _DtCvSegment **[num_rows] ;
2288
2289   // allocate column memory
2290   for (i = 0 ; i < num_rows ; i++)
2291     {
2292       grid[i] = new _DtCvSegment *[num_columns]  ;
2293       for (int c = 0 ; c < num_columns; c++)
2294         grid[i][c] = NULL ;
2295     }
2296
2297   // allocate space in _DtCvTable for storing the segments
2298   f_segment->handle.table.cells = new _DtCvSegment* [num_rows*num_columns + 1];
2299   for (i = 0; i <= num_rows*num_columns; i++)
2300     f_segment->handle.table.cells[i] = NULL ; // initialize list
2301
2302
2303   if (f_colformats.entries() < num_columns) {
2304     int i, deficit = num_columns - f_colformats.entries();
2305
2306     for (i = 0; i < deficit; i++)
2307         f_colformats.append(new ColFormat());
2308
2309 #ifdef TABLE_DEBUG
2310     // print warning message if num_columns exceeds # of colformat
2311     cerr << "(WARNING) # of physical cols exceeds that of colformats" << endl;
2312     cerr << "# of cols = " << num_columns << ", # of colformats = "
2313          << f_colformats.entries() << endl;
2314 #endif
2315   }
2316
2317   // now walk the structure that we built up during parsing, and fill
2318   // in the grid
2319
2320   unsigned cell_counter = 0 ;
2321   row_cursor.reset();
2322   for (current_row = 0; ++row_cursor; current_row++)
2323   {
2324       CC_TPtrSlist<ColDefn> columns(row_cursor.key()->columns());
2325
2326       int vc, kept, count = columns.entries();
2327       for (vc = 0, kept = 0; vc < count; vc++) { // iterate for virtual cells
2328 #if 0
2329       fprintf(stderr, "(vc,kept,count)=(%d,%d,%d)\n", vc, kept, count);
2330 #endif
2331
2332         ColDefn* colcell;
2333         mtry {
2334           colcell = columns.at(kept);
2335         }
2336         mcatch_any() {
2337           abort(); // consider it as fatal
2338         }
2339         end_try;
2340
2341         int start_index;
2342         int spancols = 1;
2343         ColFormat* msformat; // most  significant format
2344         ColFormat* lsformat; // least significant format
2345         if (msformat = find_format(colcell->colstart(), &start_index)) {
2346           int end_index;
2347           if (lsformat = find_format(colcell->colend(), &end_index)) {
2348             if (start_index > end_index) {
2349                 int anonym = start_index;
2350                 start_index = end_index;
2351                 end_index = anonym;
2352             }
2353             spancols += end_index - start_index;
2354           }
2355           else
2356             lsformat = msformat;
2357         }
2358         else if (! (msformat = find_format(colcell->colref(), &start_index))) {
2359           // neither colstart nor colref are specified.
2360           // Handle implicitly later.
2361           ++kept;
2362           continue;
2363         }
2364         else
2365           lsformat = msformat;
2366
2367         // make sure the segment has a valid id and add it into the 
2368         // _DtCvTable list of cells
2369         _DtCvSegment *segment = ensure_id(colcell->parseg());
2370         setup_cell(segment, colcell, row_cursor.key(), msformat, lsformat);
2371
2372         for (int i = 0 ; i < spancols; i++) {
2373           for (int j = 0; j < colcell->spanrows(); j++) {
2374             // if there's entrenchment from above rows, skip it.
2375             if (grid[current_row + j][start_index + i])
2376               continue;
2377             grid[current_row + j][start_index + i] = segment;
2378           }
2379         }
2380
2381         f_segment->handle.table.cells[cell_counter++] = segment ;
2382
2383         columns.removeAt(kept);
2384       }
2385
2386       unsigned current_column = 0 ; 
2387       count = columns.entries();
2388       for (vc = 0, kept = 0; vc < count; vc++) // iterate for virtual cells
2389       {
2390         // stick the item into each grid of the table that it
2391         // occupies, including spanning rows and columns
2392 #if 0
2393         fprintf(stderr, "[vc,kept,count]=[%d,%d,%d]\n", vc, kept, count);
2394 #endif
2395         ColDefn* colcell;
2396         mtry {
2397           colcell = columns.at(kept);
2398         }
2399         mcatch_any() {
2400           abort();
2401         }
2402         end_try;
2403
2404         int i, start_index;
2405         for (i = 0; i < num_columns; i++) {
2406           if (grid[current_row][i] == NULL) {
2407             start_index = i;
2408             break;
2409           }
2410         }
2411         if (i == num_columns) { // all columns occupied
2412           ++kept;
2413           continue;
2414         }
2415
2416         ColFormat* msformat = f_colformats.at(start_index);
2417         ColFormat* lsformat = msformat;
2418
2419         // make sure the segment has a valid id and add it into the 
2420         // _DtCvTable list of cells
2421         _DtCvSegment *segment = ensure_id(colcell->parseg());
2422
2423         setup_cell(segment, colcell, row_cursor.key(), msformat, lsformat);
2424
2425         f_segment->handle.table.cells[cell_counter++] = segment ;
2426
2427         for (i = 0; i < colcell->spanrows(); i++) {
2428             // if there's entrenchment from above rows, skip it.
2429             if (grid[current_row + i][start_index])
2430               continue;
2431             grid[current_row + i][start_index] = segment;
2432         }
2433
2434         columns.removeAt(kept);
2435       }
2436
2437       for (int c = 0; c < num_columns; c++)
2438       {
2439         if (grid[current_row][c] == NULL)
2440         {
2441           _DtCvSegment* seg = ensure_id(new_segment(_DtCvCONTAINER));
2442           setup_cell(seg, NULL, row_cursor.key(), f_colformats.at(c),
2443                                                   f_colformats.at(c));
2444
2445           grid[current_row][c] =
2446           f_segment->handle.table.cells[cell_counter++] = seg;
2447         }
2448       }
2449   }
2450   
2451   // temp for now deal with blank spots in the table
2452   unsigned r;
2453   for (r = 0 ; r < num_rows ; r++)
2454     for (unsigned c = 0; c < num_columns ;  c++)
2455       {
2456         if (grid[r][c] == NULL)
2457           {
2458 #ifdef TABLE_DEBUG
2459             fprintf(stderr, "(DEBUG) blank spot found in the table.\n");
2460 #endif
2461             int col;
2462
2463             if (c == 0)
2464             {
2465               // look for a meaningful grid
2466               for (col = 0; col < num_columns && grid[r][col] == NULL; col++);
2467
2468               if (col == num_columns) // not found
2469                 abort();
2470               else
2471               {
2472                 _DtCvSegment* filler = grid[r][col];
2473
2474                 for (int i = c; i < col; i++)
2475                   grid[r][i] = filler;
2476               }
2477             }
2478             else {
2479               col = c - 1 ;
2480               while (grid[r][col] == NULL)
2481                 col-- ;
2482
2483               _DtCvSegment* filler = grid[r][col] ;
2484               col++ ;
2485               while (col <= c)
2486                 grid[r][col++] = filler ;
2487             }
2488           }
2489       }
2490
2491   // now create the _DtCvTable layout information
2492   _DtCvTable *table = &f_segment->handle.table ;
2493
2494   table->num_cols = num_columns ;
2495
2496   table->cell_ids = new char *[num_rows + 1] ;
2497   table->cell_ids[num_rows] = NULL;
2498
2499   // walk the grid and insert the segment id information
2500   // into the cell_ids strings
2501
2502   for (r = 0 ; r < num_rows ; r++)
2503     {    
2504       for (int c = 0 ; c < num_columns ; c++)
2505         {
2506           if (c == 0)
2507             {
2508               // first item in row
2509               table->cell_ids[r] = strdup(grid[r][c]->handle.container.id);
2510             }
2511           else
2512             {
2513               // subsequent items, append space separated id
2514
2515               unsigned len = strlen (table->cell_ids[r]) + strlen (grid[r][c]->handle.container.id);
2516               char *new_ids = new char [len + 2] ;
2517               strcpy(new_ids, table->cell_ids[r]) ;
2518               strcat(new_ids, " ");
2519               strcat(new_ids, grid[r][c]->handle.container.id);
2520               delete table->cell_ids[r] ;
2521               table->cell_ids[r] = new_ids ;
2522             }
2523         }
2524       delete grid[r] ;          // clean up column memory
2525     }
2526
2527   delete grid ;                 
2528
2529   // now  apply the formats to the _DtCvTable
2530   { 
2531     _DtCvFrmtOption *justify = new _DtCvFrmtOption[num_columns] ;
2532     char **col_widths = new char *[num_columns] ;
2533     char *justify_chars = new char [num_columns + 1] ;
2534     
2535     justify_chars[0] = 0 ;
2536
2537     for (int i = 0 ; i < num_columns ; i++)
2538       {
2539         justify[i]       = _DtCvOPTION_BAD;
2540         col_widths[i]    = NULL;
2541         justify_chars[i] = 0;
2542
2543         char buffer[16] ;
2544         ColFormat *format = f_colformats.at(i);
2545
2546         if (format == NULL) continue;
2547
2548         justify[i] = format->justify();
2549
2550         sprintf(buffer, "%d", format->width());
2551         col_widths[i] = strdup(buffer);
2552
2553         if (format->justify() == _DtCvJUSTIFY_CHAR)
2554           {
2555             char buf[2] ;
2556             buf[0] = format->char_align() ;
2557             buf[1] = 0 ;
2558             strcat(justify_chars, buf);
2559           }
2560       }
2561     table->col_justify = justify ;
2562 #ifdef COLSPEC_DEBUG
2563     for (i = 0; i < num_columns; i++)
2564         fprintf(stderr, "col%d=%d\n", i, table->col_justify[i]);
2565 #endif
2566     table->col_w = col_widths ;
2567     table->justify_chars = justify_chars ;
2568   }
2569
2570 #ifdef TABLE_DEBUG
2571   {
2572     cerr << "widths: " << endl;
2573     for (int i = 0; i < num_columns; i++)
2574       cerr << table->col_w[i] << endl;
2575   }
2576 #endif
2577
2578 #ifdef TABLE_DEBUG_X
2579   {
2580     int i = 0 ;
2581     while (table->cell_ids[i])
2582       cerr << table->cell_ids[i++] << endl;
2583
2584   }
2585 #endif
2586 }
2587
2588 #ifdef HIERARCHY_DEBUG
2589
2590 static char* seg_type_to_string(unsigned long seg_type)
2591 {
2592     char* ret = NULL;
2593     switch (seg_type) {
2594         case _DtCvCONTAINER:
2595             ret = "<C>";
2596             break;
2597         case _DtCvSTRING:
2598             ret = "<S>";
2599             break;
2600         case _DtCvREGION:
2601             ret = "<R>";
2602             break;
2603         case _DtCvMARKER:
2604             ret = "<M>";
2605             break;
2606         case _DtCvLINE:
2607             ret = "<L>";
2608             break;
2609         case _DtCvNOOP:
2610             ret = "<N>";
2611             break;
2612         case _DtCvTABLE:
2613             ret = "<T>";
2614             break;
2615         default:
2616             ret = "<?>";
2617     }
2618
2619     return ret;
2620 }
2621
2622 static void print_hierarchy_traversal(_DtCvSegment* seg, int depth, ostream& ostr)
2623 {
2624     if (seg == NULL)
2625         return;
2626
2627     unsigned long seg_ptype = seg->type & _DtCvPRIMARY_MASK;
2628
2629     int i;
2630     for (i = 0; i < depth; ostr << ' ', i++);
2631
2632     if (seg_ptype != _DtCvCONTAINER)
2633         ostr << seg_type_to_string(seg_ptype) << '\n';
2634     else
2635         ostr << seg_type_to_string(seg_ptype);
2636
2637     if (seg_ptype == _DtCvCONTAINER) {
2638         if (seg->handle.container.border != _DtCvBORDER_NONE)
2639             ostr << " border";
2640         ostr << " leading=" << seg->handle.container.leading;
2641         ostr << '\n';
2642         
2643         _DtCvSegment* subordinates = seg->handle.container.seg_list;
2644         if (subordinates)
2645             print_hierarchy_traversal(subordinates, depth+1, ostr);
2646     }
2647     if (seg_ptype == _DtCvTABLE) {
2648         _DtCvTable *table = &seg->handle.table;
2649         _DtCvSegment** cells = table->cells;
2650         while (*cells)
2651             print_hierarchy_traversal(*cells++, depth+1, ostr);
2652     }
2653     else if (seg_ptype == _DtCvSTRING) {
2654         for (i = 0; i < depth+1; ostr << ' ', i++);
2655         ostr << (char*)seg->handle.string.string << '\n';
2656     }
2657
2658     // traverse siblings
2659     _DtCvSegment* siblings;
2660     if (siblings = seg->next_seg)
2661         print_hierarchy_traversal(siblings, depth, ostr);
2662 }
2663
2664 static void print_hierarchy(_DtCvTopicInfo* topic)
2665 {
2666     _DtCvSegment* top = topic->seg_list;
2667
2668     if (top) {
2669         int depth = 0;
2670         ofstream ostr("SegmentStructure");
2671         print_hierarchy_traversal(top, depth, ostr);
2672         ostr.close();
2673     }
2674 }
2675
2676 #endif
2677
2678 // colcell can be NULL where cell-related values are not taken
2679 // into account.
2680 void
2681 TGDefn::setup_cell(_DtCvSegment* segment, ColDefn* colcell,
2682                    RowDefn* row, ColFormat* msformat, ColFormat* lsformat)
2683 {
2684   _DtCvSegment* parseg;
2685
2686   // segment is optional, but if it's specified it'll be used
2687   // instead of colcell->parseg().
2688   if (segment)
2689     parseg = segment;
2690   else
2691     parseg = colcell->parseg();
2692
2693   if (colcell && colcell->justify() != _DtCvOPTION_BAD)
2694   {
2695     parseg->handle.container.justify = colcell->justify();
2696
2697     if (colcell->justify() == _DtCvJUSTIFY_CHAR)
2698     {
2699       char charalign = '\0';
2700
2701       if (colcell->char_align()) {
2702         charalign = colcell->char_align();
2703       }
2704       else if (msformat && msformat->char_align()) {
2705         charalign = msformat->char_align();
2706       }
2707       else { // always available here thanks to dtd
2708         charalign = char_align();
2709       }
2710
2711       char *align = new char[2];
2712       align[0] = charalign;
2713       align[1] = '\0';
2714               
2715       // need to deallocate container.justify_char later
2716       parseg->handle.container.justify_char = align;
2717     }
2718   }
2719   else if (msformat && msformat->justify() != _DtCvOPTION_BAD)
2720   {
2721     parseg->handle.container.justify = _DtCvINHERIT;
2722   }
2723   else if (justify() != _DtCvOPTION_BAD)
2724   {
2725     if (justify() == _DtCvJUSTIFY_CHAR)
2726     {
2727       char charalign = '\0';
2728
2729       if (colcell && colcell->char_align()) {
2730         charalign = colcell->char_align();
2731       }
2732       else if (msformat && msformat->char_align()) {
2733         charalign = msformat->char_align();
2734       }
2735       else { // always available here due to dtd
2736         charalign = char_align();
2737       }
2738
2739       char *align = new char[2];
2740       align[0] = charalign;
2741       align[1] = '\0';
2742               
2743       // need to deallocate container.justify_char later
2744       parseg->handle.container.justify_char = align;
2745     }
2746
2747     parseg->handle.container.justify = justify();
2748   }
2749
2750   if (colcell && colcell->vjustify() != _DtCvOPTION_BAD) {
2751     parseg->handle.container.vjustify = colcell->vjustify();
2752   }
2753   else if (row->vjustify() != _DtCvOPTION_BAD) {
2754     parseg->handle.container.vjustify = row->vjustify();
2755   }
2756   else if (vjustify() != _DtCvOPTION_BAD) {
2757     parseg->handle.container.vjustify = vjustify();
2758   }
2759
2760   int col_sep = CRSEP_NOT_SPECIFIED;
2761   // colsep does not apply to the last column
2762   if (lsformat != f_colformats.last())
2763   {
2764     // Precedence: col->colformat->tgroup->table
2765     if (colcell && colcell->colsep() != CRSEP_NOT_SPECIFIED) {
2766       col_sep = colcell->colsep();
2767     }
2768     else if (msformat && msformat->colsep() != CRSEP_NOT_SPECIFIED) {
2769       col_sep = msformat->colsep();
2770     }
2771     else if (colsep() != CRSEP_NOT_SPECIFIED) {
2772       col_sep = colsep();
2773     }
2774     else if (table() && table()->colsep() != CRSEP_NOT_SPECIFIED) {
2775       col_sep = table()->colsep();
2776     }
2777   }
2778
2779   int row_sep = CRSEP_NOT_SPECIFIED;
2780   // rowsep does not apply to the last row
2781   if (f_rows.last() != row) {
2782     // Precedence: col->row->colformat->tgroup->table
2783     if (colcell && colcell->rowsep() != CRSEP_NOT_SPECIFIED) {
2784       row_sep = colcell->rowsep();
2785     }
2786     else if (row->rowsep() != CRSEP_NOT_SPECIFIED) {
2787       row_sep = row->rowsep();
2788     }
2789     else if (msformat && msformat->rowsep() != CRSEP_NOT_SPECIFIED) {
2790       row_sep = msformat->rowsep();
2791     }
2792     else if (rowsep() != CRSEP_NOT_SPECIFIED) {
2793       row_sep = rowsep();
2794     }
2795     else if (table() && table()->rowsep() != CRSEP_NOT_SPECIFIED) {
2796       row_sep = table()->rowsep();
2797     }
2798   }
2799
2800   if (col_sep == CRSEP_YES && row_sep == CRSEP_YES)
2801     parseg->handle.container.border = _DtCvBORDER_BOTTOM_RIGHT;
2802   else if (col_sep == CRSEP_YES)
2803     parseg->handle.container.border = _DtCvBORDER_RIGHT;
2804   else if (row_sep == CRSEP_YES)
2805     parseg->handle.container.border = _DtCvBORDER_BOTTOM;
2806   else {
2807     // respect border feature of cell, do not overwrite here.
2808   }
2809 }
2810