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